Terminal (PTY)
A real shell — not a fake REPL — running in your workspace, with full ANSI, multi-tab persistence, and Ctrl+C that actually delivers SIGINT.
What it is
The integrated terminal is a server-side PTY (pseudo-terminal). You type, your bytes get fed to a real bash. Programs see a terminal. top works. vim works. python -i works.
Default-on as of build 2026-04-25
Earlier builds shipped a JavaScript fake-shell as default and required opting into the real PTY. Since ticket 3.2-G, the real PTY is the default.
| State | What runs |
|---|---|
localStorage["kf.term.pty"] unset | Real PTY (default) |
localStorage["kf.term.pty"] = "1" | Real PTY (forced) |
localStorage["kf.term.pty"] = "0" | Legacy fake shell |
?ptyterm=0 URL param | Legacy fake shell (per-tab) |
?ptyterm=1 URL param | Real PTY (per-tab) |
To check which is running, in the browser console:
KFTermPTY.flag
// → { key: "kf.term.pty", on: true }
Multi-tab + persistence
- The
+button on the terminal panel header opens a new tab. - Each tab is a fresh PTY session with its own working directory.
- Reload the page → both tabs re-attach to the same PIDs. Your
cdis preserved. Background processes keep running. - Close a tab with the
×button — the underlying PTY is killed.
Session ids are persisted to localStorage["kf.term.pty.sids"].
Agent take-over
When the agent runs a command on your behalf, you'll see an "agent typing" indicator on the active tab. The agent writes through the same PTY as you — you see the output stream identically.
Font size
localStorage.setItem("kf.term.fontSize", "15");
location.reload();
Range: 9-28. Default 13.
What it can't do
- Doesn't run as root. All PTYs run as the workspace's www-data-equivalent user.
- Idle timeout. Sessions with no I/O for > the configured TTL are reaped. Long-running daemons survive; idle shells don't.
- No shared clipboard with your OS by default — use the browser's right-click → paste, or
Ctrl+Shift+V(depends on your terminal emulator behaviour).
Going back to the legacy shell
The fake JavaScript shell is still there as a fallback. To switch:
localStorage.setItem("kf.term.pty", "0");
location.reload();
To switch back:
localStorage.removeItem("kf.term.pty");
location.reload();