Skip to content

Docs: document durable + refreshable ChannelDirectory (PR #2186) #817

Description

@MervinPraison

Context

PraisonAI PR #2186 (merged 2026-06-23, fixes #2185) makes ChannelDirectory (src/praisonai/praisonai/bots/delivery.py) durable and refreshable. Previously it was purely in-memory and built opportunistically from observed traffic — reachable targets and friendly aliases were lost on every restart and never refreshed from live adapters.

The behaviour change is user-visible (new files on disk, new constructor args, new behaviour for describe_targets(), new methods on DeliveryRouter) and is not reflected anywhere in the docs today.

Decision: update existing pages (no new page)

The feature is an enhancement to the existing ChannelDirectory / DeliveryRouter system, which is already documented across three pages. Creating a new page would fragment the story. Update the existing pages instead.

Per AGENTS.md placement rules: all edits below are under docs/features/no docs/concepts/ edits.

SDK ground truth (read before writing)

Source file (PraisonAI repo): src/praisonai/praisonai/bots/delivery.py (on main at SHA f7ea543).

In this PraisonAIDocs repo, the synced mirror is at praisonai/bots/delivery.py.

New ChannelDirectory constructor signature

ChannelDirectory(
    persist_path: Optional[Path] = None,   # default: ~/.praisonai/state/channel_directory.json
    aliases_path: Optional[Path] = None,   # default: ~/.praisonai/state/channel_aliases.json
)
Arg Type Default Description
persist_path Optional[Path] ~/.praisonai/state/channel_directory.json Where home + observed channels are saved. Atomic write via temp file + os.replace.
aliases_path Optional[Path] ~/.praisonai/state/channel_aliases.json Hand-editable alias overlay re-applied on every load + refresh.

Both paths sit alongside HomeChannelRegistry under ~/.praisonai/state/. Persistence is best-effort — failures are logged and never raised.

New + changed behaviour

Method / Behaviour What it does
__init__ Calls _load() then _apply_alias_overlay() so a fresh process starts pre-populated.
set_home_channel(platform, channel_id) Now also auto-persists.
observe_channel(platform, channel_id) Now auto-persists (no-op when the channel is already known, to avoid write storms).
refresh_from_adapters(adapters: Dict[str, Any]) New. Enumerates each adapter that exposes a list_channels() method (e.g. Discord/Slack), merges results into the observed set, re-applies the alias overlay, persists. Adapter errors are caught and skipped. Channel IDs are read via getattr(ch, "id", ch) so plain-string IDs also work.
describe_targets() Now also surfaces observed channels not represented by a home or alias, as {'name': '<platform>:<channel_id>', 'platform': ..., 'channel_id': ..., 'kind': 'observed'}.
_apply_alias_overlay() Reads the overlay file on every load + refresh. Aliases sourced from the overlay are tracked in _overlay_aliases so deletions from the file are honoured on the next refresh (manually-added aliases via add_alias() are never pruned). Aliased channels are also added to _observed so they become reachable even without prior traffic.

New DeliveryRouter method

DeliveryRouter.refresh_directory() -> None

Walks self._botos.list_bots(), calls self._botos.get_bot(platform) for each, builds an {platform: adapter} dict and calls self.directory.refresh_from_adapters(...). Intended to be called periodically by the gateway's background loop.

Alias overlay file format (~/.praisonai/state/channel_aliases.json)

Two accepted shapes per entry — long form and shorthand. Invalid entries (missing channel_id, malformed shorthand without :) are skipped with a warning, not raised.

{
  "engineering": { "platform": "discord", "channel_id": "555" },
  "ops": "slack:C111"
}

A kind: "observed" entry from describe_targets() looks like:

{"name": "discord:777", "platform": "discord", "channel_id": "777", "kind": "observed"}

Backward compatibility

Fully backward-compatible. Default ChannelDirectory() / DeliveryRouter(botos) construction is unchanged — both new args are optional with sensible defaults.

Pages to update

1. docs/features/proactive-delivery.mdx — primary update

This is the main page for ChannelDirectory + DeliveryRouter. Add a new section after "Configuration" (around current line 168), before "Common Patterns".

Add: "Persistence" section (one-sentence intro, then content):

  • Explain that home + observed channels auto-persist to ~/.praisonai/state/channel_directory.json and reload on start — reachable targets survive restarts.
  • Show the directory after a restart in a code block: build a ChannelDirectory(), call set_home_channel + observe_channel, restart, show the same channels still resolvable.
  • Note: persistence is best-effort; failures log, never raise.
  • Custom paths example: ChannelDirectory(persist_path=Path("/var/lib/myapp/dir.json"), aliases_path=Path("/var/lib/myapp/aliases.json")).

Add: "Pre-naming channels (alias overlay)" section:

  • One-sentence intro: "Drop a JSON file at ~/.praisonai/state/channel_aliases.json to pre-name channels before they produce any traffic."
  • Show both accepted forms (long + shorthand) in one JSON block.
  • Show how an overlay-defined alias makes the channel reachable even with no prior traffic.
  • Note: editing the file and calling refresh_directory() (or restarting) honours deletions; manually-added aliases via add_alias() are not pruned by the overlay.

Add: "Refreshing from live adapters" section:

  • Show botos.delivery_router.refresh_directory() driven from the gateway background loop.
  • Show directory.refresh_from_adapters({"discord": discord_bot, "slack": slack_bot}) for the lower-level API.
  • Explain the contract: adapters must expose list_channels() returning iterables of objects with .id (or plain strings). Adapters without list_channels are silently skipped; adapter errors are caught.

Update: "How It Works" target table (around line 111): add a row for kind: "observed":

Target Resolves to Example
<platform>:<id> Observed channel from traffic or adapter refresh (no alias / not home) Surfaced in describe_targets() as kind: "observed"

Add: Mermaid diagram showing the persistence + refresh lifecycle (use standard colour scheme from AGENTS.md §3.1):

Start → Load (channel_directory.json) → Apply overlay (channel_aliases.json)
       → Observe traffic / Refresh from adapters → Save → ...

2. docs/features/platform-aware-agents.mdx

Two small edits:

  • ReachableTarget table (around line 156): change kind row description to include "observed": 'home', 'alias', or 'observed'.
  • "How It Works" target listing: note that describe_targets() now also surfaces reachable-but-unnamed channels from live traffic + adapter refresh.

3. docs/features/channels-gateway.mdx

Update the "Reachable Targets" section (around line 361):

  • After the existing describe_targets() table, add a third row for kind: "observed": surfaced from live traffic or refresh_from_adapters(), appears as "<platform>:<channel_id>".
  • Add a short paragraph + code snippet showing the gateway calling botos.delivery_router.refresh_directory() on a background loop so the agent sees channels it has not yet been messaged from.

User-facing flow to show in each page

Each updated section should demonstrate the agent-centric flow (per AGENTS.md §1.1 #9):

  1. User asks the agent: "Post a summary to the engineering channel".
  2. Agent has never received traffic from that channel — previously it would not have been reachable.
  3. With this change: the channel is reachable because either (a) channel_aliases.json pre-named it, or (b) the gateway's periodic refresh_directory() already enumerated it via the Discord adapter's list_channels().
  4. deliver("engineering", summary) succeeds.

Style requirements (from AGENTS.md)

  • Place all edits under docs/features/ only.
  • Use Mintlify components (<Steps>, <AccordionGroup>, <Tabs>, <Card>).
  • Mermaid diagrams use the standard colour palette (#8B0000, #189AB4, #10B981, #F59E0B, #6366F1, white text, #7C90A0 strokes).
  • Imports stay simple and user-facing: from praisonai.bots.delivery import ChannelDirectory, from praisonai.bots import BotOS, TelegramBot.
  • One-sentence section intros; active voice; no forbidden phrases ("In this section…", "As you can see…", etc.).
  • Code examples must run as-is. Use realistic but simple channel IDs ("C0123456", "555").

Acceptance checklist

  • docs/features/proactive-delivery.mdx has new "Persistence", "Pre-naming channels (alias overlay)" and "Refreshing from live adapters" sections.
  • docs/features/proactive-delivery.mdx "How It Works" table covers kind: "observed".
  • docs/features/proactive-delivery.mdx has a Mermaid diagram of the persist + load + overlay + refresh lifecycle.
  • docs/features/platform-aware-agents.mdx ReachableTarget.kind lists "home", "alias", "observed".
  • docs/features/channels-gateway.mdx "Reachable Targets" section covers observed channels + refresh_directory().
  • No edits to docs/concepts/ (human-only per AGENTS.md §1.8).
  • docs.json unchanged (no new pages; existing pages stay in their current groups).
  • All examples copy-paste runnable; imports verified against praisonai/bots/delivery.py in this repo.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingclaudeTrigger Claude Code analysisdocumentationImprovements or additions to documentationenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions