Repository navigation
feat(opencode): add STEER support for busy TUI sessions - #14815
dhiyaancnirmal wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request adds STEER (Steering) support for busy TUI sessions in opencode, allowing users to provide guidance to the AI agent during execution without aborting the in-flight processing. This is an experimental feature similar to what's available in Codex, addressing issue #9454 which requested better compatibility with Codex features.
Changes:
- Added
/session/{sessionID}/steerAPI endpoint and SDK generation for queuing user guidance messages - Implemented TUI keyboard shortcuts (
input_steer_submitwith defaultalt+return,super+return,ctrl+return) for submitting steer messages while the session is busy - Added immediate persistence and visibility of steer messages in the transcript and TUI with a "STEER" badge
- Implemented opt-in tmux-based E2E test harness for TUI testing
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
packages/sdk/js/src/v2/gen/types.gen.ts |
Generated TypeScript types for the new session.steer API endpoint including request/response types |
packages/sdk/js/src/v2/gen/sdk.gen.ts |
Generated SDK client method for the steer operation with parameter building and error handling |
packages/opencode/test/session/prompt.test.ts |
Comprehensive test coverage for steer functionality including idle fallback, busy queue draining, persistence, and edge cases |
packages/opencode/test/config/config.test.ts |
Test verifying default input_steer_submit keybinding configuration |
packages/opencode/test/cli/tui/transcript.test.ts |
Tests for transcript formatting of steer messages with markdown preservation |
packages/opencode/test/cli/tui/tmux.e2e.test.ts |
End-to-end tests using tmux harness for TUI rendering and session routing |
packages/opencode/test/cli/tui/tmux-harness.ts |
New tmux-based E2E test harness for automated TUI testing |
packages/opencode/test/cli/tui/prompt-submit-routing.test.ts |
Tests for prompt submit routing logic including steer key detection and request success validation |
packages/opencode/src/session/prompt.ts |
Core steer implementation: queue management, persistence via writeSteer, and integration with session loop for draining steer messages |
packages/opencode/src/server/routes/session.ts |
Server route definition for POST /session/{sessionID}/steer with validation and 202 response |
packages/opencode/src/config/config.ts |
Added input_steer_submit keybinding configuration with default shortcuts |
packages/opencode/src/cli/cmd/tui/util/transcript.ts |
Transcript formatting support for steer messages with "Steer:" prefix |
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx |
UI changes to display steer messages with "STEER" badge and treat them as visible text in user messages |
packages/opencode/src/cli/cmd/tui/component/prompt/submit-routing.ts |
New routing logic to handle steer vs normal prompt submission based on session state and intent |
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx |
Integrated steer submission: keyboard handling, API calls, fallback to normal prompt when idle, and UI hints |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| expect(parts.length).toBe(3) | ||
| const timestamps = parts.map((part) => part.metadata?.steer_timestamp) | ||
| expect(timestamps.every((x) => typeof x === "number")).toBe(true) | ||
| expect(parts.map((part) => part.text).toSorted()).toEqual(["first", "second", "third"]) |
There was a problem hiding this comment.
The test name suggests that steer messages should be "drained in timestamp order", but the test only verifies that all three messages exist using .toSorted() which sorts the array for comparison. The test doesn't actually verify that the messages are stored or retrieved in timestamp order. Consider either:
- Renaming the test to reflect that it only checks for existence of all messages
- Adding assertions that verify the actual order matches the timestamp order (e.g., checking that the steer_timestamp values in the parts array are in ascending order)
| expect(parts.map((part) => part.text).toSorted()).toEqual(["first", "second", "third"]) | |
| // verify that steer messages are drained in timestamp order | |
| const texts = parts.map((part) => part.text) | |
| expect(texts).toEqual(["first", "second", "third"]) | |
| const sortedTimestamps = [...(timestamps as number[])].toSorted((a, b) => a - b) | |
| expect(timestamps).toEqual(sortedTimestamps) |
| async function drainSteerQueue(input: { | ||
| sessionID: string | ||
| user: MessageV2.User | ||
| }) { | ||
| const match = state()[input.sessionID] | ||
| if (!match || match.steerQueue.length === 0) return false | ||
| return match.steerQueue.splice(0).length > 0 | ||
| } |
There was a problem hiding this comment.
The drainSteerQueue function empties the steer queue using splice(0) but doesn't utilize the queued items. While the steer messages are already persisted to the database via writeSteer, the queue contains timestamp information that could be useful for ordering. Currently, there's no guarantee that steer messages will be processed in timestamp order if multiple steer calls happen rapidly. Consider sorting the queue by timestamp before draining, or document why timestamp ordering is not necessary.
| parameters: { | ||
| sessionID: string | ||
| directory?: string | ||
| text?: string |
There was a problem hiding this comment.
The text parameter is marked as optional in the TypeScript signature (text?: string) but the server-side validation requires it to be a non-empty string (z.string().trim().min(1)). This inconsistency could lead to runtime validation errors when calling the API without providing the text parameter. The SDK should mark this parameter as required to match the server validation: change text?: string to text: string.
| text?: string | |
| text: string |
Issue for this PR
Closes #9454
Type of change
What does this PR do?
Adds STEER support for busy sessions in
opencode(TUI + API path).Included in this PR:
session.steerserver route / SDK generation support used by the TUIinput_steer_submit)tmuxTUI E2E harness + basic coverageHow did you verify your code works?
cd packages/opencode && ~/.bun/bin/bun test --timeout 120000 test/cli/tui/prompt-submit-routing.test.ts test/cli/tui/transcript.test.ts test/session/prompt.test.ts test/config/config.test.ts test/cli/tui/tmux.e2e.test.tscd packages/opencode && OPENCODE_TUI_E2E=1 ~/.bun/bin/bun test --timeout 60000 test/cli/tui/tmux.e2e.test.tsPATH="$HOME/.bun/bin:$PATH" ~/.bun/bin/bun turbo typecheckScreenshots / recordings
Not included (TUI keyboard/timing behavior change).
Checklist
If you do not follow this template your PR will be automatically rejected.