agentirc-cli 9.4.0__tar.gz → 9.5.0a1__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 (112) hide show
  1. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/CHANGELOG.md +28 -0
  2. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/CLAUDE.md +8 -8
  3. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/PKG-INFO +3 -3
  4. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/cli.py +4 -0
  5. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/config.py +10 -2
  6. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/protocol.py +130 -2
  7. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/skill.py +10 -37
  8. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/docs/api-stability.md +56 -0
  9. agentirc_cli-9.5.0a1/docs/extension-api.md +243 -0
  10. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/docs/superpowers/specs/2026-04-30-bootstrap-design.md +4 -3
  11. agentirc_cli-9.5.0a1/docs/superpowers/specs/2026-05-01-bot-extension-api-design.md +458 -0
  12. agentirc_cli-9.5.0a1/docs/superpowers/specs/2026-05-01-task14-audit.md +65 -0
  13. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/pyproject.toml +18 -18
  14. agentirc_cli-9.5.0a1/tests/test_protocol_bot_exports.py +214 -0
  15. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/uv.lock +1 -1
  16. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/SKILL.md +0 -0
  17. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/portability-lint.sh +0 -0
  18. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/pr-batch.sh +0 -0
  19. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/pr-comments.sh +0 -0
  20. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/pr-reply.sh +0 -0
  21. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/pr-sonar.sh +0 -0
  22. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/pr-status.sh +0 -0
  23. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills/pr-review/scripts/workflow.sh +0 -0
  24. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.claude/skills.local.yaml.example +0 -0
  25. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.github/workflows/publish.yml +0 -0
  26. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.github/workflows/tests.yml +0 -0
  27. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/.gitignore +0 -0
  28. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/LICENSE +0 -0
  29. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/README.md +0 -0
  30. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/__init__.py +0 -0
  31. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/__main__.py +0 -0
  32. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/__init__.py +0 -0
  33. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/aio.py +0 -0
  34. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/bots/__init__.py +0 -0
  35. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/bots/bot_manager.py +0 -0
  36. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/bots/http_listener.py +0 -0
  37. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/cli_shared/__init__.py +0 -0
  38. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/cli_shared/constants.py +0 -0
  39. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/cli_shared/mesh.py +0 -0
  40. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/constants.py +0 -0
  41. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/pidfile.py +0 -0
  42. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/protocol/__init__.py +0 -0
  43. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/protocol/message.py +0 -0
  44. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/protocol/replies.py +0 -0
  45. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/telemetry/__init__.py +0 -0
  46. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/telemetry/audit.py +0 -0
  47. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/telemetry/context.py +0 -0
  48. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/telemetry/metrics.py +0 -0
  49. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/telemetry/tracing.py +0 -0
  50. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/_internal/virtual_client.py +0 -0
  51. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/channel.py +0 -0
  52. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/client.py +0 -0
  53. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/events.py +0 -0
  54. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/history_store.py +0 -0
  55. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/ircd.py +0 -0
  56. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/remote_client.py +0 -0
  57. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/room_store.py +0 -0
  58. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/rooms_util.py +0 -0
  59. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/server_link.py +0 -0
  60. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/skills/__init__.py +0 -0
  61. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/skills/history.py +0 -0
  62. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/skills/icon.py +0 -0
  63. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/skills/rooms.py +0 -0
  64. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/skills/threads.py +0 -0
  65. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/agentirc/thread_store.py +0 -0
  66. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/docs/cli.md +0 -0
  67. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/docs/deployment.md +0 -0
  68. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/docs/steward/onboarding.md +0 -0
  69. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/__init__.py +0 -0
  70. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/_helpers.py +0 -0
  71. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/conftest.py +0 -0
  72. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/__init__.py +0 -0
  73. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/_fakes.py +0 -0
  74. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/_metrics_helpers.py +0 -0
  75. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_audit_emit.py +0 -0
  76. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_audit_lifecycle.py +0 -0
  77. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_audit_module.py +0 -0
  78. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_audit_parse_error.py +0 -0
  79. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_config.py +0 -0
  80. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_dispatch_span.py +0 -0
  81. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_emit_event_span.py +0 -0
  82. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_metrics_init.py +0 -0
  83. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_metrics_s2s.py +0 -0
  84. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_outbound_inject.py +0 -0
  85. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_parse_error.py +0 -0
  86. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_s2s_relay_span.py +0 -0
  87. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_server_init.py +0 -0
  88. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_server_link_inject.py +0 -0
  89. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/telemetry/test_tracing.py +0 -0
  90. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_channel.py +0 -0
  91. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_cli.py +0 -0
  92. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_config_loader.py +0 -0
  93. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_connection.py +0 -0
  94. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_discovery.py +0 -0
  95. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_events_basic.py +0 -0
  96. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_events_catalog.py +0 -0
  97. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_events_federation.py +0 -0
  98. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_events_history.py +0 -0
  99. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_events_lifecycle.py +0 -0
  100. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_events_reserved_nick.py +0 -0
  101. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_federation.py +0 -0
  102. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_history.py +0 -0
  103. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_link_reconnect.py +0 -0
  104. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_mentions.py +0 -0
  105. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_messaging.py +0 -0
  106. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_modes.py +0 -0
  107. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_room_persistence.py +0 -0
  108. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_rooms_federation.py +0 -0
  109. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_rooms_integration.py +0 -0
  110. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_server_icon_skill.py +0 -0
  111. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_skills.py +0 -0
  112. {agentirc_cli-9.4.0 → agentirc_cli-9.5.0a1}/tests/test_threads.py +0 -0
@@ -4,6 +4,34 @@ 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.5.0a1] - 2026-05-02
8
+
9
+ ### Added
10
+
11
+ - Public `agentirc.protocol` exports for the bot extension API: the `Event` dataclass, the `EventType` enum, 20 per-type `EVENT_TYPE_*` string constants, the `EVENTSUB` / `EVENTUNSUB` / `EVENT` / `EVENTERR` / `EVENTPUB` verb constants, and the `BOT_CAP = "agentirc.io/bot"` capability identifier. `Event.type` is widened to `EventType | str` so federation peers can deliver event types this version doesn't recognise. See `docs/superpowers/specs/2026-05-01-bot-extension-api-design.md` for the full design and `docs/extension-api.md` for the bot-author quick reference.
12
+ - `ServerConfig.event_subscription_queue_max: int = 1024` — per-subscription queue bound. Recognised by `ServerConfig.from_yaml` and `cli._resolve_config()` as a top-level YAML key. Consumed by the subscription registry that lands in 9.5.0a3.
13
+ - `agentirc.skill` keeps re-exporting `Event` and `EventType` for backward compat; the re-export shim is removed in 9.6.0 once Phase A2 confirms no consumer relies on the path.
14
+
15
+ ### Changed
16
+
17
+ - `EventType` upgraded from `enum.Enum` to `enum.StrEnum`. Members keep their `.value` strings unchanged, but Python consumers now observe new identity semantics: `isinstance(EventType.JOIN, str)` is `True`, `EventType.JOIN == "user.join"` is `True`, JSON serialization emits the bare string. Internal call sites verified — none compare `EventType.X` against a bare string today, so the truthier equality is pure improvement; downstream consumers that did string-equality round-trips against `EventType` see strictly more matches, never fewer.
18
+
19
+ ### Notes
20
+
21
+ - This is the **declarations slice** of the bot extension API. No daemon-level behavior changes: `EVENTSUB` etc. are reserved verb constants but the daemon does not yet handle them; `BOT_CAP` is exported but not yet advertised in `CAP LS` output. The wire-format envelope refactor lands in 9.5.0a2; the bot CAP behavior, subscription verbs, `EVENTPUB`, and `webhook_port` unbinding land in 9.5.0a3 / 9.5.0 final.
22
+ - Tracks [agentculture/agentirc#15](https://github.com/agentculture/agentirc/issues/15).
23
+
24
+ ## [9.4.1] - 2026-05-01
25
+
26
+ ### Documentation
27
+
28
+ - Marked the bootstrap closed in `docs/superpowers/specs/2026-04-30-bootstrap-design.md` and `CLAUDE.md`. Tasks 16–18 (tag `v9.4.0`, verify PyPI publish, report-back to culture) are done; the spec status note now reads as a closed timeline ("Released ✅"), and `CLAUDE.md`'s "Current state" reads "bootstrap complete (9.4.0 released)" with a non-blocking follow-ups list linking to issues #7–#12 (Track A wire-format fixes, steward backport, callsite sweep, A2 test migration).
29
+ - Fixed 14 stale `OriNachum/*` GitHub URLs in `pyproject.toml` and the bootstrap spec to canonical `agentculture/*` paths. Investigation showed these weren't merely stylistic — `https://github.com/OriNachum/culture` returns 404 and `https://github.com/OriNachum/agentirc` 301-redirects to the wrong path. URL fixes only; sha256s are content-hashed and unaffected. `cite check` still passes.
30
+
31
+ ### Notes
32
+
33
+ - Functionally identical to `9.4.0`. Published as a fresh PyPI release because `publish.yml` triggers on push-to-`main` and PyPI rejects re-publishing the same version with different sha256.
34
+
7
35
  ## [9.4.0] - 2026-05-01
8
36
 
9
37
  ### Added
@@ -2,9 +2,9 @@
2
2
 
3
3
  This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
4
 
5
- ## Current state: bootstrap functionally + docs complete (9.4.0); release ceremony remains
5
+ ## Current state: bootstrap complete (9.4.0 released)
6
6
 
7
- This repo is the agentirc server-core extraction out of the sibling project [`culture`](https://github.com/OriNachum/culture). As of 9.4.0:
7
+ This repo is the agentirc server-core extraction out of the sibling project [`culture`](https://github.com/agentculture/culture). As of 9.4.0 (tagged at `5590256` and live on PyPI):
8
8
 
9
9
  - **Server-core** (`agentirc/{ircd,server_link,channel,events,skill,remote_client,…}.py`, `agentirc/skills/{rooms,threads,history,icon}.py`) — vendored from `culture@df50942` via the `cite-don't-copy` pattern (see `[tool.citation]` in `pyproject.toml`).
10
10
  - **Client transport** (`agentirc/client.py`) — vendored from `culture/agentirc/client.py` in PR-B2.
@@ -15,13 +15,13 @@ This repo is the agentirc server-core extraction out of the sibling project [`cu
15
15
  - **Internal support** (`agentirc/_internal/`) — `aio`, `constants`, `protocol/`, `telemetry/`, `virtual_client`, `pidfile`, `cli_shared/`, `bots/` stubs.
16
16
  - **Bootstrap docs** (PR-B4, 9.4.0) — `docs/api-stability.md` (3 public modules + semver contract), `docs/cli.md` (verb table, flag reference, exit codes, YAML/CLI precedence, agentirc-vs-culture diff table), `docs/deployment.md` (on-disk footprint, systemd `Type=simple` example, container deployment, multi-host federation, log rotation, coexistence with culture, backup).
17
17
 
18
- End-to-end verified: `agentirc start --port <p>` boots a real IRCd, TCP NICK/USER handshake returns `001 RPL_WELCOME`, `agentirc stop` shuts cleanly. `agentirc serve --config server.yaml --port 9999` correctly overlays CLI flag on YAML.
18
+ End-to-end verified: `agentirc start --port <p>` boots a real IRCd, TCP NICK/USER handshake returns `001 RPL_WELCOME`, `agentirc stop` shuts cleanly. `agentirc serve --config server.yaml --port 9999` correctly overlays CLI flag on YAML. `pip install agentirc-cli==9.4.0` from real PyPI in a clean venv produces both `agentirc` and `agentirc-cli` binaries; both reach the same `agentirc.cli:main` entry point. Acceptance audit recorded at [`docs/superpowers/specs/2026-05-01-task14-audit.md`](docs/superpowers/specs/2026-05-01-task14-audit.md). Culture-side cutover unblocked via [agentculture/culture#308](https://github.com/agentculture/culture/issues/308).
19
19
 
20
- What is **not** done yet:
21
- - **Acceptance-criteria spot-check** (Task 14 in the bootstrap spec) read-only audit to confirm every bullet in §"Acceptance criteria" is before tagging.
22
- - **Release ceremony** (Tasks 16–18) tag `v9.4.0`, the existing `publish.yml` CI pushes to PyPI on push to `main`, then report version + source SHA back so culture's cutover PR can pin against it.
23
- - **Cross-repo wire-format fixes (Track A)** — `ROOMETAEND`/`ROOMETASET` typos, `ERR_NOSUCHCHANNEL` overload, `STHREAD` collapse. Each requires culture-side change first then agentirc bump.
24
- - **Steward backport** — port the 9.3.0 `pr-sonar.sh` + `workflow.sh` SonarCloud wiring back to the steward skills repo.
20
+ **Outstanding follow-ups (non-blocking; the bootstrap itself is closed):**
21
+ - **Cross-repo wire-format fixes (Track A)** [#7](https://github.com/agentculture/agentirc/issues/7) (`ROOMETAEND`/`ROOMETASET` typos), [#8](https://github.com/agentculture/agentirc/issues/8) (`ERR_NOSUCHCHANNEL` overload), [#9](https://github.com/agentculture/agentirc/issues/9) (`STHREAD` verb collapse). Each requires culture-side change first then agentirc bump.
22
+ - **Steward backport** — [#10](https://github.com/agentculture/agentirc/issues/10). Port the 9.3.0 `pr-sonar.sh` + `workflow.sh sonar` wiring upstream so other workspace projects pick it up via re-vendoring.
23
+ - **Optional callsite sweep** — [#11](https://github.com/agentculture/agentirc/issues/11). Replace inline IRC verb / numeric-reply string literals in `ircd.py`/`server_link.py`/`skills/*.py` with `agentirc.protocol.<NAME>` imports. Pure refactor.
24
+ - **A2 bot-fixtured tests (low-priority)** — [#12](https://github.com/agentculture/agentirc/issues/12). Currently in culture; could be migrated to agentirc via subprocess-fixture rewrite if culture's coverage drifts.
25
25
 
26
26
  Read the bootstrap spec at `docs/superpowers/specs/2026-04-30-bootstrap-design.md` for the full plan; it is the operative source of truth and is intentionally self-contained. The culture-side counterpart spec is at `../culture/docs/superpowers/specs/2026-04-30-agentirc-extraction-design.md` — not normally needed, but explains *why* if a decision looks arbitrary.
27
27
 
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentirc-cli
3
- Version: 9.4.0
3
+ Version: 9.5.0a1
4
4
  Summary: Agent-friendly IRCd: server core for AI agent meshes
5
- Project-URL: Homepage, https://github.com/OriNachum/agentirc
6
- Project-URL: Issues, https://github.com/OriNachum/agentirc/issues
5
+ Project-URL: Homepage, https://github.com/agentculture/agentirc
6
+ Project-URL: Issues, https://github.com/agentculture/agentirc/issues
7
7
  Author: Ori Nachum
8
8
  License: MIT License
9
9
 
@@ -185,6 +185,9 @@ def _resolve_config(args: argparse.Namespace) -> "ServerConfig": # noqa: F821 (
185
185
  args.data_dir, raw.get("data_dir"), os.path.expanduser("~/.culture/data")
186
186
  )
187
187
  )
188
+ event_subscription_queue_max = _pick(
189
+ None, raw.get("event_subscription_queue_max"), 1024
190
+ )
188
191
 
189
192
  cfg = ServerConfig(
190
193
  name=name or "agentirc",
@@ -195,6 +198,7 @@ def _resolve_config(args: argparse.Namespace) -> "ServerConfig": # noqa: F821 (
195
198
  links=_resolve_links(getattr(args, "link", None), raw.get("links") or []),
196
199
  system_bots=raw.get("system_bots") or {},
197
200
  telemetry=_build_telemetry(raw.get("telemetry") or {}),
201
+ event_subscription_queue_max=event_subscription_queue_max,
198
202
  )
199
203
 
200
204
  args.name = name # may be None — handler resolves via default-server file
@@ -53,13 +53,21 @@ class ServerConfig:
53
53
  links: list[LinkConfig] = field(default_factory=list)
54
54
  system_bots: dict = field(default_factory=dict)
55
55
  telemetry: TelemetryConfig = field(default_factory=TelemetryConfig)
56
+ # Bot extension API (9.5.0): per-subscription event-queue bound. When
57
+ # exceeded, the subscription is dropped with EVENTERR :backpressure-overflow
58
+ # and the bot reconciles via re-subscribe + BACKFILL. Behavior wires up in
59
+ # 9.5.0a3; the field is exposed in 9.5.0a1 so consumers can pin against the
60
+ # public surface.
61
+ event_subscription_queue_max: int = 1024
56
62
 
57
63
  @classmethod
58
64
  def from_yaml(cls, path: str | Path) -> "ServerConfig":
59
65
  """Load a ServerConfig from a YAML file.
60
66
 
61
67
  Recognises top-level ``server`` (host/port/name), ``telemetry``,
62
- ``links``, ``webhook_port``, ``data_dir``, and ``system_bots``.
68
+ ``links``, ``webhook_port``, ``data_dir``, ``system_bots``, and
69
+ ``event_subscription_queue_max`` (added in 9.5.0a1; consumed by
70
+ the subscription registry that lands in 9.5.0a3).
63
71
  Unknown top-level keys (``supervisor``, ``agents``, ``buffer_size``,
64
72
  ``poll_interval``, ``sleep_start``, ``sleep_end``) are silently
65
73
  ignored — those belong to culture's broader process supervisor,
@@ -104,7 +112,7 @@ def _yaml_kwargs(raw: dict[str, Any]) -> dict[str, Any]:
104
112
  for key in ("name", "host", "port"):
105
113
  if key in server_section:
106
114
  kwargs[key] = server_section[key]
107
- for key in ("webhook_port", "data_dir"):
115
+ for key in ("webhook_port", "data_dir", "event_subscription_queue_max"):
108
116
  if key in raw:
109
117
  kwargs[key] = raw[key]
110
118
  links_section = raw.get("links") or []
@@ -1,6 +1,6 @@
1
- """Public protocol surface for agentirc — verbs, numerics, and tags.
1
+ """Public protocol surface for agentirc — verbs, numerics, tags, and the bot extension API.
2
2
 
3
- Semver-tracked module. Three categories of constants live here:
3
+ Semver-tracked module. Five categories of public symbols live here:
4
4
 
5
5
  1. **Verb names** — IRC command verbs as bare uppercase tokens. Mostly
6
6
  RFC 2812 (PRIVMSG, JOIN, QUIT, ...), plus agentirc skill verbs
@@ -14,6 +14,16 @@ Semver-tracked module. Three categories of constants live here:
14
14
  consumers don't reach into the underscore namespace.
15
15
  3. **Message tag names** — IRCv3 tag keys for traceparent/tracestate
16
16
  and agentirc-specific event tags.
17
+ 4. **Event types and the Event dataclass** — :class:`EventType`
18
+ (a :class:`enum.StrEnum` of 20 dotted-lowercase wire strings) and
19
+ :class:`Event` (a frozen-shape dataclass). Plus 20 ``EVENT_TYPE_*``
20
+ per-type string constants for callers that prefer bare strings over
21
+ enum-coercion at JSON boundaries. Added in 9.5.0a1 as part of the
22
+ bot extension API.
23
+ 5. **Bot extension verbs and capability** — ``EVENTSUB``, ``EVENTUNSUB``,
24
+ ``EVENT``, ``EVENTERR``, ``EVENTPUB`` verb constants and
25
+ ``BOT_CAP = "agentirc.io/bot"``. Reserved in 9.5.0a1; daemon
26
+ behavior wires up in 9.5.0a3 / 9.5.0 final.
17
27
 
18
28
  Existing call sites under ``agentirc.ircd``, ``agentirc.server_link``
19
29
  and the skills modules still use inline string literals. Migrating them
@@ -31,6 +41,11 @@ clients and federation. They need a coordinated cross-repo bump.
31
41
 
32
42
  from __future__ import annotations
33
43
 
44
+ import time
45
+ from dataclasses import dataclass, field
46
+ from enum import StrEnum
47
+ from typing import Any
48
+
34
49
  # ---------------------------------------------------------------------------
35
50
  # Numeric reply codes (re-exported from the internal module)
36
51
  # ---------------------------------------------------------------------------
@@ -164,6 +179,90 @@ ROOMETAEND = "ROOMETAEND" # SIC: typo preserved for wire compat (ROOMMETAEND ta
164
179
  ROOMETASET = "ROOMETASET" # SIC: typo preserved for wire compat (ROOMMETASET target)
165
180
 
166
181
 
182
+ # ---------------------------------------------------------------------------
183
+ # Bot extension API (9.5.0)
184
+ # ---------------------------------------------------------------------------
185
+ # Public Event dataclass + EventType enum, per-type string constants, the
186
+ # EVENTSUB / EVENTUNSUB / EVENT / EVENTERR / EVENTPUB verb names, and the
187
+ # bot-CAP token. See docs/superpowers/specs/2026-05-01-bot-extension-api-design.md
188
+ # for the wire format and verb syntax. Behavior wiring lands in 9.5.0a2/a3;
189
+ # 9.5.0a1 ships these symbols only.
190
+
191
+ # `EventType` is `StrEnum` so `EventType.JOIN == "user.join"` is True at JSON
192
+ # boundaries. Adding a new member is a minor bump; renaming or removing one
193
+ # is a major bump.
194
+
195
+
196
+ class EventType(StrEnum):
197
+ MESSAGE = "message"
198
+ JOIN = "user.join"
199
+ PART = "user.part"
200
+ QUIT = "user.quit"
201
+ TOPIC = "topic"
202
+ ROOMMETA = "room.meta"
203
+ TAGS = "tags.update"
204
+ ROOMARCHIVE = "room.archive"
205
+ THREAD_CREATE = "thread.create"
206
+ THREAD_MESSAGE = "thread.message"
207
+ THREAD_CLOSE = "thread.close"
208
+ AGENT_CONNECT = "agent.connect"
209
+ AGENT_DISCONNECT = "agent.disconnect"
210
+ CONSOLE_OPEN = "console.open"
211
+ CONSOLE_CLOSE = "console.close"
212
+ SERVER_WAKE = "server.wake"
213
+ SERVER_SLEEP = "server.sleep"
214
+ SERVER_LINK = "server.link"
215
+ SERVER_UNLINK = "server.unlink"
216
+ ROOM_CREATE = "room.create"
217
+
218
+
219
+ @dataclass
220
+ class Event:
221
+ # `type` is widened to `EventType | str` so federation peers can deliver
222
+ # event types this version doesn't recognise without raising. Subscribers
223
+ # must tolerate unknown types (forward-compat).
224
+ type: EventType | str
225
+ channel: str | None
226
+ nick: str
227
+ data: dict[str, Any] = field(default_factory=dict)
228
+ timestamp: float = field(default_factory=time.time)
229
+
230
+
231
+ # Per-type string constants — parallel to `EventType` for callers that prefer
232
+ # bare strings (e.g. comparing JSON-decoded `type` field without enum-coercing).
233
+ EVENT_TYPE_MESSAGE = "message"
234
+ EVENT_TYPE_USER_JOIN = "user.join"
235
+ EVENT_TYPE_USER_PART = "user.part"
236
+ EVENT_TYPE_USER_QUIT = "user.quit"
237
+ EVENT_TYPE_TOPIC = "topic"
238
+ EVENT_TYPE_ROOM_META = "room.meta"
239
+ EVENT_TYPE_TAGS_UPDATE = "tags.update"
240
+ EVENT_TYPE_ROOM_ARCHIVE = "room.archive"
241
+ EVENT_TYPE_THREAD_CREATE = "thread.create"
242
+ EVENT_TYPE_THREAD_MESSAGE = "thread.message"
243
+ EVENT_TYPE_THREAD_CLOSE = "thread.close"
244
+ EVENT_TYPE_AGENT_CONNECT = "agent.connect"
245
+ EVENT_TYPE_AGENT_DISCONNECT = "agent.disconnect"
246
+ EVENT_TYPE_CONSOLE_OPEN = "console.open"
247
+ EVENT_TYPE_CONSOLE_CLOSE = "console.close"
248
+ EVENT_TYPE_SERVER_WAKE = "server.wake"
249
+ EVENT_TYPE_SERVER_SLEEP = "server.sleep"
250
+ EVENT_TYPE_SERVER_LINK = "server.link"
251
+ EVENT_TYPE_SERVER_UNLINK = "server.unlink"
252
+ EVENT_TYPE_ROOM_CREATE = "room.create"
253
+
254
+ # Bot extension verbs.
255
+ EVENTSUB = "EVENTSUB"
256
+ EVENTUNSUB = "EVENTUNSUB"
257
+ EVENT = "EVENT"
258
+ EVENTERR = "EVENTERR"
259
+ EVENTPUB = "EVENTPUB"
260
+
261
+ # Bot-CAP token. Vendored namespace per IRCv3 conventions, prevents collision
262
+ # with hypothetical bare-`bot` caps from non-agentirc IRC servers.
263
+ BOT_CAP = "agentirc.io/bot"
264
+
265
+
167
266
  __all__ = [
168
267
  # Numerics
169
268
  "ERR_ALREADYREGISTRED",
@@ -256,4 +355,33 @@ __all__ = [
256
355
  "STAGS",
257
356
  "STHREAD",
258
357
  "STOPIC",
358
+ # Bot extension API (9.5.0)
359
+ "BOT_CAP",
360
+ "EVENT",
361
+ "EVENTERR",
362
+ "EVENTPUB",
363
+ "EVENTSUB",
364
+ "EVENTUNSUB",
365
+ "Event",
366
+ "EventType",
367
+ "EVENT_TYPE_AGENT_CONNECT",
368
+ "EVENT_TYPE_AGENT_DISCONNECT",
369
+ "EVENT_TYPE_CONSOLE_CLOSE",
370
+ "EVENT_TYPE_CONSOLE_OPEN",
371
+ "EVENT_TYPE_MESSAGE",
372
+ "EVENT_TYPE_ROOM_ARCHIVE",
373
+ "EVENT_TYPE_ROOM_CREATE",
374
+ "EVENT_TYPE_ROOM_META",
375
+ "EVENT_TYPE_SERVER_LINK",
376
+ "EVENT_TYPE_SERVER_SLEEP",
377
+ "EVENT_TYPE_SERVER_UNLINK",
378
+ "EVENT_TYPE_SERVER_WAKE",
379
+ "EVENT_TYPE_TAGS_UPDATE",
380
+ "EVENT_TYPE_THREAD_CLOSE",
381
+ "EVENT_TYPE_THREAD_CREATE",
382
+ "EVENT_TYPE_THREAD_MESSAGE",
383
+ "EVENT_TYPE_TOPIC",
384
+ "EVENT_TYPE_USER_JOIN",
385
+ "EVENT_TYPE_USER_PART",
386
+ "EVENT_TYPE_USER_QUIT",
259
387
  ]
@@ -1,9 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- import time
4
- from dataclasses import dataclass, field
5
- from enum import Enum
6
- from typing import TYPE_CHECKING, Any
3
+ from typing import TYPE_CHECKING
4
+
5
+ # Event and EventType moved to agentirc.protocol in 9.5.0a1 as part of the
6
+ # bot extension API public surface. This module keeps re-exporting them so
7
+ # internal call sites and any pre-9.5 vendored consumers keep working; the
8
+ # re-export is removed in 9.6.0 once Phase A2 confirms no consumer relies on
9
+ # this path.
10
+ from agentirc.protocol import Event, EventType
11
+
12
+ __all__ = ["Event", "EventType", "Skill"]
7
13
 
8
14
  if TYPE_CHECKING:
9
15
  from agentirc.client import Client
@@ -11,39 +17,6 @@ if TYPE_CHECKING:
11
17
  from agentirc._internal.protocol.message import Message
12
18
 
13
19
 
14
- class EventType(Enum):
15
- MESSAGE = "message"
16
- JOIN = "user.join"
17
- PART = "user.part"
18
- QUIT = "user.quit"
19
- TOPIC = "topic"
20
- ROOMMETA = "room.meta"
21
- TAGS = "tags.update"
22
- ROOMARCHIVE = "room.archive"
23
- THREAD_CREATE = "thread.create"
24
- THREAD_MESSAGE = "thread.message"
25
- THREAD_CLOSE = "thread.close"
26
- # Lifecycle + link events introduced by mesh-events feature.
27
- AGENT_CONNECT = "agent.connect"
28
- AGENT_DISCONNECT = "agent.disconnect"
29
- CONSOLE_OPEN = "console.open"
30
- CONSOLE_CLOSE = "console.close"
31
- SERVER_WAKE = "server.wake"
32
- SERVER_SLEEP = "server.sleep"
33
- SERVER_LINK = "server.link"
34
- SERVER_UNLINK = "server.unlink"
35
- ROOM_CREATE = "room.create"
36
-
37
-
38
- @dataclass
39
- class Event:
40
- type: EventType
41
- channel: str | None
42
- nick: str
43
- data: dict[str, Any] = field(default_factory=dict)
44
- timestamp: float = field(default_factory=time.time)
45
-
46
-
47
20
  class Skill:
48
21
  name: str = ""
49
22
  commands: set[str] = set()
@@ -12,6 +12,33 @@ import only from these three modules.
12
12
  | [`agentirc.cli`](#agentirccli) | `main()`, `dispatch(argv) -> int` | Public, semver-tracked |
13
13
  | [`agentirc.protocol`](#agentircprotocol) | Verb constants, numeric reply codes, IRCv3/extension tag names | Public, semver-tracked |
14
14
 
15
+ > **Bot extension API — phased rollout:**
16
+ >
17
+ > - **9.5.0a1 (current alpha — declarations slice):** `agentirc.protocol`
18
+ > exports the `Event` dataclass, the `EventType` enum (now `StrEnum`),
19
+ > 20 per-type `EVENT_TYPE_*` string constants, the
20
+ > `EVENTSUB`/`EVENTUNSUB`/`EVENT`/`EVENTERR`/`EVENTPUB` verb constants,
21
+ > and the `BOT_CAP = "agentirc.io/bot"` capability identifier.
22
+ > `ServerConfig` gains the `event_subscription_queue_max: int = 1024`
23
+ > field. **The symbols are importable; the daemon does not yet handle
24
+ > the verbs and does not advertise `BOT_CAP`** — calling them is a
25
+ > no-op until the behavior slices land.
26
+ > - **9.5.0a2 (planned):** wire-format envelope refactor —
27
+ > `_build_event_payload`/`_encode_event_data` emit the 5-field envelope
28
+ > `{type, channel, nick, data, timestamp}`; federated `SEVENT` shifts
29
+ > to the new shape. Internal change; no new public symbols.
30
+ > - **9.5.0a3 (planned):** bot-CAP behavior, `EVENTSUB`/`EVENTUNSUB`
31
+ > handlers, the in-memory `SubscriptionRegistry`, and `EVENTPUB`
32
+ > handler. Daemon starts advertising `BOT_CAP` in `CAP LS` output.
33
+ > - **9.5.0 (final):** `webhook_port` no longer bound; `cli.md` /
34
+ > `deployment.md` updated; this block flips from "phased rollout" to
35
+ > "current," and the version-history table picks up a 9.5.0 row.
36
+ >
37
+ > Wire format and verb syntax are specified in
38
+ > [`docs/superpowers/specs/2026-05-01-bot-extension-api-design.md`](superpowers/specs/2026-05-01-bot-extension-api-design.md);
39
+ > a quick reference for bot authors is at [`docs/extension-api.md`](extension-api.md).
40
+ > Tracking issue: [agentculture/agentirc#15](https://github.com/agentculture/agentirc/issues/15).
41
+
15
42
  ## Semver contract
16
43
 
17
44
  Following [SemVer 2.0](https://semver.org/):
@@ -179,6 +206,35 @@ Re-exported from `agentirc._internal.protocol.replies`. About 33 names:
179
206
  Re-exported from `agentirc._internal.telemetry.context`:
180
207
  `TRACEPARENT_TAG`, `TRACESTATE_TAG`, `EVENT_TAG_TYPE`, `EVENT_TAG_DATA`.
181
208
 
209
+ ### Reserved for 9.5.0: bot extension surface
210
+
211
+ These additions are **specified but not yet implemented**. They will land
212
+ together as a single minor bump in 9.5.0 — see the design spec at
213
+ [`docs/superpowers/specs/2026-05-01-bot-extension-api-design.md`](superpowers/specs/2026-05-01-bot-extension-api-design.md)
214
+ for rationale, federation behavior, and acceptance criteria, and
215
+ [`docs/extension-api.md`](extension-api.md) for the bot-author quick
216
+ reference.
217
+
218
+ - **Event verbs:** `EVENTSUB`, `EVENTUNSUB`, `EVENT`, `EVENTERR`, `EVENTPUB`. Subscribers stream events with filter syntax (`type=`/`channel=`/`nick=` AND-ed globs); `EVENTPUB` lets a bot emit its own typed events back into the stream (server-side validation of `type` against `EVENT_TYPE_RE`; `nick` and `timestamp` derived server-side, not trusted from the client).
219
+ - **Bot capability:** `BOT_CAP = "agentirc.io/bot"`. When negotiated via
220
+ the existing CAP REQ/ACK flow, the connection is treated as a bot:
221
+ silent JOIN/PART/QUIT broadcasts, no auto-op on channel creation,
222
+ `+` prefix in NAMES output, `B` flag in WHO output, authorized to
223
+ issue `EVENTSUB`.
224
+ - **Event dataclass and enum:** `Event` and `EventType` (currently
225
+ internal in `agentirc.skill`). Promoted to public for Python consumers.
226
+ Wire format — not the Python class names — is the contract; non-Python
227
+ bots pin against the JSON shape documented in `extension-api.md`.
228
+ - **Per-type string constants:** `EVENT_TYPE_MESSAGE`,
229
+ `EVENT_TYPE_USER_JOIN`, …, one per type-string in the canonical
230
+ vocabulary. Convenience for callers that prefer non-enum-aware
231
+ constants.
232
+
233
+ The `ServerConfig` additions (one new field
234
+ `event_subscription_queue_max: int = 1024`) and the `webhook_port`
235
+ binding-removal are described under
236
+ [`agentirc.config`](#agentircconfig) once 9.5.0 lands.
237
+
182
238
  ### Wire-format quirks (preserved verbatim)
183
239
 
184
240
  Four known wire-format issues are **preserved** rather than fixed,