agentirc-cli 9.13.1__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.1 → agentirc_cli-9.14.0}/CHANGELOG.md +68 -0
  2. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/PKG-INFO +1 -1
  3. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/event_subscriptions.py +23 -9
  4. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/protocol/replies.py +11 -0
  5. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/metrics.py +7 -0
  6. agentirc_cli-9.14.0/agentirc/accounts.py +574 -0
  7. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/channel.py +29 -11
  8. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/cli.py +10 -1
  9. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/client.py +706 -68
  10. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/config.py +121 -6
  11. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/ircd.py +154 -16
  12. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/protocol.py +28 -0
  13. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skills/history.py +8 -1
  14. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skills/rooms.py +88 -14
  15. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skills/threads.py +56 -6
  16. agentirc_cli-9.14.0/docs/accounts.md +501 -0
  17. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/api-stability.md +27 -0
  18. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/cli.md +3 -0
  19. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/extension-api.md +24 -4
  20. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/pyproject.toml +1 -1
  21. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/_helpers.py +3 -0
  22. agentirc_cli-9.14.0/tests/test_accounts_grants.py +230 -0
  23. agentirc_cli-9.14.0/tests/test_accounts_hardening.py +704 -0
  24. agentirc_cli-9.14.0/tests/test_accounts_hardening_medium.py +625 -0
  25. agentirc_cli-9.14.0/tests/test_accounts_review66.py +385 -0
  26. agentirc_cli-9.14.0/tests/test_accounts_revoke.py +310 -0
  27. agentirc_cli-9.14.0/tests/test_accounts_sasl.py +985 -0
  28. agentirc_cli-9.14.0/tests/test_accounts_sighup.py +133 -0
  29. agentirc_cli-9.14.0/tests/test_cap_registration_hold.py +132 -0
  30. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_events_reserved_nick.py +2 -2
  31. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_verb_discovery.py +9 -10
  32. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/uv.lock +1 -1
  33. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/SKILL.md +0 -0
  34. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  35. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  36. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  37. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  38. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  39. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  40. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/cicd/SKILL.md +0 -0
  41. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  42. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  43. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  44. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  45. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  46. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/communicate/SKILL.md +0 -0
  47. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  48. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  49. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  50. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  51. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/recall/SKILL.md +0 -0
  52. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/recall/scripts/recall.sh +0 -0
  53. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/remember/SKILL.md +0 -0
  54. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/remember/scripts/remember.sh +0 -0
  55. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  56. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  57. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/think/SKILL.md +0 -0
  58. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills/think/scripts/think.sh +0 -0
  59. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.claude/skills.local.yaml.example +0 -0
  60. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/current +0 -0
  61. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/current_plan +0 -0
  62. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/frames/agentirc-9-7-0-ships-an-embedded-bot-framework-bot.json +0 -0
  63. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/frames/agentirc-now-speaks-presence-the-ircd-parses-resid.json +0 -0
  64. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/frames/agentirc-ships-an-agent-accessibility-release-ai-a.json +0 -0
  65. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/plans/agentirc-9-7-0-ships-an-embedded-bot-framework-bot.json +0 -0
  66. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/plans/agentirc-now-speaks-presence-the-ircd-parses-resid.json +0 -0
  67. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.devague/plans/agentirc-ships-an-agent-accessibility-release-ai-a.json +0 -0
  68. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.github/workflows/publish.yml +0 -0
  69. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.github/workflows/tests.yml +0 -0
  70. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/.gitignore +0 -0
  71. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/CLAUDE.md +0 -0
  72. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/LICENSE +0 -0
  73. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/README.md +0 -0
  74. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/TRADEMARKS.md +0 -0
  75. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/__init__.py +0 -0
  76. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/__main__.py +0 -0
  77. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/__init__.py +0 -0
  78. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/aio.py +0 -0
  79. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/bots/__init__.py +0 -0
  80. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/bots/bot_manager.py +0 -0
  81. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/bots/http_listener.py +0 -0
  82. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/cli_client.py +0 -0
  83. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/cli_shared/__init__.py +0 -0
  84. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/cli_shared/constants.py +0 -0
  85. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/cli_shared/mesh.py +0 -0
  86. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/constants.py +0 -0
  87. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/pidfile.py +0 -0
  88. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/protocol/__init__.py +0 -0
  89. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/protocol/message.py +0 -0
  90. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/__init__.py +0 -0
  91. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/audit.py +0 -0
  92. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/context.py +0 -0
  93. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/telemetry/tracing.py +0 -0
  94. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/_internal/virtual_client.py +0 -0
  95. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/agent_client.py +0 -0
  96. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/__init__.py +0 -0
  97. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/bot.py +0 -0
  98. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/bot_manager.py +0 -0
  99. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/cli.py +0 -0
  100. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/config.py +0 -0
  101. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/filter_dsl.py +0 -0
  102. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/http_listener.py +0 -0
  103. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/template_engine.py +0 -0
  104. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/bots/virtual_client.py +0 -0
  105. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/events.py +0 -0
  106. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/history_store.py +0 -0
  107. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/remote_client.py +0 -0
  108. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/room_store.py +0 -0
  109. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/rooms_util.py +0 -0
  110. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/server_link.py +0 -0
  111. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skill.py +0 -0
  112. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skills/__init__.py +0 -0
  113. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skills/icon.py +0 -0
  114. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/skills/presence.py +0 -0
  115. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/thread_store.py +0 -0
  116. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/agentirc/virtual_client.py +0 -0
  117. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/agent-walkthrough.md +0 -0
  118. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/bots.md +0 -0
  119. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/deployment.md +0 -0
  120. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/plans/2026-06-12-agentirc-9-7-0-ships-an-embedded-bot-framework-bot.md +0 -0
  121. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/plans/2026-07-01-agentirc-ships-an-agent-accessibility-release-ai-a.md +0 -0
  122. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/plans/2026-07-07-agentirc-now-speaks-presence-the-ircd-parses-resid.md +0 -0
  123. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/specs/2026-06-12-agentirc-9-7-0-ships-an-embedded-bot-framework-bot.md +0 -0
  124. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/specs/2026-07-01-agentirc-ships-an-agent-accessibility-release-ai-a.md +0 -0
  125. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/specs/2026-07-02-agent-accessibility-gap-verification.md +0 -0
  126. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/specs/2026-07-02-agent-accessibility-release-audit.md +0 -0
  127. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/specs/2026-07-07-agentirc-now-speaks-presence-the-ircd-parses-resid.md +0 -0
  128. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/steward/onboarding.md +0 -0
  129. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/superpowers/specs/2026-04-30-bootstrap-design.md +0 -0
  130. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/superpowers/specs/2026-05-01-bot-extension-api-design.md +0 -0
  131. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/docs/superpowers/specs/2026-05-01-task14-audit.md +0 -0
  132. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/scripts/sonar-pr-issues.py +0 -0
  133. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/sonar-project.properties +0 -0
  134. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/__init__.py +0 -0
  135. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/__init__.py +0 -0
  136. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_bot.py +0 -0
  137. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_bot_host.py +0 -0
  138. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_bot_manager.py +0 -0
  139. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_cli_bot.py +0 -0
  140. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_config_bots.py +0 -0
  141. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_filter_dsl.py +0 -0
  142. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_http_listener.py +0 -0
  143. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_public_surface.py +0 -0
  144. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/bots/test_template_engine.py +0 -0
  145. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/conftest.py +0 -0
  146. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/__init__.py +0 -0
  147. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/_fakes.py +0 -0
  148. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/_metrics_helpers.py +0 -0
  149. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_emit.py +0 -0
  150. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_lifecycle.py +0 -0
  151. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_module.py +0 -0
  152. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_audit_parse_error.py +0 -0
  153. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_config.py +0 -0
  154. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_dispatch_span.py +0 -0
  155. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_emit_event_span.py +0 -0
  156. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_metrics_init.py +0 -0
  157. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_metrics_s2s.py +0 -0
  158. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_outbound_inject.py +0 -0
  159. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_parse_error.py +0 -0
  160. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
  161. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_server_init.py +0 -0
  162. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_server_link_inject.py +0 -0
  163. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/telemetry/test_tracing.py +0 -0
  164. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_agent_client.py +0 -0
  165. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_agent_walkthrough.py +0 -0
  166. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_api_stability_embedding.py +0 -0
  167. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_bot_capability.py +0 -0
  168. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_channel.py +0 -0
  169. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_cli.py +0 -0
  170. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_cli_client_verbs.py +0 -0
  171. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_cli_json.py +0 -0
  172. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_client_backfill.py +0 -0
  173. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_config_loader.py +0 -0
  174. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_connection.py +0 -0
  175. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_discovery.py +0 -0
  176. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_disk_paths.py +0 -0
  177. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_dm_history.py +0 -0
  178. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_docs_promises.py +0 -0
  179. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_error_tokens.py +0 -0
  180. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_event_subscriptions.py +0 -0
  181. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_eventpub.py +0 -0
  182. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_events_basic.py +0 -0
  183. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_events_catalog.py +0 -0
  184. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_events_federation.py +0 -0
  185. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_events_history.py +0 -0
  186. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_events_lifecycle.py +0 -0
  187. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_federation.py +0 -0
  188. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_history.py +0 -0
  189. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_history_since.py +0 -0
  190. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_link_reconnect.py +0 -0
  191. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_liveness.py +0 -0
  192. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_long_messages.py +0 -0
  193. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_mentions.py +0 -0
  194. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_message_tags.py +0 -0
  195. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_messaging.py +0 -0
  196. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_modes.py +0 -0
  197. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_motd.py +0 -0
  198. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_presence.py +0 -0
  199. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_presence_federation.py +0 -0
  200. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_presence_stale.py +0 -0
  201. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_protocol_bot_exports.py +0 -0
  202. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_protocol_presence.py +0 -0
  203. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_reconnect_catchup.py +0 -0
  204. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_room_persistence.py +0 -0
  205. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_rooms_federation.py +0 -0
  206. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_rooms_integration.py +0 -0
  207. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_server_icon_skill.py +0 -0
  208. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_skills.py +0 -0
  209. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_sonar_pr_issues.py +0 -0
  210. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_threads.py +0 -0
  211. {agentirc_cli-9.13.1 → agentirc_cli-9.14.0}/tests/test_wire_format_envelope.py +0 -0
@@ -4,6 +4,74 @@ 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
+
7
75
  ## [9.13.1] - 2026-10-30
8
76
 
9
77
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentirc-cli
3
- Version: 9.13.1
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