Arcana::Directory
Registry of agent and service capabilities.
Address format:
- A routing label — any single token matching
[a-z][a-z0-9-]*, or anowner:capability-style two-token form (both halves matching the same pattern). Colons in the address are just a naming convention now, not a type marker. - Kind (agent vs service) and capability (chat/image/tts/...) are explicit fields on the listing. Legacy callers that don't set them get the old behavior: kind is derived from the address (colon = service), and capability is the substring after the colon.
- Internal ephemeral mailboxes (
_reply:<id>) are carved out by a leading underscore and are neither agents nor services.
Constants
Agent handles use the leading @ sigil so a process representing
a conversational identity ("@mj", "@arcana") doesn't collide with
a same-project tool service registered at the bare name ("mj",
"arcana"). Enforced at register time: kind=agent addresses MUST
start with @, and kind=service addresses MUST NOT. The bus
still routes purely on the address string — the sigil is a naming
convention that the registration path enforces so senders can
tell at a glance who's an agent.
Token body: what an address (or each half of a colon-form address) must match after any leading sigil is stripped.
Class methods
Migrate a pre-0.14 address to the new format. Returns nil for addresses that can't be sensibly mapped — callers should skip them with a warning rather than fabricate a bogus owner.
"memo:agent" → "memo" "chat:openai:service" → "openai:chat" (owner-first reorder) "memo:service" → nil (no owner in old form — drop) "owner:cap" / "foo" → unchanged
The owner-less ":service" form was a degenerate case from the original two-token convention. Rewriting it to "<name>:legacy" (as 0.14.0 originally did) preserved the entry but introduced a made-up owner that misleads readers; cleaner to drop and let the registrant re-register with a proper owner:capability.
TODO: remove this whole helper in 0.16 once no downstream consumers carry pre-0.14 snapshots.
Return the owner half of a service address, or nil if not a service.
Is this address a service? (Contains : and is not an internal ephemeral.)
Instance methods
Listings provided by a given owner (service address prefix).
Optional event recorder. When set, material directory actions (register, unregister, busy changes, prune) emit events.
Optional event recorder. When set, material directory actions (register, unregister, busy changes, prune) emit events.
Load listings from a JSON file. Skips addresses already registered (so built-in services registered in code take precedence). Tolerates pre-0.14 address formats and rewrites them in-place.
Look up a listing by address. Returns nil if not found.
Remove agent listings older than ttl. Services are never pruned
(they get re-registered from code on each startup anyway).
Returns the list of pruned addresses.
The walk collects candidates under the mutex, releases it, then re-acquires briefly per-address for each delete. Each delete re-verifies staleness — a re-registration between the walk and the delete is respected. Keeps every mutex acquisition O(1), so registration/lookup can slip through even during a large prune sweep.
Register a listing. Raises if the address is malformed, already taken, or its sigil doesn't match its kind.
Replace the tags on an existing listing. No-op if unregistered. Toolset uses this at start time to union user-provided tags with the names of its registered tools.
Search listings by substring match on name, description, or tags.
Set the last-seen timestamp directly (used by snapshot restore).