Channel ownership
When a human talks to an agent over Telegram, their conversation needs to stay glued to one session across many messages — and a scheduled task that messages them later must land in that same thread with context, not start a confusing new one. Channel ownership is how AgentParley keeps a conversation coherent.
Bindings: one conversation, one session
An inbound message arrives on a channel instance (e.g. your Telegram bot tg) in an external conversation (a specific chat). AgentParley maps that (instance, conversation) pair to exactly one session and persists the binding. Every later message in that chat routes to the same session — so the human stays in one continuous thread instead of spawning a fresh agent each time.
(instance: tg, conversation: 12345) ──► session 1789… (the owner)
The binding is created atomically the first time a conversation is seen, so two messages arriving at once can't create two sessions for the same chat. That session is the conversation's owner.
The active channel
Each session remembers an active channel — the instance and conversation its replies go to. It's set automatically when a human message comes in, and it's what send_message and an agent's plain final text are delivered to. If a session has no active channel (a silent background run), its output just shows in the console feed.
Reaching a human proactively
What about a session that the human didn't just message — a cron mission that needs to ping them? It can't rely on an active channel being set. AgentParley resolves the agent's last-known conversation on a given instance and points the session there:
SetActiveChannelToDefault(session, "tg") → looks up the agent's most recent conversation on tg → if found, that becomes the session's active channel → if the human has never messaged on tg, there's nothing to address (no-op)
Why cron routes through the owner
Here's the problem ownership solves. Say you're mid-conversation with an agent, and an hourly cron job on that same agent checks your inbox and wants to message you. If the cron session just blasted a message, you'd suddenly be "talking to" a different session with none of your dialog's context — and your reply would land in the wrong place.
Instead:
- A cron job with a notify channel sets its session's active channel to the agent's last-known conversation on that channel.
- The message is delivered to that conversation — so it reaches you in the right thread.
- The delivery is recorded as provenance in the owning session's history (see below), so the conversation the human is actually in stays aware of what was said on its behalf.
- When you reply, the binding routes you back to the owner session — with full context — not to the cron session.
Cross-session delivery provenance
When a session that isn't the owner speaks on a channel, AgentParley appends a note to the owner's conversation log — an assistant message recording what was said on its behalf —without waking the owner. Nothing runs right then; but the next time the owner does run (because the human replied), it already has the context:
[owner session's history]
…
assistant: "{the cron's message}"
— sent to the user on your behalf by the hourly inbox check;
call get_session("…") for the full context.Delivery outcome is honest
Outbound delivery reports whether it actually landed. If a session has a real channel target but no recipient conversation (nobody to address), the send fails rather than silently dropping — so an agent is never told "sent" when nothing was. A feed-only session (no external channel) succeeds by rendering in the console.