chore: changesets for the 1.32.1 batch

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-09-23 11:40:03 +02:00
parent 2fb744f865
commit 13e652e43f
13 changed files with 46 additions and 58 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Terminal copy: copying text out of a Claude Code or Codex pane no longer puts the pane's two-column transcript gutter on the clipboard, so pasted lines arrive flush instead of indented (#469). The width comes from the CLI registry (`capabilities.transcriptGutter`, 2 for claude and codex, measured on live panes) and is only a ceiling: a selection only ever shifts as a block, so its own indentation survives. Other CLIs and shells are untouched. It works in split panes and detached session windows too, and can be turned off per device in App Settings under Selection & clipboard.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Sessions: recovering a Claude session whose tmux pane had died relaunched `claude --session-id <id>`, which Claude refuses once that id has a transcript, so the pane died again straight away and the conversation was stranded. The relaunch now resumes the conversation (`--resume <id> || --session-id <id>`), including when tmux lost the whole session (#467).
+1 -9
View File
@@ -2,12 +2,4 @@
"aicodeman": patch "aicodeman": patch
--- ---
fix(terminal): a dropped output frame is recovered, not just scheduled for recovery Terminal: when a burst of output overflows the render queue and a frame has to be dropped, the repaint that repairs it is now retried until it actually happens, instead of being scheduled once and silently skipped when another load was in flight (#470).
`_onSessionTerminal` drops an incoming frame once the app-owned render queues already hold 128KB. That is the right call — the alternative is an unbounded backlog — but a hole in a TUI byte stream is a desynced cursor, and a desynced cursor is muffled text (#464). The drop was only half of it.
The recovery was a fire-and-forget timer: it nulled its own handle and then called `_onSessionNeedsRefresh()`, which opens with four early returns. Two of those — a buffer load already in flight, a refresh already owning this session — are **most likely to be true during exactly the output burst that caused the drop**, so the recovery was skipped precisely when it was needed, with nothing left to retry it, and those bytes were never replayed.
`_onSessionNeedsRefresh` now reports whether it actually repainted, and `_scheduleDroppedOutputRecovery` re-arms while it has not. Bounded, because every reason the refresh can be skipped is transient contention that clears in seconds and a permanently failing refresh must not become a loop against the API — and giving up at the cap leaves exactly what the old code left, so the floor is no worse. The same 2s debounce still collapses a burst of drops into one attempt.
This is the principle the review of #431 established for the WebSocket output-gap marker — only a repaint that actually happened settles the recovery — applied to the one recovery path that still relied on a timer having fired.
-25
View File
@@ -1,25 +0,0 @@
---
'aicodeman': patch
---
Terminal copy: take the transcript gutter off the clipboard, using the width the CLI declares.
Copying a paragraph out of a Claude Code pane put the pane's own two-column transcript gutter on the clipboard, so every pasted line arrived indented. #451 shipped the trailing half of the copy clean and deliberately left the leading half out, because deriving the width from the selection fires on 73% of ordinary indented text and cannot tell a margin from content.
The width is now declared rather than derived. `capabilities.transcriptGutter` on the CLI registry is a bounded integer; claude and codex each declare 2, measured on live panes, and no other stock entry declares any, so a CLI whose transcript layout nobody has measured is never touched. The server publishes the map as `window.__codemanTranscriptGutter`, built by filtering `enabledClis()` on the capability rather than by listing ids, and the copy path looks the active session's mode up in it. It reads no terminal buffer at all.
The declared width is a ceiling, not the answer: `clean()` strips the lesser of it and the run every selected line shares. A block can therefore only shift as a unit, the structure inside a selection survives by construction, and a selection reaching column 0 loses nothing. That is what keeps a `git log` body at its own four-space indent inside an agent's two-column gutter.
Two derived versions were built and measured first, and both are worth recording because both looked correct. Painted trailing padding — a full-screen TUI writes real spaces across the unused part of a row, a shell leaves them never-written — has no false positives and never over-stripped, and is a function of pane WIDTH: that padding exists only while a rendered line stops short of the CLI's own layout width, and Claude's prose wraps to fill it. Dragging the same two prose rows of one live transcript at five window sizes, the share of padded rows ran 44%, 6%, 6%, 7% and 87% at 123, 160, 198, 235 and 298 columns, so the strip silently did nothing at every ordinary size. Taking the narrowest indent on the surrounding rows fires at every width and over-strips about 1%, because a file listing inside the transcript can be the narrowest thing on screen.
Measured over 1,392,281 selections — every 1, 2, 3, 5, 10 and 20-row window of real Claude screens replayed from live PTY streams at 100, 120, 160, 198, 235 and 282 columns — the declared width over-strips none, breaks no relative indent and alters no text, and serves 100% of the selections whose own indent covers the gutter. Verified end to end in a browser with a real mouse drag and a real Ctrl+C at 123, 160, 198, 235 and 298 columns: a Claude pane pastes flush at every one, a shell pane is untouched at every one.
Codex was measured the same way and gets the same two columns. It renders nothing like Claude — it draws boxes narrower than the pane and pushes its transcript into ordinary scrollback — so its layout was checked on its own live answer: the `•`/`›`/`⚠` markers sit in the gutter, prose continuations sit at 2, and a nested YAML block the model wrote rendered at 2/4/6/8 for its own 0/2/4/6. Replayed at 100, 120, 160, 198, 235 and 282 columns its indents were 0, 2, 4, 6 and 8 at every one and never 1. Copying that YAML out of a live Codex pane now yields 0/2/4/6: the gutter gone, the block's own nesting intact and paste-ready.
The strip sits behind `copyStripMargin` in App Settings under Selection & clipboard, per-device and default ON. It is a display key, deliberately absent from the `.strict()` `SettingsUpdateSchema`, and read as `!== false` because the desktop branch of `getDefaultSettings()` returns `{}`. The toggle is checked before the map is consulted.
The mid-row flag is back and governs one line rather than the whole block. It reads an ordered range, so neither end of a drag can move the result, and `range.start.x > 0` excludes only the first line — the one the mousedown genuinely cut the margin off.
Both panes of a split strip the width their own CLI declares. `_cliGutterColumns()` and `_normalisedSelectionRange()` take the session and the terminal to read, defaulting to the primary pane's, so Pane B looks its own run mode up instead of keeping a margin Pane A drops on the same keystroke.
A detached session window (`/session/:id`) receives the gutter map too. It runs a terminal and so copies through the same path, and the injection needs no availability probe, so it sits outside the block that skips the run menu's payloads for a solo window.
-5
View File
@@ -1,5 +0,0 @@
---
"aicodeman": minor
---
Docker images: optional GitHub CLI and Azure CLI for private GitHub and Azure DevOps repositories. Both are opt-in and off by default. Build the server image with `CODEMAN_INSTALL_GH=1` / `CODEMAN_INSTALL_AZ=1` (under `build: args:` in `docker/docker-compose.override.yml`) to add `gh` and/or `az` with the `azure-devops` extension, plus system Git credential helpers that route github.com through `gh auth git-credential` and dev.azure.com / *.visualstudio.com through a new `az`-backed helper (`docker/git-credential-azure-cli`, which also honours `AZURE_DEVOPS_EXT_PAT`). Sign the CLIs in once from a shell session and Add Case → Clone Repo can clone private repositories; until then a private clone still fails fast with an authentication error. The Docker-case agent image takes the same switches from `CODEMAN_AGENT_IMAGE_INSTALL_GH` / `_AZ` (the `environment:` of the override file, or in front of `build-agent-image.mjs`), and, only with those switches on, a seeded Docker case also copies the `gh` sign-in (`~/.config/gh/hosts.yml`, `config.yml`) and the `az` sign-in files from `~/.azure` into newly created case containers, read-only and per file like the other CLIs. In multi-user mode, Clone Repo runs a non-admin's clone and preflight with git's credential helpers cleared, so the server account's sign-in is never lent to them. The Clone Repo authentication error now says how to sign the server's git in instead of claiming private repositories cannot be cloned. This changes `server.Dockerfile`, so Compose deployments need a `Start-Codeman.sh` rebuild rather than an in-app update; with neither switch set the rebuilt image is functionally unchanged.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Mobile: a long press on blank terminal space on Android Chrome no longer opens the keyboard and blanks the terminal (#471, fixes #360). The long-press guards are now armed before the press is checked for selectable text, so a press on empty space is swallowed the same way a press on a word already was.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Sessions: a tab whose agent has exited (the CLI quit, but tmux kept the pane) now says so with a muted dot and an `exited (137)` badge, instead of looking like an idle session (#466, part 1 of #446). The state is published as `paneExit` on the session and survives a restart. Nothing closes such sessions yet; that is part 2.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Docker: optional GitHub CLI and Azure CLI for private repositories (#472). Both are off by default. With `CODEMAN_INSTALL_GH=1` / `CODEMAN_INSTALL_AZ=1` as build args in `docker-compose.override.yml`, the server image gets `gh` and/or `az` (with the `azure-devops` extension) wired in as git credential helpers, so after one `gh auth login` or `az login` from a shell session, Add Case → Clone Repo can clone private GitHub and Azure DevOps repositories. `CODEMAN_AGENT_IMAGE_INSTALL_GH` / `_AZ` do the same for the Docker-case agent image, and only then are the sign-ins copied into new case containers. In multi-user mode a non-admin's clone runs with the credential helpers cleared. This changes `server.Dockerfile`, so Compose deployments need a `Start-Codeman.sh` rebuild rather than an in-app update.
-19
View File
@@ -1,19 +0,0 @@
---
"aicodeman": patch
---
fix(terminal): five ways the terminal silently stopped being correct
**The PTY and the browser terminal must never disagree about size (#464).** "Text gets muffled sometimes" was arithmetic, not a dropped frame. Claude Code's TUI wraps its frame at the width the PTY reported and erases the previous frame by walking the cursor up the rows it believes that frame took, so a browser terminal of a different width makes the erase come out short and each repaint paints over rows nothing cleared — the doubled lines and half-overwritten prose in the report. Four ways the two drifted apart, all silent: `fitAddon.fit()` sized xterm to the raw measurement while every server-facing path reported it floored at 40x10 (font size 44 on a 430px phone proposed 13 columns, the server was told 40, xterm stayed at 13); `throttledResize` and `sendResize` reflowed locally while deliberately withholding the SIGWINCH; the font setters moved the cell size and told the server nothing; and `Session.resize` declined small-viewport requests under an active desktop sizing claim without telling the asking client, because resize was write-only. `syncTerminalGeometry()` is now the one function that changes the terminal's size — it fits, floors and applies as a single step — and both transports answer a resize with the geometry the PTY actually holds, which the client adopts by **columns only**. A terminal left wider than the box that shows it now earns horizontal reach for as long as that lasts, whether the cause is another device's claim or the 40-column floor.
**A replay clear must be in-stream, never `reset()`/`clear()`.** xterm's `write()` is queued while `Terminal.reset()` is synchronous and does not reset the parser, so bytes queued just before a reset are parsed after it and fuse into the snapshot written next — `write('p8'); reset(); write('rmissions')` renders `p8rmissions`. All three replay paths now go through one queued `\x1bc`, and the gate pins that no module blanks the terminal with a `clear()`+`reset()` pair.
**A renderer that stops painting now heals itself.** iOS discards scheduled `requestAnimationFrame` callbacks when a PWA backgrounds, and xterm's `RenderDebouncer` only clears its handle from inside that callback, so one drop leaves every later refresh a no-op while the buffer keeps updating correctly. Codeman has exactly one xterm for the whole page load, so a single backgrounding wedged it until a reload. A watchdog cancels the stale handle and forces a repaint.
**Every terminal capture carries a deadline that covers the response body.** `await fetch()` settles on headers, so clearing the timer there left the multi-megabyte `?full=1` body unbounded — measured at 4026ms under a 1000ms deadline. A capture that outruns its deadline during a tab switch now falls back to the bounded tail rather than leaving a blank pane, a mute session and a tab stuck reporting `aria-busy`.
**Output lost to a half-open WebSocket is reconciled.** Terminal output frames carry no sequence number, so a socket that dies while SSE stays up leaves a hole nothing replays. The session is marked and the next successful open repaints it, with the marker cleared only once a repaint has actually happened.
**The service worker's precache is generated by the build, and its cache key rotates per build.** The list was hand-maintained with pre-hash names, so every entry 404'd in production and the failure was swallowed (15 of 23 verified failing against a running instance); `caches.match` now passes `ignoreSearch: true`, without which no precached entry was reachable behind the build's `?v=` cache-bust. The old constant cache name meant `activate` never deleted anything, so assets from every past release accumulated forever.
Also: the crash trail is flattened and length-capped, so a server-controlled WebSocket close reason can no longer forge entries.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Run menu: the Gemini, Antigravity and OMP run buttons now show their own colours on every skin; they rendered in Claude blue on all skins except OG (#463). The CLI registry's `accent` values were also corrected to the colours the UI really paints, and a test now guards the stylesheet trap that caused it.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Terminal: five ways the browser terminal could silently stop being correct are fixed (#431, #464). The browser terminal and the PTY can no longer disagree about their width, which is what produced doubled lines and half-overwritten text ("text gets muffled sometimes"): there is now one function that sizes the terminal, and every resize is answered with the geometry the PTY really holds. A replay clear goes through the terminal's own queue, so bytes written just before it no longer fuse into the next snapshot. A renderer that stops painting after an iOS PWA is backgrounded heals itself instead of needing a reload. Every terminal capture has a deadline that also covers the response body, and a capture that runs out of time during a tab switch falls back to the bounded tail instead of leaving a blank pane. Output lost to a half-open WebSocket is repainted on the next successful open. The service worker's precache list is now generated by the build and its cache is rotated per build, so old releases' assets no longer pile up.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Docker: new `docker/Update-Codeman.sh` for the major-update path the docs used to describe by hand (#465). It rebuilds the image with `--no-cache` before taking the stack down, clears the build-artefact volumes, refuses to run when another checkout's Compose project already owns the same name, and then hands over to `Start-Codeman.sh`.
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Approvals: a session that is idle only because it is waiting on its own background work (Claude Code's `1 monitor` footer chip, or a Codex background terminal) no longer raises the yellow NEEDS YOU alert or a push (#473, fixes #468). Its idle item is opened already acknowledged, and the tab, the home screens and the rail show a small `watching` badge next to the state instead. The item still exists in the Approvals Inbox, and the TUI's pending count now leaves acknowledged items out.