Skip to content

Turn-by-turn conversational agent sessions in Slack (and later Telegram), without live session injection #130

Description

@khaliqgant

Summary

Factory's human-loop today is one-shot: post a dispatch thread (src/writeback/slack.ts, MountSlackWriteback.postThread/reply), and separately handle a single clarification round-trip per thin/blocked triage (human-durable-clarification-wake, human-slack-reply-watch, human-clarified-redispatch in .agentworkforce/features/manifest.yaml). There's no way for a human to just talk to the agent back and forth in the thread the way Amika's Slack integration does (@amika mention spins up a sandbox mapped 1:1 to the thread; the session continues across replies). This issue is that capability, generalized to Slack now with Telegram as a documented follow-on.

Important constraint this must respect

FleetClient (src/ports/fleet.ts) still exposes waitForInjected/sendInput for live delivery into a running session, but issue #105 is an explicit, in-progress effort to make that structurally impossible — the stated invariant is "an agent receives input only at spawn or via resume; live injection into a running session is prohibited," with 3 remaining call sites (the babysitter wake path) being migrated to coalesced resume. A turn-by-turn chat feature must be built on the post-#105 model from day one — resume-with-fresh-task, not injection — or it becomes the reason that invariant never lands. Treat this as a hard design constraint, not a nice-to-have.

Proposed approach

Model this exactly like the babysitter's durable, coalesced wake (pr-durable-babysitter-sessions, pr-babysitter-wake-coalescing in the manifest) rather than inventing a new mechanism.

A. Thread-to-session mapping

One Slack thread maps to one agent session, keyed the same way BabysitterSessionState keys PR ownership (src/ports/state.ts, src/state/file-state-store.ts) — persist thread→session ownership so a restart doesn't lose the mapping, mirroring pr-durable-babysitter-sessions.

B. Message delivery via resume, not injection

A new reply in the thread should not call sendMessage/sendInput into a live process. It should: coalesce rapid-fire replies within a short window (reuse the coalescing logic behind pr-babysitter-wake-coalescing rather than reinventing it), then deliver via FleetClient.resume({ sessionRef, task: <folded-in message + prior context> }) — exactly the pattern human-clarified-redispatch already uses for the single-clarification case. This is the same resume-with-fresh-task shape #105 is converging the babysitter onto; this feature should land after or alongside #105's migration, reusing its coalescing primitive rather than building a second one.

C. Slack-side subscription

Extend the Slack subscription surface (src/subscriptions/specs.ts and the Slack-filtering equivalent of linear-filter.ts) to watch arbitrary thread replies, not just the single correlated clarification reply human-slack-reply-watch currently expects — this is a broadening of an existing mechanism, not new plumbing.

D. Telegram (follow-up, not this issue's critical path)

Telegram has none of Slack's existing surface — no mount/writeback module (src/writeback/slack.ts has no Telegram counterpart), no subscription globs, no channel-allow-list concept (assertSlackChannelAllowed in slack.ts is Slack-specific). Land Slack first; scope Telegram as a second, smaller issue once the resume-based session-continuity core (A/B) is proven — only the provider-specific mount/writeback/subscription layer needs to be rebuilt per channel. The session-continuity mechanism itself should not need touching twice, which is the whole point of building it on the generic FleetClient.resume() contract rather than Slack-specific code.

Acceptance criteria

Notes for the implementer

Blocked by: #105 (the no-live-injection invariant this must be built on top of, not around)

Metadata

Metadata

Assignees

No one assigned

    Labels

    factory-readyApproved for the Factory to pick up and implementfactory:in-progressFactory agents are working on this issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions