Skip to main content

v0.5.0

Long commands stop holding the turn. A shell call that is still running after 20 seconds carries on as a background job, /jobs lists the jobs live — their pages, their clocks, a Kill tab — and the status row shows what is running. A call has no deadline unless it sets one. The terminal side is iota's own now: a resize while a reply streams loses no line, large updates appear in one piece, and the banner is a card.

Added​

  • /jobs. While a background job runs, /jobs is a command: it opens a list with one row per job — the id, how long it has run, the command — and Enter on a row opens the job's page: the command in full, the clock with the time it started, the pid, the output file, and the file's last twenty lines. Esc on the page goes back to the list, Esc on the list closes it. The row leaves the completion list with the last job, so the command exists exactly when there is something to show (X-50).
  • The status row shows the running jobs. One job reads · job b3 cargo test 1m12s at the end of the row, the command in the same cyan as the call's header and the seconds walking; several read · 3 jobs 3m01s, the oldest's clock. The segment is gone once the last job's notice has landed, and an iota with no job repaints nothing (X-50).
  • /jobs is live. The list and the page re-read the jobs once a second: the clocks walk, a job that ends leaves the list and one that starts joins it, the cursor stays on the job it was on, and a page open on a job that ends says so where the clock was — finished: exit 0 after 1m 12s, finished: killed, finished: timed out after … — with the rest of the page as it was (X-50).
  • A Kill tab in /jobs. Tab to Kill, Space to check jobs, Enter to kill them: the surface closes and each job's notice, [background job b3 finished: killed] …, says what happened, as it does for any job that ends. There is no kill key on a job's page — letters on the list are its search (X-50).

Changed​

  • After you make the window narrower, the last few lines of a streaming reply can appear twice (measured: 3 lines in tmux and 2 in herdr and Ghostty when a drag narrows the window a column at a time; 3 in tmux and 4 in herdr and Ghostty when the width drops by half or more). iota no longer guesses how your terminal rewrapped those lines, so it no longer erases lines on that guess (X-52).
  • A running background job keeps the host at working. While a job runs and the chat is otherwise idle, herdr shows the pane working, the terminal's progress indicator stays on and cmux reads Running, until the job's notice has had its turn: the job's result still comes back as a reply, so the chat is not done before it — herdr agent prompt --wait now waits for the job too. An approval prompt or a failed turn shows as before (X-47).
  • A shell command has no deadline unless the call sets one. A call without timeout runs until the command exits — or iota exits, which kills every job — instead of being killed after 600 seconds; a call with timeout is killed after that many seconds, any number, where 3600 used to be the ceiling. Once a long command runs on as a background job rather than holding the turn, a default could only cut off a server, a watcher or a child agent at an arbitrary minute (X-06).
  • A command is cut the same way wherever it is named. The finished-job notice, the /jobs row and the status row's job segment show a command as the call's [shell …] header did: the first line, … when there are more, no more than 64 characters. The notice used to paste the whole command, a multi-line script included. The full text is on the job's page in /jobs (X-50).
  • A shell call lets go after 20 seconds. A command still running then carries on as a background job, and the call answers with what it printed so far — Still running after 20s as background job b3 … — so the model never has to guess in advance which commands will take long; the notice with the exit status and the output arrives when the job ends, as it always did for background: true. That switch is for what should not be waited on at all — a server, a watcher, a child agent. In the chat, a lone call shows ⎿ still running after 20s → background job b3 over the output so far, and a group of calls says · job b3 running. With 16 jobs already running the call is waited for as before and says so (X-50).
  • The banner is a card. The three facts — ι> iota and the version, the mode row, the directory — stand inside a rounded frame that hugs the widest of them; the half-block wordmark is gone. The dots between the mode row's segments are dim like the frame, so the facts stand out. A terminal too narrow for the mode row and the frame (38 columns in a plain terminal, more when the row names a host) gets the three rows bare; NO_COLOR keeps the frame and loses the colors (X-46).

Fixed​

  • \| keeps a pipe inside a table cell. A | written as \| no longer splits the cell — in a code span in the cell and in the header row too — the same way GitHub and /export read it. And backslash escapes work everywhere else: \*not italic\*, \`not code\`, \[not a link\], \\ show the character instead of a stray backslash and an unwanted style (X-56).
  • A line of output is no longer cut short when it arrives during a resize. A line laid out for the wider window just before the window was made narrower lost its end when it was printed; it now wraps like any other line.
  • Large screen updates appear all at once. When iota draws a lot at once — opening a picker, a big block of output — terminals that support synchronized output (Ghostty, kitty, WezTerm, Alacritty, iTerm2, Windows Terminal, herdr, tmux 3.7 and newer) show it in one piece instead of half-drawn. Small updates, like typing or a streaming line, are sent as before, so tmux 3.7 over a slow connection is not slowed down (X-55).
  • A resize while a reply streams no longer erases lines iota cannot prove are its own; a narrowing could still cut the reply's last lines until 0.5.1. Dragging a tmux window's or pane's height while a reply was streaming lost a few of its lines every time; so did a height drag in a terminal that never answers iota's cursor query, and, rarely, one in tmux under load. iota now draws the whole prompt area counting from the cursor rather than from row numbers, so rows the terminal moves in the middle of a resize move with it (X-54).
  • Opening or closing a picker no longer blinks the prompt. A change in the prompt area's height used to erase it and draw it again; now only the rows that change are written (X-54).
  • Dragging a window's or a tmux pane's height down and back no longer loses a line. In tmux, growing the height again within the same drag pulls rows back out of the scrollback; iota could erase the conversation's last line above the prompt while it caught up. The resize now reads the terminal's size at that moment and confirms where the prompt is before it erases anything (X-52).
  • A terminal that doesn't answer iota's cursor query no longer loses a line on a resize. Over a slow ssh link, or in a terminal that ignores the query, changing the window's height could erase the conversation's last line; iota now erases nothing above the prompt. Resizing in tmux also no longer erases a line that tmux moves back onto the screen in the middle of a resize (X-52, X-54).
  • Resizing the window no longer throws the prompt to the top or erases text. Making the window narrower moved the prompt and its separators to the top of the screen and wiped everything that was on screen above them; in a terminal that keeps cleared screens (tmux) each step of a drag left another copy of the separators in the scrollback instead. iota now handles the resize itself: the prompt stays at the bottom through any drag and the transcript above it is no longer wiped — also with a picker such as /model open, while a tool runs, and when a maximized window is restored to half its width. While you drag the window's edge, the prompt's lines stop a few columns short of the right edge (twice the widest step of the drag, at most 8) so the drag does not break them; two seconds after the last resize — or at once when you type — they span the full width again. A narrowing can still leave the last lines of a streaming reply repeated in the scrollback — the measured counts are under Changed (X-52). The staged last lines of a reply stay where they are, rewrapped for the new width, instead of being printed again. A row that ends in a wide character — CJK, an emoji — is measured to that character's right edge, so deleting one at the end of the prompt never erases half of the one before it (X-52).
  • Narrowing the window with a wide line still staged no longer stops the interface in a debug build; the line is rewrapped for the new width.
  • The status row keeps its colours when it is cut. A row wider than the terminal — the token figures grown a few digits, a long model name, a job segment — used to fall back to one faint plain line; it is now cut with its hues and its closing reset intact (X-51).
  • The banner no longer prints twice on exit. Leaving the chat right after it started — Ctrl+C or Ctrl+D at the prompt — left the banner on the screen twice: once in the scrollback, once more in the frame that had shown it. The exit now repaints the frame after the staged rows have gone to scrollback, so what stays on the screen is the transcript once, then the prompt and the status row. After a longer chat the last rows of the reply were the ones doubled; those are once now too (X-49).