feat(workspace): mid-turn abort + steering for foreman chat #564

Closed
opened 2026-04-30 16:52:49 +00:00 by claude-desktop · 1 comment
Collaborator

User story

As an operator, I want to interrupt a runaway foreman turn or inject a follow-up while it's still streaming, so I can correct course without waiting for the turn to finish or eating Pro-Max quota on a wrong direction.

Acceptance criteria

Abort

  • Esc while a foreman turn is streaming aborts it. Composer focus not required — handled at the workspace route level.
  • A "Stop" button sits next to the Send button while streaming is true.
  • Abort flips the worker's currentAbort controller; the SDK iterator tears down and the task lands cancelled in task_history.
  • Server emits a synthetic result event with subtype: "aborted" so the client clears ?task= and refetches the session.
  • The persisted asst text up to the abort point is saved (appendMessage with whatever assistantText accumulated).

Steering

  • While streaming, the composer accepts text and a "Queue" button (replaces "Send" while busy) enqueues it via POST /foreman/steer/:task_id. Server already broadcasts steer_queued / steer_delivered events.
  • Queued steer messages render as a pending pill above the streaming bubble. Pill flips to "delivered" when the SDK consumes the message.
  • Multiple steers queue in order; server delivers FIFO.

Out of scope

  • Pause / resume (would need SDK pause hook). Abort + restart only.
  • Steering arbitrary tools (text-only first).

References

  • Server abort plumbing: apps/server/src/background/worker.ts:412 (currentAbort).
  • Server steer events already wired: apps/server/src/domain/dispatch/registry.ts:294 (onSteerQueued).
  • Spec: specs/workspace-chat-overhaul.md § P1.
## User story As an operator, I want to interrupt a runaway foreman turn or inject a follow-up while it's still streaming, so I can correct course without waiting for the turn to finish or eating Pro-Max quota on a wrong direction. ## Acceptance criteria ### Abort - [ ] `Esc` while a foreman turn is streaming aborts it. Composer focus not required — handled at the workspace route level. - [ ] A "Stop" button sits next to the Send button while `streaming` is true. - [ ] Abort flips the worker's `currentAbort` controller; the SDK iterator tears down and the task lands `cancelled` in `task_history`. - [ ] Server emits a synthetic `result` event with `subtype: "aborted"` so the client clears `?task=` and refetches the session. - [ ] The persisted asst text up to the abort point is saved (`appendMessage` with whatever `assistantText` accumulated). ### Steering - [ ] While streaming, the composer accepts text and a "Queue" button (replaces "Send" while busy) enqueues it via `POST /foreman/steer/:task_id`. Server already broadcasts `steer_queued` / `steer_delivered` events. - [ ] Queued steer messages render as a pending pill above the streaming bubble. Pill flips to "delivered" when the SDK consumes the message. - [ ] Multiple steers queue in order; server delivers FIFO. ## Out of scope - Pause / resume (would need SDK pause hook). Abort + restart only. - Steering arbitrary tools (text-only first). ## References - Server abort plumbing: `apps/server/src/background/worker.ts:412` (`currentAbort`). - Server steer events already wired: `apps/server/src/domain/dispatch/registry.ts:294` (`onSteerQueued`). - Spec: `specs/workspace-chat-overhaul.md` § P1.
Collaborator

🤖 Auto-assigned to dev (heuristic: area:dashboard + body 1681 bytes (≤ 2 KB) — dev). Reply /unassign to reroute.

🤖 Auto-assigned to **dev** (heuristic: area:dashboard + body 1681 bytes (≤ 2 KB) — dev). Reply `/unassign` to reroute.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#564
No description provided.