What's new in LiveShortly
live v2.3.3 ·
Upgrade with live update or brew upgrade liveshortly/tap/live.
New
- Any agent that speaks the Agent Client Protocol gets full capture (LSH-13).
LIVE_ACP=1 live <agent> <its ACP args>(for examplegoose acporgemini --experimental-acp) drives the agent over ACP instead of recording its terminal: replies, tool steps and file edits with diffs stream as structured events, permission requests are answered from the web and fail closed, and viewer comments are sent as the next turn. For web-started sessions, add"acp": trueto the agent's entry in agents.json. This works for agents live has no adapter for, including ones released later.
New
- Any agent that speaks the Agent Client Protocol gets full capture (LSH-13).
LIVE_ACP=1 live <agent> <its ACP args>(for examplegoose acporgemini --experimental-acp) drives the agent over ACP instead of recording its terminal: replies, tool steps and file edits with diffs stream as structured events, permission requests are answered from the web and fail closed, and viewer comments are sent as the next turn. For web-started sessions, add"acp": trueto the agent's entry in agents.json. This works for agents live has no adapter for, including ones released later.
- Any agent that speaks the Agent Client Protocol gets full capture (LSH-13).
New
- Gemini CLI runs on its hooks, not a raw terminal (LSH-04).
live gemini(Gemini CLI 0.26+) now streams prompts, replies, tool steps, file edits with diffs, permission pings, the model and turn ends as structured events, likelive claude. Viewer comments reach gemini at tool and turn boundaries (a comment that arrives after the last tool re-opens the turn once). With permission control on, gemini runs with--approval-mode=yoloand live's BeforeTool hook is the gate: the owner approves from the web or the tmux popup, "always allow" is remembered, and the gate fails closed like LSH-07. Read-only tools (read, grep, glob, search…) aren't gated; MCP and unknown tools are. A web-started gemini is gated and drivable the same way.- Hooks ship as a
liveshortlyextension in~/.gemini/extensions(Gemini has no per-run settings flag, and it ignores a system-settings override outside a root-owned directory). It is inert outside live: each hook runstrueunlesslivesetLIVE_BIN.live uninstallremoves it. - Live only takes over approvals when its hook is certain to fire. If hooks
or extensions are disabled in any gemini settings scope, an admin
extension allowlist is set, the extension is disabled, a settings file
can't be parsed, or the run is under GitHub Actions (hook env stripped),
gemini's own prompt stays in charge and live says why. Passing
--yolo/--approval-modeyourself also leaves approvals to gemini. - Known gaps: in an untrusted folder gemini refuses yolo, so each gated
tool is approved twice (web, then gemini's own prompt). With
gemini's env redaction on,
LIVE_TOKENis stripped from hooks, so capture is off and gated tools are denied. Comments wait for the next boundary while gemini is idle (no channel like Claude's). No model switch. Gemini older than 0.26 keeps raw terminal capture. testdata/gemini/0.65.0/hooks.jsonlis a golden replay built from gemini's documented hook payloads (synthetic, seetestdata/README.md); the old PTY fixture moved totestdata/gemini-pty/0.65.0/.
- Hooks ship as a
- One support matrix (LSH-08). What live supports per agent (capture,
approvals, comments, model switch, handoff, min version, golden replays,
pocket.ai approvals, known gaps) now lives in one table,
internal/harness, which the CLI also reads for its own decisions (display names,/modelswitch, typed comments, pocket.ai approval status).go run ./scripts/harnessmatrixwrites it into the README,docs/HARNESS-MATRIX.mdanddocs/harness-matrix.json(for the web docs and new-session picker);-checkandgo test ./...fail when a copy drifts or a fixture folder is added without regenerating. Stale README lines (codex as "terminal recording", unchecked roadmap items) are gone.
Fixed
- codex app-server sends
initialized(LSH-12).LIVE_CODEX_APPSERVER=1went straight from theinitializeresponse tothread/startwithout theinitializednotification the protocol requires. Both app-server clients (live codexand the pocket.ai codex driver) now share one handshake: initialize → initialized → thread/start, covered by a test. - Terminal agents stream as readable rows (LSH-11). PTY capture (ollama,
aider, hermes, gemini < 0.26, any
live <cmd>) now releases whole lines once output idles for 600 ms and holds the line still being written until its newline, so a slow model no longer produces one feed row per token. A line that keeps streaming is released at a word boundary every 45 s; operator input and agent exit flush everything at once. Cursor motion and erase sequences (CSI D/C/G/K) are now replayed instead of stripped, so ollama's grey "Send a message" placeholder no longer glues onto the typed prompt and a soft-wrapped line no longer repeats the word that moved down ("be be of"). - agy 1.4.9 sessions read correctly (LSH-10). The newer tool names
edit_file,write_fileandread_filenow produce Edit/Write/View rows (with the diff for edits and writes) and approvals name the file, instead of "wants to run edit_file: edit_file". The oldreplace_file_content/write_to_file/view_filenames keep working. Command output no longer starts with agy'sCreated At… / Command output:header (only that exact leading header is removed), and the prompt row appears once per user turn instead of on every model step. - codex ≥ 0.163 sessions show tool and file rows again (LSH-09). Newer
codex runs shell commands and patches inside a code-mode
execcall and records them asitem_completedCommandExecution/FileChangeitems, which the rollout mapper ignored, so viewers saw no commands or edits and a boguswaitrow instead. Those items now becomeShelltool rows andfile_writerows with diffs; thewaithelper is hidden, and older codex (0.142) no longer shows the apply_patch result as a stray output row. - codex app-server (
LIVE_CODEX_APPSERVER=1) reports the model and answers codex in its own schema. The model comes from the thread start. Command and file approvals are answeredaccept/decline(the legacy methods keepapproved/denied), permission requests get a granted-permissions reply,item/tool/requestUserInputgets an emptyanswersmap, MCP elicitations are declined, and unsupported requests get a JSON-RPC error instead of a{"decision":"denied"}codex cannot parse.
Security
- Approvals fail closed (LSH-07). In web-permissions mode the agent's own
prompt is skipped and
live hook pre-tool-useis the only gate, yet it answeredallowwhenever the sidecar was unreachable, timed out, returned a non-200, or sent an unreadable reply, so a crashed sidecar let every gated tool run unreviewed (Claude, codex, agy). The gated hook is now installed aslive hook pre-tool-use --gatedand denies in all of those cases, telling the agent approvals are offline and printing the cause to stderr. Sessions where the owner opted into bypass (--dangerously-skip-permissions,bypass_permissions,LIVE_CODEX_PERMS=0) behave as before. Sessions already running keep their old hook until restarted.
- Gemini CLI runs on its hooks, not a raw terminal (LSH-04).
Fixed
-
A fresh
livereaches liveshortly.com again. v2.3.0 defaulted tohttp://localhost:8080, solive loginon a new install could not reach the hosted service. With no~/.liveshortly/host.jsonand noLIVESHORTLY_API_URL, the CLI now useshttps://liveshortly.com(hosted first, self-host first-class — D131). Resolution order is unchanged: host.json, then the env var, then the hosted default; existing self-host configurations keep working.make use-prodpoints back at liveshortly.com. -
Installer copy no longer says no hosted account is involved; it points at
live loginfor liveshortly.com with a one-line self-host pointer. Installer behaviour (downloads, checksum verification, paths) is unchanged. -
Fixed: a session whose capture process died no longer sits "live" forever. A spawned
live --attachchild runs in its own process group so a session outlives the daemon that started it. The flip side went unhandled: kill that child — reboot,kill -9, OOM — and its tmux pane survives with no sidecar behind it. The hook relay fails silently by design (live hookexits 0 on a connection error, so a dead sidecar can never wedge the agent), so nothing is captured and nobody ever calls stop.live daemonnow sweeps at startup and every 5 minutes: for eachlive-*pane on its tmux socket it probes the sidecar address recorded in that pane's environment, and archives the session when nothing answers. The pane is deliberately left running — that socket also carrieslive clauderuns you started by hand, and killing an agent mid-task to tidy a feed is not a trade the daemon gets to make for you; it stays attachable, it just stops pretending to be captured.
-
Fixed
- A fresh
livereaches liveshortly.com again. v2.3.0 defaulted tohttp://localhost:8080, solive loginon a new install could not reach the hosted service. With no~/.liveshortly/host.jsonand noLIVESHORTLY_API_URL, the CLI now useshttps://liveshortly.com(hosted first, self-host first-class — D131). Resolution order is unchanged: host.json, then the env var, then the hosted default; existing self-host configurations keep working.make use-prodpoints back at liveshortly.com. - Installer copy no longer says no hosted account is involved; it points at
live loginfor liveshortly.com with a one-line self-host pointer. Installer behaviour (downloads, checksum verification, paths) is unchanged.
- A fresh
New
-
live daemonoffers every agent harness installed on the machine, not a fixed list of six. The builtin probe widens to the harnesses worth guessing at (copilot,opencode,crush,goose,amp,qwen,pi,hermesalongside the existing set), and anything else goes in~/.liveshortly/agents.json— a different executable name, an absolute path, or a fixed argv prefix:[{"name": "hermes"}, {"name": "pi", "command": "pi.dev", "args": ["chat"]}]Same schema the Pocket
--agents-fileadapters already used, so one file covers both modes, and--agents-fileis no longer--server-only. An agent live has no native capture for runs through the generic pty path — exactly aslive <that harness>already did when typed by hand. -
Web spawns can pick a claude model alias, and
/modelswitches the model of a running session.
Fixed
agyandantigravitywere probed and registered by the daemon but silently dropped by the server, so they never appeared in the picker.
Security
- The daemon's agent allowlist is now the set it built at startup from this
machine's PATH and the operator's own adapter file, rather than a hardcoded
list — the same shape as the
--dirallowlist. A spawn still carries only a NAME; the argv is still built locally, and a name the daemon did not register is refused.
-
Fixed
- Web-spawned sessions with
bypass_permissionsended the instant they were created:--no-permissionsalso dropped tmux mode, so the headless child ran claude with no terminal and it exited immediately. Headless runs now keep the tmux pane regardless of permission gating, and the claude settings follow the resolved gating (no permission hook installed on a bypassed run).
- Web-spawned sessions with
New
-
Web-spawned sessions can run unattended: the spawn command's new
bypass_permissionsboolean makes the daemon start the agent withlive --no-permissionsplus the agent's own bypass flag (claude --dangerously-skip-permissions,gemini --yolo), so an owner-started session raises no permission probes. The boolean is mapped to these fixed flags in the daemon — nothing from the wire reaches the argv. -
Document self-hosted Pocket daemon and browser-platform setup separately.
-
Install and update from GitHub Releases without a website mirror or automatic login.
-
Default new CLI configurations to the local single-origin web stack; preserve explicit host settings.
-
Remove the hosted website mirror/deployment from CLI releases.
-
- Fix: web-spawned / headless sessions ended instantly with no agent attached.
tmuxPaneStatusparsedtmux list-panes -F "#{pane_dead} #{pane_exit_status}"assuming two fields, but a live pane reports an empty#{pane_exit_status}(one field,"0 "). That was treated as an error, soattachOrWaittore the session down the moment it started. Only the headless path (live daemonweb-started sessions) was affected — interactive runs usetmux attach-sessionand never hit this. Parsepane_deadfirst; read the exit code only once the pane has actually exited.
- Fix: web-spawned / headless sessions ended instantly with no agent attached.
live agysingle permission gate parity with Claude & ephemeral plugin scoping:- Automatically pass
--dangerously-skip-permissionsto AGY when LiveShortly's web permissions are ON so the Web UI / popup is the single source of truth (no double CLI prompt). - Auto-detect
--dangerously-skip-permissionson the CLI to disable LiveShortly's hook broker. - Make
live hookalways output safeallowdecisions whenLIVE_SIDECARis unset, preventing non-live AGY sessions from ever being blocked. - Scope AGY plugin lifecycle to the session by automatically running
agy plugin uninstallon session cleanup.
- Automatically pass
- Fixed: input/comments from the web were not delivered into
live agy.- Wire
sc.OnCommentin tmux mode to paste and submit viewer comments directly into AGY's composer when idle, prompting AGY to process them immediately. - Automatically queue comments for
Stophook continuation (decision: "continue") andPreInvocationinjection if AGY is mid-turn. - Remove standard MCP tool siphoning that previously emptied the hook queue for AGY.
- Wire
- Fixed: input/comments from the web were not delivered into
live agy— full parity withlive claudefor Google Antigravity (AGY):- Deep lifecycle hook capture for Antigravity CLI (
agy/antigravity) via an ephemeral managed plugin (PreInvocation,PreToolUse,PostToolUse,Stop). - Structured live feed: captures prompts, replies, tool executions (
run_command), file diffs (write_to_file,replace_file_content), and web fetching. - Permission control: answer AGY tool prompts from the web or in-terminal tmux popups
(
allow,deny,always-allow). - Viewer comments: delivered into running AGY sessions via the
liveshortly-channelMCP server and hook-boundary injection (injectSteps/Stopcontinuation). - Session forks: continue any session with
live agy --session <id>orlive agy --handoff <code>. - Web daemon:
live daemoncan now spawnagysessions directly from the web.
- Deep lifecycle hook capture for Antigravity CLI (
CLI audit follow-up: correctness fixes across join, exit codes, session lookup, and UTF-8 handling.
- Fixed:
live versionlied ongo runbuilds. The fallback was stuck at1.0.1while releases were on2.x. It now reads the checked-inVERSIONfile when not set via-ldflags. - Fixed: tmux and app-server modes always exited 0. Agent failures were invisible to shells and CI. Tmux paths now propagate the pane exit status; app-server mode returns the codex process exit code (130 on Ctrl-C).
- Fixed:
live joincalledos.Exitwhen a session ended. That skipped terminal cleanup and raced with raw-mode input. Session end now unblocks the main loop cleanly. - Fixed: live join stream dedup used hidden events.
baseSeqcame from the last raw event (includingstream_start), which could duplicate or skip live events. It now tracks the max seq of visible events only. - Fixed:
live ls -n 0fetched every session.-nnow requires a positive count; use--allfor the full list. - Fixed: short session handles missed on large accounts.
resolveIDonly scanned the first 100 sessions; it now paginates through all of them. - Fixed: invalid session URLs were forwarded to the API. Pasted links now
validate the UUID after
/session/. - Fixed: UTF-8 truncation in viewer comments, PTY output, codex instructions, and rollout clips. Byte cuts could split multi-byte runes; all are now rune-safe.
- Fixed: desktop notifications always opened liveshortly.com. They now use
the configured
WebURLfrom host.json. - Fixed: sidecar shutdown race. The hook HTTP server could still POST while the event queue was closed; HTTP now shuts down before the queue.
- Fixed: app-server JSON-RPC hang on write failure. A failed write left a pending entry that blocked forever; pending entries are now cleaned up.
- Fixed:
Follow-up to v2.0.0: a live "is working" indicator, and a codex capture bug bad enough to justify the release on its own.
- Fixed:
live codexcaptured nothing unless you typed within 30 seconds. The rollout watcher waited 30s from launch for codex to open its session file and then gave up for good — but codex writes that file when you send your FIRST PROMPT, not when it starts. Any session where the first message came later was recorded as an empty feed, which is most of them, and certain whenever codex opens with an update prompt. The timer bought nothing: the goroutine already exits with the session's context, which is the correct bound. It now waits as long as the session lives, polling briskly at first and easing off once it is clearly waiting on a human. Verified by idling 45s past launch — the window that used to kill capture — and getting a full transcript. - The prompt says when the agent is working. A spinner and "<agent> is
working…" sit in the status chip for as long as the turn runs, so a watcher
can tell a thinking agent from a hung one. The rule differs by capture because
the captures differ: a hooks agent brackets a turn (
prompt…response) and emits nothing in between, so an opening event holds the indicator until the turn closes — the web's silence-based rule would drop it a few seconds into every turn, which is precisely the stretch worth reporting. A pty agent has no prompt/response at all, onlyoutputand astream_startheartbeat, so silence is the only signal and the window is set wider than the flush interval to stop it strobing between the batches of one reply. Taking the later of the two deadlines covers both without branching, and an open turn is capped so a dead agent stops claiming to work.live watchcarries the same indicator on a status line, without taking the keyboard. - The indicator names what is actually running, most specific first: the
model when the session has reported one, else the agent that was launched —
gpt-5.5,claude-opus-5,codex,gemma4. It used to say "Claude", which is a brand rather than the thing doing the work, and would have been an outright lie beside a codex or ollama session. The transcript's own speaker label now resolves through the same function; the two disagreed before, one saying "Claude" while the turn above it said "claude-opus-5". - Fixed: the status chip rendered faint. The prompt wrapped the whole status
in
\033[2m, so dim was still in effect when a colour inside it began and the terminal drew dim green — applied to the two things on that line most needing to be seen, the working indicator and the BLOCKED permission chip. The status owns its own colours now, and the indicator is bright rather than normal green: a terminal's normal green is a body-text colour, not a signal. - A message you send lights the indicator on keypress, not when the round trip finishes. Sending is itself a network call, so the terminal used to admit nothing was happening for two round trips. The optimistic window is short and expires by itself, so a message queued against an idle agent does not leave a lying indicator behind.
- Fixed:
The terminal stops being write-only. Until now
livecould start a session and nothing else: to see what you had running, who it was shared with, or what someone else's agent was doing, you opened the website. This release moves that whole surface into the CLI, and adds the thing the website could never do — letting two developers hold a conversation through one agent, terminal to terminal.live ls/live shared— your sessions, in the terminal. One line each — status, title, handle, link, agent — and the ten most recent by default, since a listing is a glance rather than an archive;--allpages through the rest and-n <limit>picks a count. Filters:--mine/--shared,--live/--ended,-q <text>, and--jsonfor piping.live sharedislive ls --shared. The link is never truncated to make a row fit: the title column gives way first, and the agent column is dropped before the title stops naming anything.live join <id>— attach a second terminal to a running session. It prints recent history for context, streams the session as it happens, and sends anything you type to the running agent as a viewer message — the same path the web composer uses, so it reaches the agent mid-task. The person who owns the session sees your message arrive in their agent; you see the reply stream back.live watch <id>is the read-only half./info,/url,/fork,/stopand/quitwork while joined.- The joined view reads like the web viewer, not like a log. Every turn is
a block that names who produced it — the owner (marked you when it is your
own session), the model that replied, or the viewer who spoke — with its text
wrapped and shown in full instead of clipped to one line. Work steps render
as compact rows: the tool and its target, captured output under a
⎿, and file writes with a coloured diff and its+/−counts. Permission and input waits surface as their own notice, a command flagged dangerous is marked, and long paths keep the tail that identifies the file. - The input line holds its ground.
live joindraws its own prompt in raw mode — carrying the session's uptime and live watcher count — and prints arriving events above it, so an event landing mid-sentence no longer splices itself into what you are typing.↑/↓recall what you sent,Ctrl-Uclears,Ctrl-Wdeletes a word,Ctrl-A/Ctrl-Eand the arrows move the cursor. A non-terminal stdin falls back to plain line reads, soecho "ship it" | live join <id>still works, and the join exits by itself when the session ends. - A joined terminal can answer the agent's permission prompts. A session
used to stall whenever its owner walked away from the tool prompt. Now the
request surfaces in the joined view with
/allow,/denyand/always, the prompt turns into aBLOCKEDchip until it is answered, and joining an already-stuck session says so on arrival. It posts to the same/decisionendpoint the web viewer uses and carries the same comment-level authorization, so the running shim — which polls for a verdict every second — resumes just as it would from the browser, and the decision is emitted as an event so every watcher sees who answered. live share <id> <email>— grant access without leaving the shell. Defaults tocommenter(the role a CLI joiner needs to talk back);--role vieweris watch-only, and--listshows who has access.live info <id>prints one session in full — lineage, machine, repo and branch, whether the agent stream is currently attached — plus its last events.live stop,live open,live hostsend a running session from anywhere, open its page, and list the machines currently runninglive daemon.- Every command that takes an
<id>accepts the eight-character handle the listings print, a full session id, or a pasted session link.
No server change was needed for any of it: the listing, streaming, comment and share endpoints already existed for the web client.
- Fixed:
~/.liveshortly/host.jsonwins overLIVESHORTLY_API_URLagain. v1.3.0 inverted this.LIVESHORTLY_API_URLis routinely left exported in a shell profile from earlier debugging, so a staleexport LIVESHORTLY_API_URL=http://localhost:8000silently redirected everylivecommand away from the production host the user had configured withmake use-prod— sessions, login, and token refresh alike. host.json is the explicit, tool-managed switch and is authoritative; the env var remains the way to point at a stack when no host.json exists. - Fixed: a token refresh against the wrong host no longer deletes your
credentials.
Refreshtreated any 401 as "this token was revoked" and removedcredentials.json. Refreshing against a host that never issued the token also 401s, so the combination above could destroy a perfectly good production login. A 401 now only deletes when it comes from the issuing host; otherwise it reports the mismatch and leaves the file alone.
- Fixed:
- Start a session from the browser:
live daemon. Runninglive <agent>in a terminal was the only way to open a session, so every session cost a context switch to a shell.live daemonregisters this machine with the server, holds a command stream open, and starts the ordinary capture path when the web asks for a session — same hooks, same permission gating, same feed. Take one over locally at any time withtmux -L liveshortly attach -t live-<id8>. A spawn command is remote code execution on your machine, so the daemon trusts the server for nothing: the server sends an agent NAME, never a command line, and the daemon re-checks it against its own allowlist and the directory against the list passed to--dir. Nothing reaches a shell. Needs a server with the hosts API. - Fixed:
curl … | bashnow really does install onto your PATH and sign you in. Both interactivity checks tested[ -t 0 ], but under the documented one-liner stdin is the script pipe — so for everyone who followed the README it was false, and the installer quietly skipped/usr/local/bin(landing in~/.local/bin, off PATH) and skipped sign-in. It now tests the controlling terminal, which also keeps CI and cron from hanging on a device flow nobody can approve. - Fixed: a viewer comment was answered twice. Every comment was pushed to the channel and unconditionally queued for hook-boundary injection, on the reasoning that a buffered send is not proof the agent consumed it. Nothing ever acked, so with a healthy channel the duplicate was not rare — it was every message. Comments now route to the channel first and fall back to hooks only when no subscriber took them; delivery stays at-least-once, acked per hop.
- Fixed: the second session on a long-lived tmux server inherited the first
one's environment. tmux seeds a new session from the server's environment
for anything outside
update-environment, which lists neitherHOMEnorPATH. The agent could therefore read a differentHOME, find no config and restart its first-run wizard, while hooks posted to whatever API URL the tmux server was born with. The load-bearing variables are now pinned per session. - Fixed:
LIVESHORTLY_API_URLnow wins over~/.liveshortly/host.json. With the file taking precedence, pointingliveat a local stack meant overridingHOME— which relocates~/.claudeand caused exactly the first-run wizard above. - The "Loading development channels" confirmation fires on every launch, not once as the message claimed. A web-started session answers it, since no operator is there to.
- Start a session from the browser:
- Storage-quota aware. When the server auto-ends a session because its owner
hit their storage quota,
livenow reacts to the crossing signal on the event-emit response: it stops capturing and prints where to pick up — the downloadable session summary and thelive claude --handoff <code>command to continue after freeing space. - Capture is lossless up to 256 KB per event. Prompt, assistant-reply and tool-output caps were raised to match the server's per-event belt cap; truncation is now purely a display concern handled when fanning out to viewers, so the stored feed (which the storage quota meters) is complete.
- Storage-quota aware. When the server auto-ends a session because its owner
hit their storage quota,
- Forking a session into
codexnow carries the prior context. codex has no--append-system-prompt-file, so a fork printed the digest and then tmux repainted over it. The digest is now passed as-c developer_instructions=…, which orients the agent without submitting an opening turn. Clipped to 64 KiB so a long fork's launch command stays underARG_MAX; the full text always remains at the digest path. - Fixed: a trailing
-c key=valuewas mistaken for the opening prompt and submitted to the model.liveinjects those itself, so the scan that looks backwards for the positional prompt now skips-c/--configvalues. - Releases are cut by CI from a push to
main, gated by the newVERSIONfile: a push releases only if the tag it names does not exist yet. One run tags the repo, publishes the binaries tolive-dist, mirrors them (and the installer) toliveshortly.com, and bumps the Homebrew formula. - The Homebrew formula installs the prebuilt binary instead of compiling from
source, so
brew installno longer needs Go or access to the private repo. - Everything moved to the
liveshortlyGitHub org. The Go module path is nowgithub.com/liveshortly/live. Old URLs redirect, so nothing you have pinned breaks.
- Forking a session into
livenow installs as a prebuilt binary.curl -fsSL https://liveshortly.com/i.sh | bashdownloads a signed-off, checksum-verified static binary for your platform and puts it on your PATH. No Go toolchain, no source checkout, no build step, and no repo access required. Installs went from ~30 s to ~2 s.- Release artifacts are published on every
v*tag to the public resapce/live-dist repo by CI, and mirrored athttps://liveshortly.com/install/. The installer prefers GitHub and falls back to the mirror;live updateis unchanged. - The installer verifies the SHA-256 and runs the staged binary to confirm it
reports the expected version before replacing anything. The final move is an
atomic rename, so a failed install can never leave a partial binary on PATH,
and a running
liveis never truncated under itself. - The whole script is wrapped in
main(), invoked on the last line: a truncated download now runs nothing at all instead of half an installer. - Rerunning the installer upgrades
livein place wherever it already lives, rather than dropping a second copy that shadows the first on PATH. - New installer flags:
--bin-dir,--no-login,--no-modify-path,--from-source. Non-interactive installs (piped, no TTY) no longer hang on the device-flow sign-in — they printlive loginand exit cleanly. install.shstill works and is byte-identical toi.sh.
-
live codexis now interactive and real-time at the same time. Codex runs in an isolated tmux session (private-L liveshortlysocket) — you drive the real codex TUI as usual — and viewer comments are typed straight into its composer. This closes the long-standing gap where a comment posted from the web never reached an idle codex.Why it was broken: codex is a standard MCP client, so it only saw viewer comments when it chose to call the
poll_viewer_messagestool — which an idle agent never does. Delivery was pull; it needed to be push. tmux gives a scriptable handle on the pane, so a comment lands the moment it arrives, whether codex is idle or mid-turn (mid-turn comments queue and run next).- Comments are delivered via a tmux paste buffer with bracketed paste, so
arbitrary text (
;, leading-, embedded newlines) can't be misread as tmux arguments or submit a turn early. - A modal guard refuses to type when codex is showing a dialog (directory trust, approval prompt), rather than silently answering the wrong question.
- Permission prompts keep the existing
display-popup+ web race. - Feed events still come from the structured rollout tailer — the screen is never scraped for content.
- Falls back to the previous PTY rollout mode when tmux is absent, and is
skipped for headless
-pruns.LIVE_TMUX=0opts out.
- Comments are delivered via a tmux paste buffer with bracketed paste, so
arbitrary text (
-
tmux is now required for the full
live codexexperience (it was only "nice to have", for Claude's permission popups). Without it, codex silently falls back to the mode where idle comments never arrive. -
Fixed: a viewer comment injected into codex was emitted to the feed twice — once as
viewer_comment, once as thepromptcodex appeared to receive. The rollout mapper now skips viewer-injected turns.
-
- Real-time
live codexvia the app-server protocol (opt-in,LIVE_CODEX_APPSERVER=1). Drives a headlesscodex app-serverover JSON-RPC instead of the native-TUI sidecar, for true real-time parity with Claude:- Viewer comments push instantly as real turns (
turn/steermid-turn,turn/startwhen idle) — no polling. - Permission prompts answered from the browser. Codex's approval requests (command / file-change) are gated through the same web allow/deny flow as Claude and mapped to codex's decision.
- Streamed items map to the existing feed rows (prompt / response / tool_call / file_write / stream_end).
- Codex is headless in this mode — you watch, steer, and approve from the session page. The default remains the native-TUI rollout mode.
- Viewer comments push instantly as real turns (
- Also shows the prior-context digest in the terminal on fork (from the
interim build) —
LIVE_NO_CONTEXT=1to suppress.
- Real-time
live codex— full parity withlive claude(codex-cli 0.142.5). SeeCODEX.md.- Structured capture. New
rolloutcapture mode tails codex's own session file (~/.codex/sessions/**/rollout-*.jsonl) and maps its events to the same feed rows Claude produces — prompts, replies, tool calls, and file diffs — instead of scraping raw terminal text. - Handoff parity.
live codex --handoff <code>/--session <id>now inject the orient prompt as codex's initial prompt automatically (no more paste banner). Applies to any agent that takes an initial prompt. - Two-way viewer comments. The
live channelMCP loads into codex per-session (ephemeral-c mcp_servers.*overrides, nothing persisted) and exposes a standardpoll_viewer_messagestool so codex can pull viewer messages as real turns. - Web-gated permissions (opt-in,
LIVE_CODEX_PERMS=1). A session-scoped codexPreToolUsehook gates tool runs through the same browser allow/deny flow as Claude — codex's hook payload/decision format matches Claude's, so the server side is reused unchanged.
- Structured capture. New
- Prior context is shown in the terminal on fork. A fork/handoff now prints
a compact, bounded view of the source session's digest (the most recent
turns) so you can see what you continued from without opening the file or the
web.
LIVE_NO_CONTEXT=1suppresses it. - Fix:
--handoffno longer exits instantly. The viewer-channel flags ended on a variadic flag that swallowed the injected handoff prompt, so the forked agent started with no prompt and quit. The channel flags now end on a boolean so the prompt survives.
The handoff & fork major release. Bumps the app to 4.0.0 and consolidates the handoff/fork work (below) with the following:
- Prior-context full-history view (virtual, copy-on-write). A forked session
now shows its source's entire prior history.
GET /api/sessions/{id}/lineagereturns the source events up to the pinned snapshot seq; the web renders them as a distinct, collapsible PRIOR CONTEXT block above the fork's own feed — nothing is physically copied. RBAC is re-checked on both the fork and the source (never a capability bypass): no source access → events withheld (restricted: true); deleted source → reference only. Because each session numbers its ownseqfrom 1, lineage is a separate block, never merged into the fork's seq-sorted feed. It's a human/replay surface — the agent is still seeded by the compact digest, not by replaying every prior turn. - Codex support. Codex is a first-class capture framework; the viewer shows a human-friendly Framework label (Claude Code / Codex / Gemini / Terminal).
- Server version surface.
GET /healthnow returnsversion(single source of truth inapi/internal/version).
- Prior-context full-history view (virtual, copy-on-write). A forked session
now shows its source's entire prior history.
- Session handoff / fork. Continue any session you can read — live or
archived, yours or someone else's — as a brand-new session you own, with
any agent:
live claude --session <id>forks from a session's latest state.live claude --handoff <code>forks from a pinned handoff snapshot.- The original session is untouched; the server increments its fork count.
On launch, the new agent is seeded with the prior context: the digest is
written to
~/.liveshortly/handoffs/<id>.mdand Claude opens with a prompt to read it, restate the state, and continue. PTY agents (gemini/codex/…) get the file plus a paste-in banner. A private, unshared source prints a clean "you don't have access to fork this session". --pre-digest(off by default) pre-compresses the context with a headlessclaude -ppass before the interactive launch.
live handoff <session-id>prints the copyable command to continue a session (mints a handoff code server-side).
- Session handoff / fork. Continue any session you can read — live or
archived, yours or someone else's — as a brand-new session you own, with
any agent:
- Handoff / fork. Continue any readable session (live or archived, even someone
else's) as a new session you own, with any agent — the original is untouched.
Mint a 7-day signed handoff code (
POST /api/sessions/{id}/handoff) or fork by id (POST /api/sessionswithforked_from/forked_from_session_id[+ forked_from_seq]). The server returns a deterministic digest (no LLM) that seeds the continuing agent. Lineage columns (forked_from_session_id,forked_from_seq,forked_at) are recorded on the fork and the source'sfork_countis bumped in one tx. Session-detail responses carryforked_from(source ref) +forker_count. - Admin. Super-admin grant by email allowlist.
- Handoff / fork. Continue any readable session (live or archived, even someone
else's) as a new session you own, with any agent — the original is untouched.
Mint a 7-day signed handoff code (
- Permission control is now ON by default for interactive
live claude. You approve tool runs from a tmux popup (when tmux is installed) or the session page — allow / deny / allow-always, owner only. Turn it off per run with--no-permissions/LIVE_WEB_PERMS=0; disable just the tmux popup withLIVE_TMUX=0. When tmux is absent, a plainlive claudefalls back to Claude's native terminal prompts (install tmux for the in-terminal popup), and headless-p/--printruns are never gated.
- Permission control is now ON by default for interactive
Permission control — answer Claude's tool prompts from the web, or a tmux popup. (Opt-in in v0.3.0; on by default from v0.4.0.)
-
New
--web-permissionsmode (LIVE_WEB_PERMS=1), opt-in, owner-only: answer Claude's tool-permission prompts from the web instead of the keyboard. The PreToolUse hook blocks and long-polls the/decisionendpoint; your Approve/Deny tap on the session page becomes Claude's permission decision, pre-empting the local prompt. Verified end to end (allow → tool runs, deny → tool blocked, no keyboard prompt either way). While on, it gates every matched tool, so the local allowlist is bypassed; falls back to deny if unanswered within ~8 minutes. -
--web-permissionsgains Allow always: tapping ✓✓ ALLOW ALWAYS on the session page allows the tool now and remembers it for the rest of the session, so matching calls auto-allow without re-prompting — Claude Code's "don't ask again", owner-driven. Memory is keyed by the leading command word for Bash (git,npm, …), the tool for Edit/Write/MultiEdit, and the host for WebFetch. Verified end to end: after one "always" ontouch, a secondtouchauto-ran with no further prompt; deny still blocks. -
LIVE_TMUX=1— clean CLI permission prompts via tmux (install tmux for the best experience). Runs Claude inside an isolated tmux session (dedicated socket) so a permission request appears in atmux display-popup([y] allow / [a] allow always / [n] deny) without corrupting Claude's TUI — tmux composites them in separate regions. Dual-source: answer from the popup keyboard OR the web, first responder wins (via the broker + a local/permendpoint and alive perm-promptpopup subcommand). Requires tmux; falls back to web-only if absent. Verified e2e: both the popup-keyboard and web answers run the tool. Caveat: you're wrapped in tmux (Ctrl-B prefix; nested-tmux quirks), and it shows our allow/deny/always, not Claude's exact native menu.
-
- Fix: viewer comments delivered to an idle Claude over the channel were
echoed into the feed a second time as a raw
promptevent, showing the<channel …>…</channel>wrapper around the message. These synthetic channel turns are now skipped — the cleanviewer_commentevent already represents them.
- Fix: viewer comments delivered to an idle Claude over the channel were
echoed into the feed a second time as a raw
- Idle-viewer channel delivery is now on by default for interactive
live claude: a per-session channel MCP server pushes viewer messages to Claude even while it sits idle (previously opt-in behindLIVE_CHANNEL=1). Verified end to end — an idle Claude surfaces and answers a viewer comment. The first launch shows Claude Code's one-time "Loading development channels" confirmation (accept it once). SetLIVE_CHANNEL=0to force pure hook-boundary delivery; skipped automatically in-p/headless mode.
- Idle-viewer channel delivery is now on by default for interactive
- New
live updatecommand: self-updates from the release binaries served athttps://liveshortly.com/install/(sha256-verified, staged + atomically swapped, sanity-run before replacing).live update --version vX.Y.Zinstalls any published version, downgrades included. install.shaccepts a version pin:--version vX.Y.Zflag orLIVE_VERSIONenv (piped form:curl … | bash -s -- --version vX.Y.Z).- New
make release/make publishtargets: cross-compile stripped binaries for darwin/linux × arm64/amd64 and publish them (gz + sha256 + latest.txt) into the LiveShortly web app's/install/folder. The folder is cumulative — every released version stays installable.
- New
install.shno longer installs or modifies Go. Go 1.21+ is now a stated prerequisite: the script checks for it and exits with instructions (go.dev/dl or your package manager) when missing. Everything else — fetch/update source, build, PATH install, sign-in — is unchanged.
Initial release.
live claude— full semantic capture via injected lifecycle hooks (prompt, pre_tool, tool_call, file_write with diffs, response, input_requested, stream_end) plus model auto-detect.live <any command>— universal PTY capture (ANSI-stripped, batchedoutputevents) with viewer-comment overlay.- Two-way viewer messaging: comments injected as context at prompt /
post-tool boundaries; SSE
agent/streamclient with automatic 3s-polling fallback for servers that predate it. - Browser device-flow auth (
live login/logout/whoami) sharing~/.liveshortly/credentials.json. - One-command installer (
install.sh), Makefile, Homebrew tap (resapce/homebrew-tap).
- Publish → Feed. "Share to all" is replaced by Publish: a session becomes
public and discoverable. New
/feedpage — a searchable, infinite-scroll grid of synthesized HUD tiles;published_at/hero/search_vectorcolumns; keyset feed pagination +ts_rankrelevance search;POST /publish,/unpublish,GET /api/feed. - Typing indicators. Ephemeral "viewer is typing" presence over SSE (
POST /typing, not persisted) and a derived "Claude is working" indicator, in a themed blinking-caret component. - Session layout. The event log now fills the viewport and scrolls internally, so short sessions no longer waste space and the composer stays pinned below.
- Real model name in the UI — detected from the session transcript and reported
via
PATCH /api/sessions/{id}instead of a hardcoded"claude". - 7-hour idle timeout (was 2h) for live sessions and the pending-comment TTL.
- Input/permission requests surface in the web (non-blocking) via the capture
client's
Notificationhook →input_requested; the CLI is never stalled on the web.
- Publish → Feed. "Share to all" is replaced by Publish: a session becomes
public and discoverable. New
- Surface input-requested in the web — banner, browser notification, focused composer.
- Mobile-friendly UI; lighten the dark theme to a graphite gray for readability.
- CI deploy: self-heal the SSH host key via
ssh-keyscanafter a server IP change.
- Night mode, brand icon, local clock, fancy session names, rename/end, idle reaper, and session visibility.
- Multi-domain auth + nginx for liveshortly.com; relative browser URLs so auth stays on the active domain.
- Theme + header polish: slate-gray light theme, theme toggle above the user menu, no long-caching of HTML so deploys propagate.
- Initial LiveShortly: Go API + SSE, Next.js terminal-HUD web client, and Claude Code capture hooks for live session sharing.
- Auth: Google sign-in for the web; device-flow CLI login; persistent users, authorization, and Drive-style sharing; "share to all" view-only public link.
- Chat-transcript viewer with quoted replies + markdown; owner identification from
the CLI
user@hostname. - Production deploy: compose, SSE-aware nginx, env template, deploy guide, and
auto-deploy CI on push to
main.