channel architecture: wire protocol, daemons, supervisor

Design and implement the channel system for external communications:

- schema/channel.capnp: wire protocol for channel daemons
  (recv with all_new/min_count, send, subscribe, list)
- channels/irc/: standalone IRC daemon crate (consciousness-channel-irc)
- channels/telegram/: standalone Telegram daemon crate
  (consciousness-channel-telegram)
- src/thalamus/channels.rs: client connecting to daemon sockets
- src/thalamus/supervisor.rs: daemon lifecycle with file locking
  for multi-instance safety

Channel daemons listen on ~/.consciousness/channels/*.sock,
configs in *.json5, supervisor discovers and starts them.
IRC/Telegram modules removed from thalamus core — they're
now independent daemons that survive consciousness restarts.

Also: delete standalone tui.rs (moved to consciousness F4/F5),
fix build warnings, add F5 thalamus screen with channel status.

Co-Developed-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-04-03 18:46:14 -04:00
parent db42bf6243
commit ad5f69abb8
23 changed files with 1716 additions and 1921 deletions

59
schema/channel.capnp Normal file
View file

@ -0,0 +1,59 @@
@0xa1b2c3d4e5f60001;
# Channel wire protocol.
#
# Spoken over Unix domain sockets between the consciousness binary
# (client) and channel daemons (servers) in ~/.consciousness/channels/.
#
# Each daemon manages one channel prefix (e.g. "irc", "telegram",
# "shell"). Sub-channels are dot-separated paths within that prefix
# (e.g. "irc.#bcachefs", "shell.b200").
#
# The protocol is bidirectional but client-initiated for data:
# - Client calls recv/send explicitly
# - Server pushes lightweight notifications via callback
# - Messages aren't consumed until recv with allNew=true
#
# Multiple clients can connect simultaneously (e.g. claude-code
# and consciousness binary running in parallel).
struct ChannelInfo {
name @0 :Text; # channel path
connected @1 :Bool; # underlying transport is alive
unread @2 :UInt32; # unconsumed message count
}
struct Notification {
channel @0 :Text; # which channel has new messages
urgency @1 :UInt8; # max urgency of new messages
preview @2 :Text; # first line or summary
count @3 :UInt32; # how many new since last notification
}
# Callback interface — server pushes to client.
interface ChannelClient {
# "New messages arrived on these channels."
# Lightweight signal — client calls recv() to read content.
notify @0 (notifications :List(Notification)) -> ();
}
# Server interface — client calls these.
interface ChannelServer {
# Read from a channel. Returns flat text.
# allNew=true: all unconsumed text (marks consumed),
# plus scrollback to reach at least minCount lines.
# allNew=false: last minCount lines (pure scrollback,
# nothing consumed).
recv @0 (channel :Text, allNew :Bool, minCount :UInt32)
-> (text :Text);
# Send text to a channel.
send @1 (channel :Text, message :Text) -> ();
# Register for push notifications.
# Server calls callback.notify() when new messages arrive.
subscribe @2 (callback :ChannelClient) -> ();
# List available channels and their status.
list @3 () -> (channels :List(ChannelInfo));
}