Skip to content

feat(agent): add Session.Steer to deliver mid-run user messages - #6306

Open
Jaisev-Sachdev wants to merge 1 commit into
multica-ai:mainfrom
Jaisev-Sachdev:feat/agent-session-steer
Open

feat(agent): add Session.Steer to deliver mid-run user messages#6306
Jaisev-Sachdev wants to merge 1 commit into
multica-ai:mainfrom
Jaisev-Sachdev:feat/agent-session-steer

Conversation

@Jaisev-Sachdev

@Jaisev-Sachdev Jaisev-Sachdev commented Aug 3, 2026

Copy link
Copy Markdown

Problem

Multica execution is end-to-end today: once an agent starts a task there is no way to talk to it until the run finishes. A comment posted mid-run is (correctly, and carefully, #5914) recorded as a deferred obligation and replayed as a follow-up task after completion. That guarantees nothing is dropped, but the human is always a full task-cycle behind: you cannot say "stop, the fixture you're editing is generated, change the generator instead" while the agent still has 20 minutes of work ahead.

What this PR adds

The missing capability layer underneath live steering: an optional, additive Steer func(text string) error on agent.Session, implemented by the claude backend.

The claude backend can support this essentially for free: it already runs --input-format stream-json and deliberately keeps stdin open for the whole run (for control_response frames). Steer writes one more user frame to that same pipe; the CLI queues it as the next user turn, so the in-flight turn is never corrupted.

Design points

  • Additive, nil-able field — not an interface change. The other 16 backends compile unchanged and advertise no support. Callers must treat Steer == nil as "unsupported → use the existing follow-up-task path".
  • A steer frame can never overtake the initial prompt (gated on the prompt write completing). Steering a finished or cancelled run returns an error instead of silently dropping the message — in every failure shape the caller still holds the message and can fall back to the existing deferral machinery, so exactly-once is preserved by construction at this layer.
  • All stdin frame producers now share one locked writer (initial prompt, control responses, Steer), making each frame write atomic on the pipe. This also hardens a pre-existing latent interleaving between the prompt-writer goroutine and the scanner's control responses, which previously wrote to stdin from different goroutines with no synchronisation.

Tests

  • Fake-CLI round trip (same CLAUDE_FAKE_MODE re-exec pattern as the deadlock tests): the steer text must arrive as a well-formed user frame on the same stdin, after the prompt, while the run is live — the fake blocks on the second frame, so non-delivery hangs the test rather than passing.
  • Steer-after-completion and steer-after-cancel both error.
  • go vet clean, full ./pkg/agent suite green, whole server tree builds.

What this PR deliberately does NOT do

No server-side wiring. Routing a mid-run comment to a live session instead of the deferred replay touches the #5914 invariants (completion reconcile, planned-comment registration, head-scoped merges), and that deserves its own design review rather than riding along here. Proposed direction, for context: live delivery as an optimization of the existing deferred outcome — the daemon claims a deferred comment for live delivery, the claim marks it covered so completion reconcile skips it, and anything unclaimed (unsupported backend, race with completion, delivery failure) flows through the existing replay path unchanged. Happy to write that up as an RFC issue if there's interest.

Why from us

We run self-hosted Multica driving Claude Code runtimes on GKE; "you can't chat with an agent while it works" is the top piece of feedback from our engineers. This is the smallest reviewable step toward fixing it.

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

@Jaisev-Sachdev is attempting to deploy a commit to the IndexLabs Team on Vercel.

A member of the Team first needs to authorize it.

Multica tasks are fire-and-forget today: once an agent starts on an issue,
there is no way to talk to it until the run ends. A comment posted mid-run
is (correctly, and with care — multica-ai#5914) deferred and replayed as a follow-up
task after completion. This adds the missing capability layer underneath a
future live-delivery path: an optional, additive Steer hook on
agent.Session that injects an additional user message into the RUNNING
session.

The claude backend can support this for free: it already runs
--input-format stream-json and deliberately keeps stdin open for the whole
run (for control_response frames). Steer writes one more user frame to
that same pipe; the CLI queues it as the next user turn.

Design points:

- Steer is a nil-able func field on Session, not a new interface method,
  so the other 16 backends compile unchanged and callers must treat nil
  as "unsupported → use the existing follow-up-task path".
- A steer frame can never overtake the initial prompt (gated on the
  prompt write completing), and a steer after the run finishes or is
  cancelled returns an error instead of silently dropping the message —
  the caller keeps the deferral fallback in both cases.
- All stdin frame producers (initial prompt, control responses, Steer)
  now share one locked writer, making each frame write atomic. This also
  hardens a pre-existing latent interleaving between the prompt-writer
  goroutine and the scanner's control responses.

Tested with a fake-CLI round trip: the steer text must arrive as a
well-formed user frame on the same stdin, after the prompt, while the run
is live (the fake blocks on the second frame, so non-delivery hangs the
test rather than passing); plus steer-after-completion and
steer-after-cancel error paths.

Server-side wiring (routing a mid-run comment to a live session instead of
the deferred replay, claim-based so completion reconcile never
double-delivers) is deliberately NOT in this PR — proposed separately so
the multica-ai#5914 invariants get their own review.
@Jaisev-Sachdev
Jaisev-Sachdev force-pushed the feat/agent-session-steer branch from 473ae26 to f07b6da Compare August 3, 2026 08:06
@Jaisev-Sachdev Jaisev-Sachdev changed the title feat(agent): add Session.Steer — deliver a user message into a live run feat(agent): add Session.Steer to deliver mid-run user messages Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant