Not a patch, because the obvious fix double-delivers. Filing the analysis instead.
What happens
src/process/upstream.lua delivers to exactly one recipient:
function session_upstream:_send_message(topic, message)
-- Send to parent process (which can relay to all connections)
if self.parent_pid then
process.send(self.parent_pid :: string, topic, message)
end
end
and the only thing in the module that relays onward is src/process/plugin.lua:559-562:
elseif string.sub(topic, 1, string.len(consts.TOPIC_PREFIXES.SESSION)) == consts.TOPIC_PREFIXES.SESSION then
if state.user_hub_pid then
process.send(state.user_hub_pid :: string, topic, payload:data())
end
So received, response_started, content, update and command_response reach a browser only when the parent happens to be the relay plugin. Spawn a session from anything else — a channel bridge, a job, an automation that wants the conversation visible in the UI — and every upstream update stops at that parent. The session runs correctly, the rows are written, and nothing on screen moves.
That is a supported-looking arrangement with an unsupported half: spawn a session process is a public shape, and only one of its callers gets updates.
Why conn_pid is not the answer
conn_pid looks like the missing route. It is accepted by upstream.new, stored on the instance, and refreshed by the session process on every inbound message (src/process/session.lua:209 and :237). src/process/plugin.lua threads it through spawn payloads and command payloads, and errors go straight to it via send_error. src/process/message_handlers.lua:361-363 reaches in for it as the streaming reply_to.
The one place that never reads it is the module that owns it. upstream.lua stores conn_pid and no method uses it.
I started a patch to send there as well and abandoned it: in the plugin path the parent forwards to the user hub, and the hub fans out to that user’s connections — including the one at conn_pid. Adding a direct send would deliver every update twice to the client that is actually connected. Only the maintainers can say which of these is meant to be the route.
What would settle it
One of:
conn_pid is vestigial — then upstream.new should stop taking it, session.lua should stop refreshing it, and message_handlers should get the connection some other way. Right now three files maintain a field with one indirect reader.
- Forwarding is a parent’s contract — then say so where a parent is spawned, because
plugin.lua is currently the only worked example and it is 649 lines.
- The hub is the real destination — then
upstream could take it directly and the plugin’s forward becomes redundant.
How I got here
Tracing why a Telegram conversation never appeared live in a Kickside UI. The cause turned out to be elsewhere (kickside/sessions 0.1.22 has no subscription at all — no WebSocket, no polling, and onHostVisibilityChanged left as the empty base method), and this deployment mirrors turns into wippy.session rows and pushes the same topics to the user hub by hand, which works.
But it means the natural fix — run the turns inside a real session process — would not have helped, and it took reading plugin.lua to find out why. That seemed worth writing down.
Related: #32.
Not a patch, because the obvious fix double-delivers. Filing the analysis instead.
What happens
src/process/upstream.luadelivers to exactly one recipient:and the only thing in the module that relays onward is
src/process/plugin.lua:559-562:So
received,response_started,content,updateandcommand_responsereach a browser only when the parent happens to be the relay plugin. Spawn a session from anything else — a channel bridge, a job, an automation that wants the conversation visible in the UI — and every upstream update stops at that parent. The session runs correctly, the rows are written, and nothing on screen moves.That is a supported-looking arrangement with an unsupported half:
spawn a session processis a public shape, and only one of its callers gets updates.Why
conn_pidis not the answerconn_pidlooks like the missing route. It is accepted byupstream.new, stored on the instance, and refreshed by the session process on every inbound message (src/process/session.lua:209and:237).src/process/plugin.luathreads it through spawn payloads and command payloads, and errors go straight to it viasend_error.src/process/message_handlers.lua:361-363reaches in for it as the streamingreply_to.The one place that never reads it is the module that owns it.
upstream.luastoresconn_pidand no method uses it.I started a patch to send there as well and abandoned it: in the plugin path the parent forwards to the user hub, and the hub fans out to that user’s connections — including the one at
conn_pid. Adding a direct send would deliver every update twice to the client that is actually connected. Only the maintainers can say which of these is meant to be the route.What would settle it
One of:
conn_pidis vestigial — thenupstream.newshould stop taking it,session.luashould stop refreshing it, andmessage_handlersshould get the connection some other way. Right now three files maintain a field with one indirect reader.plugin.luais currently the only worked example and it is 649 lines.upstreamcould take it directly and the plugin’s forward becomes redundant.How I got here
Tracing why a Telegram conversation never appeared live in a Kickside UI. The cause turned out to be elsewhere (
kickside/sessions0.1.22 has no subscription at all — no WebSocket, no polling, andonHostVisibilityChangedleft as the empty base method), and this deployment mirrors turns intowippy.sessionrows and pushes the same topics to the user hub by hand, which works.But it means the natural fix — run the turns inside a real session process — would not have helped, and it took reading
plugin.luato find out why. That seemed worth writing down.Related: #32.