Channels
A channel connects agents to a messaging platform. You register a factory (the channel kind); the operator creates one or more instances of it. Telegram ships as a plugin and is the reference implementation.
The interfaces
A channel instance implements IHumanChannel:
public interface IHumanChannel : IAsyncDisposable
{
string InstanceId { get; }
ChannelCapabilities Capabilities { get; } // Typing / Attachments / Markdown / Voice
Task Start(CancellationToken ct = default);
// returns true only if the provider actually accepted it (so callers stop reporting "sent")
Task<bool> Send(string conversationId, OutboundMessage msg, CancellationToken ct = default);
Task SetPresence(string conversationId, PresenceState state, CancellationToken ct = default);
IAsyncEnumerable<InboundHumanMessage> Receive(CancellationToken ct = default);
}A factory creates instances for a provider kind and declares the operator config it needs:
public interface IHumanChannelFactory
{
string ProviderId { get; } // "telegram", "slack", ...
IHumanChannel Create(ChannelInstanceConfig cfg);
string? Title => null; // console display name
IReadOnlyList<ConfigField> ConfigFields => []; // fields collected when creating an instance
}Registering the factory
Channels register pre-build (Configure), but a real channel usually needs services that only exist post-build (e.g. the vault to resolve its bot token). The pattern: register the factory in Configure, bind it to the service provider in Start.
public sealed class TelegramPlugin : IAgentParleyPlugin
{
private readonly TelegramChannelFactory factory = new();
public PluginManifest Manifest => new()
{
Id = "telegram", Version = "1.0.0", Author = "AgentParley",
DependsOn = ["base"], ContractVersion = "0.1",
};
public void Configure(IPluginContext context) => context.Channels.Register(factory);
public Task Start(IPluginRuntime runtime, CancellationToken ct)
{
factory.Bind(runtime.Services); // now it can resolve ISecretsProvider when creating instances
return Task.CompletedTask;
}
public Task Stop(CancellationToken ct) => Task.CompletedTask;
public Task Uninstall(IPluginRuntime runtime, CancellationToken ct) => Task.CompletedTask;
}Instances & routing
An operator configures instances in parley.yaml (or the console). Credentials are vault references, never plaintext:
channels:
- instanceId: tg
providerId: telegram
secretRefs: { token: telegram-token } # resolved from the vaultInbound messages carry an InstanceId, ConversationId, and Sender. The router binds (instance, conversation) to one session, so a person stays in a single continuous thread across turns.
Setting up Telegram
- Create a bot with @BotFather and copy its token.
parley secret set telegram-token(paste it — stored in the vault).- Add the channel instance (above), publish the plugin into
~/parley/plugins/, restart. - On the agent, list the
tginstance and add your numeric Telegram user id to its authorized senders.