Skip to content

feat: long-lived streaming sessions (#21)#22

Open
jacksoncage wants to merge 12 commits intomainfrom
feat/long-lived-sessions
Open

feat: long-lived streaming sessions (#21)#22
jacksoncage wants to merge 12 commits intomainfrom
feat/long-lived-sessions

Conversation

@jacksoncage
Copy link
Owner

Summary

  • Replace fire-and-forget task execution with bidirectional streaming sessions
  • Enable AskUserQuestion relay — Claude can ask users questions during execution, Ove relays via chat
  • Add autonomous CI retry loop for cron tasks — check CI after push, resume session if failed, max 3 retries
  • New waiting_user task status for tasks blocked on user input
  • Session tracking via SessionManager — routes user replies back to waiting Claude sessions
  • Store Claude session IDs for --resume support

Changes

File What
src/runner.ts Added StreamEvent, StreamingSession types, resumeSessionId to RunOptions, sessionId to RunResult
src/runners/claude.ts Added buildStreamingArgs() (enables AskUserQuestion, adds --input-format stream-json), runStreaming() with bidirectional stdin/stdout
src/queue.ts Added waiting_user status, setWaiting(), resume(), getWaitingForUser(), setSessionId(), sessionId column
src/session-manager.ts New — tracks live streaming sessions, routes messages to waiting sessions
src/worker.ts Streaming path for interactive tasks, checkRecentPR() helper, autonomous CI retry loop for cron tasks
src/handlers.ts Reply routing — user messages go to waiting sessions instead of creating new tasks
src/index.ts Wired SessionManager into handler deps, worker deps, and shutdown

Test plan

  • Queue: waiting_user transitions, resume, dequeue blocking, cancel, resetStale, stats (9 tests)
  • Queue: sessionId tracking (2 tests)
  • SessionManager: register, unregister, waiting state, sendToTask, killAll (9 tests)
  • ClaudeRunner: streaming args (no AskUserQuestion disallow, input-format), resume flag, regular args unchanged (12 tests)
  • Handlers: reply routing to waiting sessions, queue resume, history preservation, fallthrough (4 tests)
  • Full suite: 405 tests passing, 0 failures

Closes #21

🤖 Generated with Claude Code

jacksoncage and others added 11 commits March 2, 2026 22:52
Add setWaiting, resume, and getWaitingForUser methods to TaskQueue.
Update dequeue, listActive, cancel, and resetStale queries to handle
the new waiting_user status. This is the foundation for long-lived
streaming sessions where tasks can pause waiting for user input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add StreamEvent, StreamingSession types to runner.ts. Add resumeSessionId
to RunOptions and sessionId to RunResult. Add buildStreamingArgs method to
ClaudeRunner (with --input-format stream-json, without --disallowed-tools
AskUserQuestion). Update buildArgs and run() to support session resume and
capture session_id from result messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add runStreaming() method to ClaudeRunner that spawns Claude with stdin pipe
for bidirectional communication. Parses stream-json events, detects
AskUserQuestion tool_use blocks to emit ask_user StreamEvents, captures
session_id from system/result messages, and returns a StreamingSession
handle with sendMessage(), kill(), sessionId, and done promise.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a user has a streaming session waiting for input (waiting_user
status), intercept their next message before any parsing and route it
directly to the session's stdin. This enables the conversational flow
where Claude asks a follow-up question and the user replies naturally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After a cron task completes successfully, check if it created a PR and
whether CI passed. If CI failed, resume the session and retry up to 3
times, waiting for CI between attempts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reconcile PR #20 (parallel tasks) with waiting_user blocking.
Only block dequeuing from repos that have a waiting_user task
(live session waiting for input), not from repos with running tasks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jacksoncage jacksoncage force-pushed the feat/long-lived-sessions branch from d3195ca to d08f8da Compare March 2, 2026 21:54
- Show waiting_user tasks in handleListTasks display
- Add updateResult() to queue for CI retry updates (avoids overwriting
  completed_at on already-completed tasks)
- Wrap sendMessage stdin write in try/catch for closed pipe safety
- Simplify setWaiting() signature (remove redundant status param)
- Add waiting_user to repoBreakdown in metrics()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

Long-lived streaming sessions — take tasks all the way to goal

1 participant