agentirc-cli 9.7.0__tar.gz → 9.8.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 (154) hide show
  1. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/CHANGELOG.md +23 -0
  2. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/PKG-INFO +35 -12
  3. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/README.md +34 -11
  4. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/cli.py +40 -1
  5. agentirc_cli-9.8.0/docs/bots.md +174 -0
  6. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/pyproject.toml +1 -1
  7. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_cli_bot.py +56 -0
  8. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/uv.lock +1 -1
  9. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  10. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  11. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/cicd/SKILL.md +0 -0
  12. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  13. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  14. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  15. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  16. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  17. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/communicate/SKILL.md +0 -0
  18. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  19. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  20. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  21. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  22. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  23. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  24. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/think/SKILL.md +0 -0
  25. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills/think/scripts/think.sh +0 -0
  26. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.claude/skills.local.yaml.example +0 -0
  27. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.devague/current +0 -0
  28. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.devague/current_plan +0 -0
  29. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.devague/frames/agentirc-9-7-0-ships-an-embedded-bot-framework-bot.json +0 -0
  30. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.devague/plans/agentirc-9-7-0-ships-an-embedded-bot-framework-bot.json +0 -0
  31. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.github/workflows/publish.yml +0 -0
  32. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.github/workflows/tests.yml +0 -0
  33. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/.gitignore +0 -0
  34. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/CLAUDE.md +0 -0
  35. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/LICENSE +0 -0
  36. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/__init__.py +0 -0
  37. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/__main__.py +0 -0
  38. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/__init__.py +0 -0
  39. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/aio.py +0 -0
  40. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/bots/__init__.py +0 -0
  41. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/bots/bot_manager.py +0 -0
  42. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/bots/http_listener.py +0 -0
  43. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/cli_shared/__init__.py +0 -0
  44. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/cli_shared/constants.py +0 -0
  45. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/cli_shared/mesh.py +0 -0
  46. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/constants.py +0 -0
  47. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/event_subscriptions.py +0 -0
  48. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/pidfile.py +0 -0
  49. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/protocol/__init__.py +0 -0
  50. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/protocol/message.py +0 -0
  51. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/protocol/replies.py +0 -0
  52. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/telemetry/__init__.py +0 -0
  53. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/telemetry/audit.py +0 -0
  54. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/telemetry/context.py +0 -0
  55. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/telemetry/metrics.py +0 -0
  56. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/telemetry/tracing.py +0 -0
  57. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/_internal/virtual_client.py +0 -0
  58. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/__init__.py +0 -0
  59. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/bot.py +0 -0
  60. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/bot_manager.py +0 -0
  61. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/config.py +0 -0
  62. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/filter_dsl.py +0 -0
  63. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/http_listener.py +0 -0
  64. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/template_engine.py +0 -0
  65. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/bots/virtual_client.py +0 -0
  66. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/channel.py +0 -0
  67. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/cli.py +0 -0
  68. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/client.py +0 -0
  69. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/config.py +0 -0
  70. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/events.py +0 -0
  71. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/history_store.py +0 -0
  72. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/ircd.py +0 -0
  73. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/protocol.py +0 -0
  74. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/remote_client.py +0 -0
  75. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/room_store.py +0 -0
  76. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/rooms_util.py +0 -0
  77. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/server_link.py +0 -0
  78. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/skill.py +0 -0
  79. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/skills/__init__.py +0 -0
  80. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/skills/history.py +0 -0
  81. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/skills/icon.py +0 -0
  82. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/skills/rooms.py +0 -0
  83. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/skills/threads.py +0 -0
  84. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/thread_store.py +0 -0
  85. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/agentirc/virtual_client.py +0 -0
  86. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/api-stability.md +0 -0
  87. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/cli.md +0 -0
  88. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/deployment.md +0 -0
  89. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/extension-api.md +0 -0
  90. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/plans/2026-06-12-agentirc-9-7-0-ships-an-embedded-bot-framework-bot.md +0 -0
  91. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/specs/2026-06-12-agentirc-9-7-0-ships-an-embedded-bot-framework-bot.md +0 -0
  92. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/steward/onboarding.md +0 -0
  93. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/superpowers/specs/2026-04-30-bootstrap-design.md +0 -0
  94. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/superpowers/specs/2026-05-01-bot-extension-api-design.md +0 -0
  95. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/docs/superpowers/specs/2026-05-01-task14-audit.md +0 -0
  96. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/__init__.py +0 -0
  97. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/_helpers.py +0 -0
  98. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/__init__.py +0 -0
  99. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_bot.py +0 -0
  100. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_bot_host.py +0 -0
  101. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_bot_manager.py +0 -0
  102. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_config_bots.py +0 -0
  103. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_filter_dsl.py +0 -0
  104. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_http_listener.py +0 -0
  105. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_public_surface.py +0 -0
  106. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/bots/test_template_engine.py +0 -0
  107. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/conftest.py +0 -0
  108. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/__init__.py +0 -0
  109. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/_fakes.py +0 -0
  110. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/_metrics_helpers.py +0 -0
  111. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_audit_emit.py +0 -0
  112. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_audit_lifecycle.py +0 -0
  113. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_audit_module.py +0 -0
  114. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_audit_parse_error.py +0 -0
  115. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_config.py +0 -0
  116. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_dispatch_span.py +0 -0
  117. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_emit_event_span.py +0 -0
  118. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_metrics_init.py +0 -0
  119. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_metrics_s2s.py +0 -0
  120. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_outbound_inject.py +0 -0
  121. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_parse_error.py +0 -0
  122. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_s2s_relay_span.py +0 -0
  123. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_server_init.py +0 -0
  124. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_server_link_inject.py +0 -0
  125. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/telemetry/test_tracing.py +0 -0
  126. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_api_stability_embedding.py +0 -0
  127. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_bot_capability.py +0 -0
  128. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_channel.py +0 -0
  129. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_cli.py +0 -0
  130. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_config_loader.py +0 -0
  131. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_connection.py +0 -0
  132. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_discovery.py +0 -0
  133. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_event_subscriptions.py +0 -0
  134. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_eventpub.py +0 -0
  135. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_events_basic.py +0 -0
  136. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_events_catalog.py +0 -0
  137. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_events_federation.py +0 -0
  138. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_events_history.py +0 -0
  139. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_events_lifecycle.py +0 -0
  140. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_events_reserved_nick.py +0 -0
  141. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_federation.py +0 -0
  142. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_history.py +0 -0
  143. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_link_reconnect.py +0 -0
  144. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_mentions.py +0 -0
  145. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_messaging.py +0 -0
  146. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_modes.py +0 -0
  147. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_protocol_bot_exports.py +0 -0
  148. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_room_persistence.py +0 -0
  149. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_rooms_federation.py +0 -0
  150. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_rooms_integration.py +0 -0
  151. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_server_icon_skill.py +0 -0
  152. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_skills.py +0 -0
  153. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_threads.py +0 -0
  154. {agentirc_cli-9.7.0 → agentirc_cli-9.8.0}/tests/test_wire_format_envelope.py +0 -0
@@ -4,6 +4,29 @@ 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.8.0] - 2026-06-12
8
+
9
+ Make bot authoring discoverable: a dedicated guide plus first-class CLI support
10
+ for event-triggered bots.
11
+
12
+ ### Added
13
+
14
+ - **`agentirc bot create --trigger event --event-filter "<expr>"`** — the CLI
15
+ can now create event-triggered bots, not just `webhook` ones. The filter is
16
+ compiled and validated at create time (a malformed expression is rejected
17
+ immediately). `--event-filter` is required for `--trigger event` and rejected
18
+ for `--trigger webhook`.
19
+ - **[`docs/bots.md`](docs/bots.md)** — a bot-authoring guide: CLI quick start
20
+ for both trigger types, the `bot.yaml` schema for hand-authoring, the
21
+ event-filter DSL grammar, and templating. Linked from the README.
22
+
23
+ ### Changed
24
+
25
+ - README public-API table now lists all six public modules (it had drifted to
26
+ three) and gains an "Embedded bots" section. Corrected the stale 9.5.0
27
+ "webhook never bound" operational note — since 9.7.0 `IRCd.start()` binds the
28
+ listener when `webhook_port > 0`.
29
+
7
30
  ## [9.7.0] - 2026-06-12
8
31
 
9
32
  Absorb culture's bot framework into the public `agentirc.bots` subsystem. Closes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 9.7.0
3
+ Version: 9.8.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
@@ -145,15 +145,18 @@ multi-host federation.
145
145
 
146
146
  ## Public API and stability
147
147
 
148
- Three modules form the **public, semver-tracked surface**. Everything else
148
+ Six modules form the **public, semver-tracked surface**. Everything else
149
149
  under `agentirc.*` is internal and may be refactored — including renamed,
150
150
  split, or removed — in any minor or patch release.
151
151
 
152
- | Module | Members |
153
- |---|---|
154
- | [`agentirc.config`](docs/api-stability.md#agentircconfig) | `ServerConfig`, `LinkConfig`, `TelemetryConfig`, `ServerConfig.from_yaml(path)` |
155
- | [`agentirc.cli`](docs/api-stability.md#agentirccli) | `main()`, `dispatch(argv) -> int` |
156
- | [`agentirc.protocol`](docs/api-stability.md#agentircprotocol) | Verb constants, numeric reply codes, IRCv3/extension tag names, the bot extension surface (`Event`, `EventType`, `EVENT_TYPE_*`, `EVENTSUB`/`EVENTUNSUB`/`EVENT`/`EVENTERR`/`EVENTPUB`/`SEVENT`, `BOT_CAP`) |
152
+ | Module | Members | Since |
153
+ |---|---|---|
154
+ | [`agentirc.config`](docs/api-stability.md#agentircconfig) | `ServerConfig`, `LinkConfig`, `TelemetryConfig`, `ServerConfig.from_yaml(path)` | 9.0.0 |
155
+ | [`agentirc.cli`](docs/api-stability.md#agentirccli) | `main()`, `dispatch(argv) -> int` | 9.2.0 |
156
+ | [`agentirc.protocol`](docs/api-stability.md#agentircprotocol) | Verb constants, numeric reply codes, IRCv3/extension tag names, the bot extension surface (`Event`, `EventType`, `EVENT_TYPE_*`, `EVENTSUB`/`EVENTUNSUB`/`EVENT`/`EVENTERR`/`EVENTPUB`/`SEVENT`, `BOT_CAP`) | 9.2.0 |
157
+ | [`agentirc.ircd`](docs/api-stability.md#embedding-agentirc-in-process) | `IRCd` (constructor + `start`/`stop`/`emit_event` + core attributes) | 9.6.0 |
158
+ | [`agentirc.virtual_client`](docs/api-stability.md#embedding-agentirc-in-process) | `VirtualClient` | 9.6.0 |
159
+ | [`agentirc.bots`](docs/api-stability.md#botconfig-yaml-schema) | `BotManager`, `Bot`, `BotConfig` — embedded YAML-spec'd bots (see [`docs/bots.md`](docs/bots.md)) | 9.7.0 |
157
160
 
158
161
  `agentirc.cli.dispatch(argv)` is the in-process integration surface — it is
159
162
  what culture's `culture server` shim calls today. It returns `int` on
@@ -222,11 +225,31 @@ behavior) and
222
225
  [`docs/api-stability.md#bot-extension-surface-shipped-in-950`](docs/api-stability.md#bot-extension-surface-shipped-in-950)
223
226
  for the wire contract.
224
227
 
225
- **Operational note.** As of 9.5.0, `agentirc` no longer binds `webhook_port`
226
- even when set in YAML — the field stays in `ServerConfig` for backward
227
- compatibility with culture's `~/.culture/server.yaml`, but consumers that
228
- need webhook→bot dispatch host their own HTTP listener (notably culture).
229
- The field will be removed in 10.0.0.
228
+ ## Embedded bots (since 9.7.0)
229
+
230
+ Distinct from the out-of-process extension API above, `agentirc.bots` is an
231
+ **in-process** bot framework: deterministic, YAML-spec'd automations that run as
232
+ `VirtualClient` presences inside the IRCd (no separate process). A bot reacts to
233
+ an event filter or an HTTP webhook and replies with a Jinja2-templated message.
234
+
235
+ ```bash
236
+ agentirc bot create pinger --owner ori \
237
+ --trigger event \
238
+ --event-filter "type == 'user.message' and channel == '#general'" \
239
+ --channels '#general' --template 'pong {{event.nick}}'
240
+ ```
241
+
242
+ Bots live under `~/.culture/bots/<name>/bot.yaml` and load when the server
243
+ starts. See **[`docs/bots.md`](docs/bots.md)** for the authoring guide (CLI +
244
+ hand-written YAML, the two trigger types, the filter DSL, templates) and
245
+ [`docs/api-stability.md`](docs/api-stability.md#botconfig-yaml-schema) for
246
+ embedding a `BotManager` onto a running `IRCd`.
247
+
248
+ **Operational note.** Since 9.7.0, `IRCd.start()` binds the webhook listener
249
+ when `webhook_port` is set (>0), giving webhook-triggered bots HTTP ingress
250
+ under standalone `agentirc serve`; when `webhook_port` is unset/0 nothing is
251
+ bound (preserving the 9.5.0 default). The field stays in `ServerConfig` for
252
+ backward compatibility with culture's `~/.culture/server.yaml`.
230
253
 
231
254
  ## Current state and roadmap
232
255
 
@@ -107,15 +107,18 @@ multi-host federation.
107
107
 
108
108
  ## Public API and stability
109
109
 
110
- Three modules form the **public, semver-tracked surface**. Everything else
110
+ Six modules form the **public, semver-tracked surface**. Everything else
111
111
  under `agentirc.*` is internal and may be refactored — including renamed,
112
112
  split, or removed — in any minor or patch release.
113
113
 
114
- | Module | Members |
115
- |---|---|
116
- | [`agentirc.config`](docs/api-stability.md#agentircconfig) | `ServerConfig`, `LinkConfig`, `TelemetryConfig`, `ServerConfig.from_yaml(path)` |
117
- | [`agentirc.cli`](docs/api-stability.md#agentirccli) | `main()`, `dispatch(argv) -> int` |
118
- | [`agentirc.protocol`](docs/api-stability.md#agentircprotocol) | Verb constants, numeric reply codes, IRCv3/extension tag names, the bot extension surface (`Event`, `EventType`, `EVENT_TYPE_*`, `EVENTSUB`/`EVENTUNSUB`/`EVENT`/`EVENTERR`/`EVENTPUB`/`SEVENT`, `BOT_CAP`) |
114
+ | Module | Members | Since |
115
+ |---|---|---|
116
+ | [`agentirc.config`](docs/api-stability.md#agentircconfig) | `ServerConfig`, `LinkConfig`, `TelemetryConfig`, `ServerConfig.from_yaml(path)` | 9.0.0 |
117
+ | [`agentirc.cli`](docs/api-stability.md#agentirccli) | `main()`, `dispatch(argv) -> int` | 9.2.0 |
118
+ | [`agentirc.protocol`](docs/api-stability.md#agentircprotocol) | Verb constants, numeric reply codes, IRCv3/extension tag names, the bot extension surface (`Event`, `EventType`, `EVENT_TYPE_*`, `EVENTSUB`/`EVENTUNSUB`/`EVENT`/`EVENTERR`/`EVENTPUB`/`SEVENT`, `BOT_CAP`) | 9.2.0 |
119
+ | [`agentirc.ircd`](docs/api-stability.md#embedding-agentirc-in-process) | `IRCd` (constructor + `start`/`stop`/`emit_event` + core attributes) | 9.6.0 |
120
+ | [`agentirc.virtual_client`](docs/api-stability.md#embedding-agentirc-in-process) | `VirtualClient` | 9.6.0 |
121
+ | [`agentirc.bots`](docs/api-stability.md#botconfig-yaml-schema) | `BotManager`, `Bot`, `BotConfig` — embedded YAML-spec'd bots (see [`docs/bots.md`](docs/bots.md)) | 9.7.0 |
119
122
 
120
123
  `agentirc.cli.dispatch(argv)` is the in-process integration surface — it is
121
124
  what culture's `culture server` shim calls today. It returns `int` on
@@ -184,11 +187,31 @@ behavior) and
184
187
  [`docs/api-stability.md#bot-extension-surface-shipped-in-950`](docs/api-stability.md#bot-extension-surface-shipped-in-950)
185
188
  for the wire contract.
186
189
 
187
- **Operational note.** As of 9.5.0, `agentirc` no longer binds `webhook_port`
188
- even when set in YAML — the field stays in `ServerConfig` for backward
189
- compatibility with culture's `~/.culture/server.yaml`, but consumers that
190
- need webhook→bot dispatch host their own HTTP listener (notably culture).
191
- The field will be removed in 10.0.0.
190
+ ## Embedded bots (since 9.7.0)
191
+
192
+ Distinct from the out-of-process extension API above, `agentirc.bots` is an
193
+ **in-process** bot framework: deterministic, YAML-spec'd automations that run as
194
+ `VirtualClient` presences inside the IRCd (no separate process). A bot reacts to
195
+ an event filter or an HTTP webhook and replies with a Jinja2-templated message.
196
+
197
+ ```bash
198
+ agentirc bot create pinger --owner ori \
199
+ --trigger event \
200
+ --event-filter "type == 'user.message' and channel == '#general'" \
201
+ --channels '#general' --template 'pong {{event.nick}}'
202
+ ```
203
+
204
+ Bots live under `~/.culture/bots/<name>/bot.yaml` and load when the server
205
+ starts. See **[`docs/bots.md`](docs/bots.md)** for the authoring guide (CLI +
206
+ hand-written YAML, the two trigger types, the filter DSL, templates) and
207
+ [`docs/api-stability.md`](docs/api-stability.md#botconfig-yaml-schema) for
208
+ embedding a `BotManager` onto a running `IRCd`.
209
+
210
+ **Operational note.** Since 9.7.0, `IRCd.start()` binds the webhook listener
211
+ when `webhook_port` is set (>0), giving webhook-triggered bots HTTP ingress
212
+ under standalone `agentirc serve`; when `webhook_port` is unset/0 nothing is
213
+ bound (preserving the 9.5.0 default). The field stays in `ServerConfig` for
214
+ backward compatibility with culture's `~/.culture/server.yaml`.
192
215
 
193
216
  ## Current state and roadmap
194
217
 
@@ -25,6 +25,7 @@ import argparse
25
25
  import sys
26
26
  import time
27
27
 
28
+ from agentirc.bots.filter_dsl import FilterParseError, compile_filter
28
29
  from agentirc.bots.config import (
29
30
  BOT_CONFIG_FILE,
30
31
  BOTS_DIR,
@@ -51,7 +52,19 @@ def register(subparsers: argparse._SubParsersAction) -> None:
51
52
  bot_create.add_argument("--owner", required=True, help="Owner nick (e.g. spark-ori)")
52
53
  bot_create.add_argument("--channels", nargs="+", default=[], help="Channels to join")
53
54
  bot_create.add_argument(
54
- "--trigger", default="webhook", choices=["webhook"], help="Trigger type"
55
+ "--trigger",
56
+ default="webhook",
57
+ choices=["webhook", "event"],
58
+ help="Trigger type: 'webhook' (HTTP POST) or 'event' (matches an "
59
+ "event filter). 'event' requires --event-filter.",
60
+ )
61
+ bot_create.add_argument(
62
+ "--event-filter",
63
+ default=None,
64
+ help="Filter expression for an event-triggered bot, e.g. "
65
+ "\"type == 'user.message' and channel == '#general'\". "
66
+ "Matches against {type, channel, nick, data}. Required for "
67
+ "--trigger event.",
55
68
  )
56
69
  bot_create.add_argument("--mention", default=None, help="Agent to @mention on trigger")
57
70
  bot_create.add_argument("--template", default=None, help="Message template")
@@ -141,6 +154,29 @@ def _bot_create(args: argparse.Namespace) -> int:
141
154
  # name has no prefix yet — mimic culture's auto-prefix
142
155
  name = f"{owner}-{name}"
143
156
 
157
+ # An event-triggered bot is meaningless without a filter; validate it
158
+ # compiles now so the user gets immediate feedback instead of a silent
159
+ # skip at load time.
160
+ event_filter = getattr(args, "event_filter", None)
161
+ if args.trigger == "event":
162
+ if not event_filter:
163
+ print(
164
+ "Error: --trigger event requires --event-filter",
165
+ file=sys.stderr,
166
+ )
167
+ return 1
168
+ try:
169
+ compile_filter(event_filter)
170
+ except (FilterParseError, TypeError) as exc:
171
+ print(f"Error: invalid --event-filter: {exc}", file=sys.stderr)
172
+ return 1
173
+ elif event_filter:
174
+ print(
175
+ "Error: --event-filter only applies to --trigger event",
176
+ file=sys.stderr,
177
+ )
178
+ return 1
179
+
144
180
  bot_config = BotConfig(
145
181
  name=name,
146
182
  owner=args.owner,
@@ -152,6 +188,7 @@ def _bot_create(args: argparse.Namespace) -> int:
152
188
  mention=args.mention,
153
189
  template=args.template,
154
190
  fallback="json",
191
+ event_filter=event_filter,
155
192
  )
156
193
 
157
194
  bot_dir = BOTS_DIR / name
@@ -163,6 +200,8 @@ def _bot_create(args: argparse.Namespace) -> int:
163
200
  print(f"Bot '{name}' created at {bot_dir}")
164
201
  print(f" Owner: {args.owner}")
165
202
  print(f" Trigger: {args.trigger}")
203
+ if event_filter:
204
+ print(f" Filter: {event_filter}")
166
205
  if args.channels:
167
206
  print(f" Channels: {', '.join(args.channels)}")
168
207
  if args.mention:
@@ -0,0 +1,174 @@
1
+ # Writing agentirc bots
2
+
3
+ agentirc bots are **deterministic, YAML-spec'd chatops automations** — think
4
+ ChanServ-class services, not LLM agents. Each bot runs as an in-process
5
+ `VirtualClient` presence inside a running IRCd: it has no separate process and
6
+ lives and dies with the server. A bot reacts to either an **event** (matched by
7
+ a filter expression) or a **webhook** (an inbound HTTP POST) and responds by
8
+ sending a templated message to its channels and/or emitting a custom event.
9
+
10
+ This guide is for **authoring** bots. For embedding the bot framework in your
11
+ own process (installing a `BotManager` onto an `IRCd`), see
12
+ [`api-stability.md` → Embedding bots](api-stability.md#botconfig-yaml-schema).
13
+
14
+ ## Where bots live
15
+
16
+ Each bot is a directory under the bots dir with a single `bot.yaml`:
17
+
18
+ ```
19
+ ~/.culture/bots/<bot-name>/bot.yaml
20
+ ```
21
+
22
+ The default bots dir is `~/.culture/bots/` (shared with culture for continuity).
23
+ `agentirc bot …` and the in-process `BotManager.load_bots()` both read from it;
24
+ non-archived bots are loaded and started when the server starts.
25
+
26
+ ## The two trigger types
27
+
28
+ | Trigger | Fires when | Needs |
29
+ |---------|-----------|-------|
30
+ | `event` | an `Event` flowing through the IRCd matches the bot's filter | a `trigger.filter` expression |
31
+ | `webhook` | an HTTP POST arrives at the bot's webhook endpoint | the server's `webhook_port` set (>0) |
32
+
33
+ `event` bots are the common case (they react to chat/room activity). `webhook`
34
+ bots give external systems an HTTP ingress; the listener binds only when the
35
+ server is configured with a `webhook_port`.
36
+
37
+ ## Quick start (CLI)
38
+
39
+ ### An event-triggered bot
40
+
41
+ ```bash
42
+ agentirc bot create pinger \
43
+ --owner ori \
44
+ --trigger event \
45
+ --event-filter "type == 'user.message' and channel == '#general'" \
46
+ --channels '#general' \
47
+ --template 'pong {{event.nick}}'
48
+ ```
49
+
50
+ The filter is **compiled and validated at create time** — a malformed
51
+ expression is rejected immediately rather than silently skipped at load. The bot
52
+ is written to `~/.culture/bots/ori-pinger/bot.yaml` (the owner is prefixed to the
53
+ name when the name has no `-`).
54
+
55
+ ### A webhook bot
56
+
57
+ ```bash
58
+ agentirc bot create deployer \
59
+ --owner ori \
60
+ --trigger webhook \
61
+ --channels '#ops' \
62
+ --template 'deploy: {{payload.status}}'
63
+ ```
64
+
65
+ POST JSON to `/<bot-name>` on the server's `webhook_port` to fire it.
66
+
67
+ ### Activate, then manage
68
+
69
+ ```bash
70
+ agentirc bot start ori-pinger # mark active (loads on next server start)
71
+ agentirc bot list # list bots (use --all to include archived)
72
+ agentirc bot inspect ori-pinger # show details
73
+ agentirc bot archive ori-pinger # disable without deleting
74
+ agentirc bot unarchive ori-pinger # re-enable
75
+ ```
76
+
77
+ Newly created bots load when the server (re)starts; `agentirc bot start` marks a
78
+ bot active for the next load.
79
+
80
+ ## Hand-authoring `bot.yaml`
81
+
82
+ The CLI is a convenience over a plain YAML file you can write directly. The
83
+ layout mirrors the `BotConfig` dataclass:
84
+
85
+ ```yaml
86
+ bot:
87
+ name: ori-pinger
88
+ owner: ori
89
+ description: "Replies to messages in #general"
90
+ trigger:
91
+ type: event # "event" or "webhook"
92
+ filter: "type == 'user.message' and channel == '#general'"
93
+ output:
94
+ channels:
95
+ - "#general"
96
+ template: "pong {{ event.nick }}" # Jinja2
97
+ fallback: json # "json" or "text"
98
+ mention: null # nick to @-mention on fire, or null
99
+ dm_owner: false # also DM the owner on fire
100
+ ```
101
+
102
+ Field reference:
103
+
104
+ | Field | Meaning |
105
+ |-------|---------|
106
+ | `bot.name` / `bot.owner` / `bot.description` | metadata |
107
+ | `trigger.type` | `event` (filter) or `webhook` (HTTP POST) |
108
+ | `trigger.filter` | event-filter expression (event bots only) — see below |
109
+ | `output.channels` | channels the bot joins and addresses |
110
+ | `output.template` | Jinja2 template rendered with the bot context + event/payload |
111
+ | `output.fallback` | render fallback when the template can't render: `json` or `text` |
112
+ | `output.mention` | nick to `@`-mention when the bot fires, or `null` |
113
+ | `output.dm_owner` | also DM the owner when the bot fires |
114
+ | `archived` | set `true` to skip loading without deleting |
115
+
116
+ ## The event-filter DSL
117
+
118
+ `trigger.filter` is a small, safe boolean expression — **no code execution**.
119
+ It evaluates against the event dict `{type, channel, nick, data}` (and nested
120
+ `data.*` via dotted paths). A missing field compares `False` to everything.
121
+
122
+ Grammar:
123
+
124
+ ```text
125
+ expr := or_expr
126
+ or_expr := and_expr ('or' and_expr)*
127
+ and_expr := not_expr ('and' not_expr)*
128
+ not_expr := 'not' not_expr | cmp_expr
129
+ cmp_expr := atom (('==' | '!=' | 'in') atom)?
130
+ atom := STRING | NUMBER | LIST | IDENT('.'IDENT)* | '(' expr ')'
131
+ LIST := '[' [atom (',' atom)*] ']'
132
+ ```
133
+
134
+ Operators: `==`, `!=`, `in`, `and`, `or`, `not`, parentheses, list literals.
135
+ String literals use **single quotes**.
136
+
137
+ Examples:
138
+
139
+ ```yaml
140
+ filter: "type == 'user.message'"
141
+ filter: "type == 'user.join' and channel == '#welcome'"
142
+ filter: "type == 'user.message' and nick in ['alice', 'bob']"
143
+ filter: "type == 'custom.ping' and data.target == 'pinger'"
144
+ filter: "not (channel == '#noisy')"
145
+ ```
146
+
147
+ ## Templates
148
+
149
+ `output.template` is a Jinja2 template. Event bots get the event in context
150
+ (`event.type`, `event.channel`, `event.nick`, `event.data.*`); webhook bots get
151
+ the posted JSON as `payload`. If rendering fails, the bot falls back to
152
+ `output.fallback` (`json` dumps the context, `text` stringifies it).
153
+
154
+ ```yaml
155
+ template: "{{ event.nick }} said hi in {{ event.channel }}"
156
+ template: "deploy {{ payload.env }} -> {{ payload.status }}"
157
+ ```
158
+
159
+ ## Validate and inspect
160
+
161
+ - `agentirc bot create … --trigger event --event-filter …` compiles the filter
162
+ up front and refuses a bad one.
163
+ - `agentirc bot inspect <name>` shows the parsed config.
164
+ - A bot with a filter that fails to compile at load time is logged and skipped
165
+ (it won't crash the server).
166
+
167
+ ## See also
168
+
169
+ - [`api-stability.md`](api-stability.md#botconfig-yaml-schema) — the public
170
+ `agentirc.bots` API, the `BotConfig` schema, and a worked example of installing
171
+ a `BotManager` onto a running `IRCd` in-process.
172
+ - [`extension-api.md`](extension-api.md) — the IRCv3 bot capability and the
173
+ `EVENTSUB`/`EVENTPUB` streaming surface for **TCP-connected** bots (a different
174
+ thing from these in-process YAML bots).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentirc-cli"
3
- version = "9.7.0"
3
+ version = "9.8.0"
4
4
  description = "Agent-friendly IRCd: server core for AI agent meshes"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -270,3 +270,59 @@ def test_bot_inspect_rejects_path_traversal_names(bots_dir, capsys):
270
270
  rc = _dispatch("bot", "inspect", "../../secret")
271
271
  assert rc == 1
272
272
  assert "Invalid bot name" in capsys.readouterr().err
273
+
274
+
275
+ # ---------------------------------------------------------------------------
276
+ # Event-triggered bot creation (since 9.8.0)
277
+ # ---------------------------------------------------------------------------
278
+
279
+
280
+ def _read_yaml(bots_dir, bot_name):
281
+ import yaml
282
+
283
+ return yaml.safe_load(
284
+ (bots_dir / bot_name / "bot.yaml").read_text()
285
+ )
286
+
287
+
288
+ def test_bot_create_event_writes_filter(bots_dir, capsys):
289
+ """``--trigger event --event-filter ...`` writes an event bot spec."""
290
+ rc = _dispatch(
291
+ "bot", "create", "pinger", "--owner", "ori",
292
+ "--trigger", "event",
293
+ "--event-filter", "type == 'user.message' and channel == '#general'",
294
+ "--channels", "#general", "--template", "pong {{event.nick}}",
295
+ )
296
+ assert rc == 0
297
+ spec = _read_yaml(bots_dir, "ori-pinger")
298
+ assert spec["trigger"]["type"] == "event"
299
+ assert spec["trigger"]["filter"] == "type == 'user.message' and channel == '#general'"
300
+
301
+
302
+ def test_bot_create_event_requires_filter(bots_dir, capsys):
303
+ """``--trigger event`` without a filter is rejected."""
304
+ rc = _dispatch("bot", "create", "nofilter", "--owner", "ori", "--trigger", "event")
305
+ assert rc == 1
306
+ assert "requires --event-filter" in capsys.readouterr().err
307
+ assert list(bots_dir.iterdir()) == []
308
+
309
+
310
+ def test_bot_create_event_rejects_bad_filter(bots_dir, capsys):
311
+ """An --event-filter that doesn't compile is rejected at create time."""
312
+ rc = _dispatch(
313
+ "bot", "create", "badfilter", "--owner", "ori",
314
+ "--trigger", "event", "--event-filter", "type == =",
315
+ )
316
+ assert rc == 1
317
+ assert "invalid --event-filter" in capsys.readouterr().err
318
+ assert list(bots_dir.iterdir()) == []
319
+
320
+
321
+ def test_bot_create_webhook_rejects_event_filter(bots_dir, capsys):
322
+ """--event-filter only applies to event triggers."""
323
+ rc = _dispatch(
324
+ "bot", "create", "wh", "--owner", "ori",
325
+ "--trigger", "webhook", "--event-filter", "type == 'x.y'",
326
+ )
327
+ assert rc == 1
328
+ assert "only applies to --trigger event" in capsys.readouterr().err
@@ -10,7 +10,7 @@ resolution-markers = [
10
10
 
11
11
  [[package]]
12
12
  name = "agentirc-cli"
13
- version = "9.7.0"
13
+ version = "9.8.0"
14
14
  source = { editable = "." }
15
15
  dependencies = [
16
16
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes