Input Queuing¶
When the agent is actively streaming a response, Keen Code queues certain inputs instead of silently dropping them. Queued messages are displayed in a preview band below the spinner and are automatically submitted one at a time as each turn completes.
When Queuing Applies¶
Queuing is active when the agent is busy — either a main stream is in progress (streamHandler.IsActive()) or compaction is running (isCompacting).
What Gets Queued¶
| Input | Queued? | Reason |
|---|---|---|
| Normal text prompts | Yes | Conversational turns, naturally sequential |
/<skill-name> [args] (enabled skill) |
Yes | Transforms to an activation message, runs as a normal LLM turn |
/adversary [prompt] |
Yes | Runs as a separate stream after the current turn finishes |
/btw <question> |
No — executes immediately | Side-channel stream, works alongside the main stream |
/emptyq |
No — executes immediately | Clears the queue (see below) |
/clear, /model, /compact, /help, etc. |
No | Immediate side-effects on REPL state; deferring is semantically wrong |
/nonexistent (unknown skill/command) |
No | Not a recognized skill or command |
!shell commands |
No | Shell execution, immediate side-effects |
The queue is capped at 5 items. When the queue is full, additional queueable inputs are rejected with a "Queue is full" notification.
Queue Preview¶
Queued messages are rendered between the spinner and the input area:
⠋ working...
▎ queue fix the off-by-one in parser
▎ queue also add a test for it
┌─ ▎build ────────────────────────────┐
│ > | │
└──────────────────────────────────────┘
- Each item shows a
queuechip with a dimmed message body - Multi-line messages display only the first line in the preview
- Long messages are truncated with an ellipsis to fit the terminal width
- The textarea remains free for new input while items are queued
Draining the Queue¶
Queued items are submitted one per turn — not all at once. After the current stream completes successfully, the first queued item is popped and submitted through the normal input path. Remaining items stay in the queue for subsequent turns.
What triggers a drain¶
| Event | Drains queue? |
|---|---|
Stream completed successfully (handleLLMDone) |
Yes |
Stream ended with incomplete response (handleLLMIncomplete) |
Yes |
Compaction completed (handleCompactionDone) |
Yes |
Compaction failed (handleCompactionError) |
Yes |
Stream canceled via Ctrl+C/Esc (handleLLMError, context.Canceled) |
No |
Stream failed with an error (handleLLMError, non-cancel) |
No |
Errors and cancellations do not drain the queue. This is intentional — if the LLM is having problems, sending another queued message immediately could compound the issue. The user can inspect the error, then manually submit the next queued item by pressing Enter, or clear the queue.
Clearing the Queue¶
There are two ways to clear the queue:
/emptyq command¶
Works at any time, including while the agent is actively streaming. Clears all queued items and shows a "Queue cleared" notification. If the queue is already empty, shows "Queue is empty" instead.
Ctrl+C / Esc while idle¶
When the agent is NOT actively streaming and the queue has items, pressing Ctrl+C or Esc clears the queue and shows a "Queue cleared" notification. If the agent IS streaming, Ctrl+C/Esc interrupts the stream but preserves the queue — the queued items will be available after the interruption resolves.
Notifications¶
Notifications appear beside the loading text (or below the textarea when idle) in accent style for 2 seconds:
| Trigger | Message |
|---|---|
Known slash command while busy (e.g. /clear) |
Operation not permitted |
Unknown /-prefixed input while busy |
No such skill found |
!shell while busy |
Operation not permitted |
/adversary while busy (queue full) |
Queue is full |
| Queue full | Queue is full |
Queue cleared via /emptyq or Ctrl+C/Esc |
Queue cleared |
/emptyq with empty queue |
Queue is empty |
Implementation¶
Queue logic lives in internal/cli/repl/repl_queue.go:
isQueueable(input)— determines if an input can be queueddrainQueuedInput()— pops the first item and submits itqueuedHeight()— computes viewport height adjustment for the preview bandrenderQueuedInputs()— renders the preview band
The queuedInputs []string field on replModel holds the queue state. The maxQueuedInputs constant (currently 5) sets the cap.
Queue-related styles (QueueItemStyle, QueueChipStyle) are defined in internal/cli/repl/theme/styles.go.