jcode (1jehuang/jcode) joins the CLI registry as the `jcode` run mode:
Run menu, phone overview, TUI, quick-start and POST /api/sessions
(`jcodeConfig: { model?, resumeSessionId? }`), local, SSH and Docker
(dedicated agent-image layer), plus codeman doctor and install.sh
through the generated catalogue. Measured on a live jcode 0.98.1 pane.
- Work detection reads jcode's braille status row and `<n>>` composer;
alt screen with mouse strip and PageUp/PageDown paging like opencode.
- Every jcode TUI is a client of one shared per-user daemon whose tools
run with the daemon's env, so no agent skill injection and no JCODE_*
envOverrides prefix.
- New `capabilities.sharedDaemon`: a session kill spares descendants
outside the pane's process group, so closing the tab that started the
daemon no longer cuts every other jcode session off (measured).
- last-response answers an honest empty for an external CLI with no
transcript reader that the pane segmenter cannot read (jcode alone).
- GET /api/jcode/status via the generic registry resolver.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- docs/api-reference.md: a non-admin's GET /api/settings omits
voiceSettings.apiKey, caseOrder and lastUsedCase; a new "System
operations" table for the admin-only routes and the scoped
POST /api/auth/revoke; the error-code table now lists FORBIDDEN and the
other multi-user codes (PASSWORD_CHANGE_REQUIRED, USER_NOT_FOUND,
USER_EXISTS, LAST_ADMIN).
- docs/wiki/Multi-User-Mode.md: what a regular user reads, that their run
mode and respawn presets stay theirs on the device, the slow sign-in
case, and the admin-only system operations.
- docs/multi-user-plan.md: Settings and System ops rows marked implemented.
- CLAUDE.md: the Multi-user mode paragraph names the new pieces and tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
api-reference gains a Settings section (GET/PUT /api/settings, the model
config, the case order and PUT /api/config, with the 403 FORBIDDEN a non-admin
gets in multi-user mode). The Multi-User Mode wiki page explains what a regular
user's App Settings save does now, Settings-Reference links it, the plan's
Settings row is marked implemented, and CLAUDE.md notes the gate for the
Multi-user paragraph and for anyone adding a settings write.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
feat(codex): raise codex approval, question and trust dialogs as BLOCKED
Conflict resolved against #634 in src/session.ts _probePaneWorking():
#624's assignment (a dialog on screen is never work) is kept, and #634's
trust-dialog verdict block (log the hold once, remember the reading)
follows it, before _readWatching().
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The wiki said .codeman-uploads/ is the one path "a local agent, a container
and a remote host all see the same way", two sentences after saying uploads
are refused for remote (SSH) sessions. Only a local agent and a Docker case
(workspace bind-mounted at the same absolute path) share it, which is why the
route refuses remote sessions; say that.
The same paragraph said every image goes in after a re-encode on the device,
but _normalizeImageForUpload passes a GIF through untouched to keep its
frames.
CLAUDE.md and a test comment described the image fallback as behaving
"as before 1.36", a release that never existed (1.35.0 was followed by
1.40.0). It means the image path as it was before this change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The dialog's own prompt glyph armed idle confirmation and the quiet dialog passed it, so a fresh case reported idle about 4 s in. An agent that waited for idle and then prompted typed into the dialog, and its Enter confirmed No, exit and killed the pane.
The scan must schedule its own next read rather than wait for PTY output, but a call inside the throttle was simply dropped. The dialog's own paint is often the last output a launching pane produces and lands inside that throttle, so the session sat on the dialog with no keystroke ever sent. A stopped session no longer scans, since the kill's own output kept it answering a dialog in a dead pane.
Claude Code's and Codex's selection dialogs ignore a bracketed paste, so
after the previous commit a pasted `3` + Enter confirmed the highlighted
option instead of option 3. sendInput() now types a single character with
`send-keys -l` as master did (submitsAsKeys()). One character cannot form
the burst Codex reads as a paste, so the Codex fix is unaffected.
The trailing-`;` escape moves into escapeTmuxTrailingSemicolon() and now
also covers every `send-keys -l` call in sendInput() and
sendInputToPane(); tmux 3.5a types `x;` for `send-keys -l 'x\;'`.
Docs: the @fileoverview lines and orchestrator-loop-architecture.md still
described `send-keys -l`; CLAUDE.md, architecture-invariants.md and
cron-guide.md name the single-character exception.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
sendInput() typed the text of a text+Enter pair with `send-keys -l` and
sent Enter 50 ms later. Codex detects an unmarked paste from a fast
character burst and folds an Enter inside that window into the paste as a
newline, so the prompt stays in the composer unsubmitted. Measured with
codex-cli 0.162.1 in a throwaway tmux pane: 1505 chars stuck 3/3 as
`[Pasted Content 1506 chars]`, a 98-char line stuck 1/4. SubmitVerifier
re-presses Enter for the short case, but it cannot match the long one: the
composer shows the placeholder instead of the prompt's head.
The text now goes in via set-buffer + `paste-buffer -p -d` (markers only
when the pane requested bracketed paste), followed by the separate Enter as
before. Same pane, same texts: 4/4 and 3/3 submitted. Each send gets its
own named buffer; a paste that fails (pane or server gone) deletes it,
since tmux never trims named buffers on its own.
Also escapes a trailing `;` in the text, which tmux treats as a command
separator (`send-keys -l 'x;'` typed `x`, a lone `;` typed nothing).
sendInputToPane() has no callers in src/ and is left as it was.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Document what the two previous commits changed for users and for whoever
adds the field to another CLI:
- docs/cli-registry.md: the `blockedLine`/`blockedLines` fields, how codex's
pattern was measured, and the three rules any declaring CLI keeps (end on
chrome only the CLI draws, alert only; raise on one reading, clear on two;
hooks win).
- docs/architecture-invariants.md: a "Screen-read blocked dialogs (codex)"
entry, plus the wait-signal, idle-detection, watching and Approvals Inbox
entries it changes.
- docs/api-reference.md, docs/extending-codeman.md and the wiki (Agent CLIs,
Notifications and Approvals, Hooks, HTTP API, Driving Codeman From An
Agent): `blocked` now fires for codex, `stop` still does not, codex
approvals are alert-only cards answered in the terminal.
- CLAUDE.md: `stop` and `blocked` are no longer one rule, the config-regex
list gains `blockedLine`, and a short pointer that a codex dialog match is
only ever an alert.
- The codeman skill's signal table and verbs note, mirrored into
plugins/codeman by scripts/sync-plugin.mjs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Documents the worktree endpoints and quick-start's worktree field in
docs/api-reference.md, adds a Git Worktrees page to the wiki (sidebar,
Home index, a pointer from Core Concepts where sessions sharing one
case folder is explained), and records the rules that keep the feature
safe in docs/architecture-invariants.md with a short CLAUDE.md pointer:
only the managed .claude/worktrees path is ever touched, git never runs
over a Docker workspace, removal is never forced and branch delete is
-d only, and sessions start through the existing create paths.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Attach button, the Compose and paste-dialog pickers, terminal drop and the
clipboard now take documents beside images and videos: the office formats
pdf/docx/pptx/xlsx/zip and the text family txt/md/csv/tsv/json/log/yaml/yml/xml.
A document is admitted by its name's extension (the MIME a phone attaches to a
.md is anyone's guess), buffered under the same 50MB image cap, and judged on
its bytes by the same fail-closed sniffer: a magic-number signature for the
binary formats, UTF-8 with no NUL byte for text. It is saved under a sanitized
stem of its name, so Q3 report.pdf is findable as paste-<ts>-<hex>-Q3-report.pdf.
sh/py/js/html are refused by name with a clear 400. That is a scope choice, not
a security property: file-raw already serves non-media text inert, so this keeps
the server from storing what it would serve or an agent could run, and html and
svg are the ones deliberately kept out. One client classifier (_promptAttachKind)
now returns image, video or document, and the server's dispatch mirrors it: an
image MIME wins whatever the name says, a document extension takes the document
branch, and a foreign extension with no image MIME is refused only after the
image sniff fails, so nothing the image path accepted by its bytes is lost.
Only GIFs that a public page embeds stay in docs/images/: the five README
GIFs plus subagent-demo-20260724.gif, which the published "mission control"
article hotlinks from master. Seven GIFs nothing embeds leave the tree
(their masters are kept in the maintainer's local media library).
.gitignore now blocks *.mp4/*.webm/*.mov/*.m4v/*.mkv/*.avi/*.gif outside
docs/images/*.gif (and the promo branch's Remotion inputs), so a video can
only be committed with an explicit `git add -f`.
docs/media-assets.md records the rule: what is tracked and where it is
embedded, website media (pr/website/assets, deployed separately), release
clips on the design-assets branch, and the local library layout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The root CLAUDE.md had grown to 202 KB, all of it loaded into every
session. This lands the split drafted on 2026-10-10 (it sat uncommitted
in the shared checkout through two releases), redone on top of today's
master so nothing that landed since is lost:
- src/web/CLAUDE.md: the frontend invariants (every src/web/public/ UI
subsystem, the module notes, the XLSX preview limits, the z-index
stack). Claude Code loads it when a file under src/web/ is read; the
root file tells sessions to Read it before editing src/web/public/.
Its link targets are relative to the file so they resolve on GitHub.
- docs/releasing.md: the COM procedure (now nine steps, with the MCP
registry publish), behind a pointer in the root COM section. The
Version line stays in the root file.
- Root CLAUDE.md (130 KB): everything else, plus the split's trims: the
Core Files table, the reference-only rows of the Additional Commands
table, Data Flow and the respawn presets, whose rules already live in
the root's other paragraphs (the "Read before touching a subsystem"
line keeps the table's doc pointers).
Every other paragraph of master's CLAUDE.md lands in exactly one of the
three files, checked line by line; the paragraphs master changed since
the draft (tile grid, OSC 52, prompt uploads, file-path links and the
HTML view, markdown preview, z-index, MCP server, agent CLI and others)
are in their current form. Both new files join CLAUDE.md in
.prettierignore. Four code comments and docs/architecture-invariants.md
now point at src/web/CLAUDE.md for the moved sections.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`codeman mcp` is listed in the official MCP registry as
io.github.Ark0N/codeman (first published for 1.41.1). The registry does
not follow npm, so without a step in the COM procedure the listing stays
on the version it was last published with. The step also records the
two traps hit on the first publish: the Snap Store mcp-publisher (1.1.0)
rejects the current server.json schema with a false "deprecated schema
detected", and older publishers save live login tokens into the current
directory as .mcpregistry_* files.
The announcement step becomes step 9.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One consolidated patch changeset for the 24 PRs in this release (the PRs'
own changesets folded in), CHANGELOG, package and plugin manifest versions,
and the CLAUDE.md version line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts resolved against #604, #606 and #601:
- constants.js: the rooted link pattern takes html-md's `markdown` and
`html?` with #606's m4v/ogv; #604's relative pattern gets the same list
(link-provider-regex pins the two equal); #604's findFilePathLinks /
folder helpers and html-md's rendersAsPage() are both kept, and
window.CodemanFilePaths exports all of them.
- terminal-ui.js: #604's resolved `target` (relative, ~/ and folder links)
now also goes through rendersAsPage(), so every link kind routes .html to
the page preview; .md routes there through FILE_PREVIEW_EXTENSIONS.
- CLAUDE.md, api-reference, architecture-invariants: both sides' text kept.
- test/folder-grid-ui.test.ts (#604) stubs rendersAsPage() like the other
panels-ui harnesses.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts resolved against #605 (and #602's test mock):
- CLAUDE.md Prompt uploads: #606's rewrite is kept and #605's bounded
workspace probe sentence follows the remote (SSH) refusal.
- test/routes/session-routes.test.ts: the remote-pane-history and
bounded-path-probe mocks are kept beside #606's _resetPasteRateBuckets
import, and both PRs' paste-image tests are kept.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflict resolved in test/routes/session-routes.test.ts: #602's
remote-pane-history mock and #605's bounded-path-probe mock are both kept.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When navigator.clipboard is missing (plain HTTP, install.sh's LAN option) or
refuses the write (an unfocused window), _copyText falls back to a hidden
textarea whose select() takes focus, and removing it left focus on <body>:
the next keystrokes after Claude Code's /copy went nowhere. The handler now
restores the element focused before the copy, as Auto Copy does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflict resolved in docs/api-reference.md: #607's prompt cache section and
#528's tabGroupId section are both kept.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts resolved against #583 and #588:
- One history-depth field: #583's `historyLines` / `capturedHistoryLines`
(reported as `paneHistoryLines`) is kept, and #601's lines cut now reads it
instead of a second `capturedHistorySize`. #601's `alternateOn` /
`capturedAlternateOn` joins it, and the one display-message format string
ends `#{history_size} #{alternate_on}`.
- selectSession keeps #588's full=1 switch for panes with no scrollback, gated
off remote and Docker sessions, and #601's SHELL_LOAD_QUERY for shells.
- computeHistoryTruncationNotice names a line count when paneHistoryLines is
known (#583) and otherwise uses #601's remainder wording.
- docs: api-reference lists both historyLinesCut and paneHistoryLines; the
notice invariant keys on history_size while the shell's automatic load is
the one place alternate_on decides.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner decision 2026-10-13 (#547): a rendered .html page behaves like a
file opened from disk in a browser. It may load scripts, styles, fonts,
images and media from any host and make fetch, XHR, WebSocket, beacon
and form requests to any host.
The /html-view Content-Security-Policy is therefore the `sandbox`
directive alone (HTML_VIEW_CSP, now exported and documented): the same
sandbox flags as before, never `allow-same-origin`, and no fetch
directive. It replaces Codeman's own `default-src 'self'` policy on these
responses. Everything that confines a page stays: the opaque origin (no
Codeman cookies or document, API writes with `Origin: null` refused, API
reads not readable cross-origin), and the capability serving only the
page's own folder under every path, symlink, dot, extension and
confinement rule.
A new test builds the route behind registerSecurityHeaders, as in
production, and pins the exact header: one `sandbox` directive with the
current flags, no `allow-same-origin`, no `-src` directive. The security
architecture, architecture invariants, API reference, CLAUDE.md and the
Working With Files wiki page now say what a page can do (reach the
network, read its own folder) and cannot do (Codeman's origin, cookies,
API, other folders), and that this is the owner's decision.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An in-workspace .md path clicked in the terminal went to the log viewer
(tail -f, a "streaming" header over raw text), because md was kept out
of FILE_PREVIEW_EXTENSIONS on purpose; every other surface already
rendered it. Owner decision 2026-10-13: markdown is a document to read,
so md and markdown join FILE_PREVIEW_EXTENSIONS and a click or tap opens
the file preview, rendered by default with the MD pill for source. The
main pane, every grid tile and the split's Pane B share the link
provider, so all of them route the same way. Other in-workspace text
(.log, .txt, .json, code) keeps the log viewer and its live follow.
FILE_PATH_LINK_PATTERN now also links `.markdown` and `.htm`, which
were not clickable at all.
Tests: file-preview-markdown.test.ts now asserts md and markdown ARE
routed to the preview (and plain text is not); terminal-touch-tap pins a
tapped .md, .markdown and .html opening the preview while .log, .txt and
.json open the log viewer, and a tile-registered provider routing the
same way against the tile's own session. Docs: CLAUDE.md, the
architecture invariants and the wiki (Working With Files, Mobile Guide).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A rendered page's own <video> or <audio> could not seek, because the
route streamed a plain 200 without Accept-Ranges. sendFileBody() and
localFileSource() are now exported from file-routes.ts and the serving
route uses them, so it advertises Accept-Ranges and answers a Range with
206 + Content-Range (or 416), exactly like file-raw, the attachment raw
route and /api/download. The body sender hijacks the reply and copies
every header set before it (the sandbox CSP, CORS, no-store), which the
new test pins on a 206.
Media types stay in ASSET_TYPES: with Range support they work, and they
are served under the same sandbox CSP as everything else.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts resolved:
- CLAUDE.md: kept master's Prompt uploads paragraph and its longer
File-path links paragraph (XLSX admission limits), and inserted the
PR's html-view sentence after the download-only rule.
- src/web/public/terminal-ui.js: kept master's tile-aware
getSessionId() in the shared file-path link provider and added the
PR's .html routing to the file preview on top of it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
xterm parses write() data after chunkedTerminalWrite has already cleared
_isLoadingBuffer, so the main terminal's replay gate never held: a tab switch
or reconnect re-copied the newest OSC 52 in the replayed history. Strip every
OSC 52 from the replay beside the DEC 2026 markers (OSC52_STRIP_RE). Tiles
keep their own _bufferLoading gate, which holds there.
One Claude Code copy arrives up to three times (its own OSC 52, tmux
load-buffer -w, set-clipboard forwarding), so identical text within
OSC52_DEDUPE_MS is dropped. The manual-copy fallback opens only in the
focused tab, so a background tab on the same session stays quiet.
The replay tests now drive the real chunkedTerminalWrite into
@xterm/headless and fail without the strip. Test harnesses that supply
DEC_SYNC_STRIP_RE by hand get OSC52_STRIP_RE too, and CLAUDE.md records
the rules next to Auto Copy.
Agents pick tools from MCP clients and registries, and Codeman had no MCP
surface: an assistant in Claude Desktop, Cursor, Codex or Gemini CLI could
not start or drive Codeman sessions without hand-written HTTP calls.
`codeman mcp` is a stdio MCP server and a CLIENT of the existing HTTP API
(it reuses codeman agent's transport and guards; no new route). Seven
tools: list_sessions, start_session, send_prompt, wait_for_session,
read_output, interrupt_session, delete_session.
- Dual-era: answers the legacy initialize handshake (2024-11-05 to
2025-11-25) and the 2026-07-28 revision (per-request _meta,
server/discover, resultType, and the ttlMs/cacheScope hints a modern
tools/list must carry: Claude Code 2.1.296 drops every tool without
them).
- Never guesses a server: the URL comes from --url or CODEMAN_API_URL.
Unconfigured, it still lists its tools and every call says how to
configure it.
- Prompting, interrupting and deleting are limited to sessions the process
started (or, inside a Codeman pane, that session's own workers) unless
--allow-any-session; never its own session. Control bytes are refused
before any request.
- Waits default to 45 s (Codex's MCP tool timeout is 60 s), max 600 s.
- read_output returns text only: a client that prefers structuredContent
otherwise showed the model metadata instead of the output.
- config/mcp-registry/server.json and package.json mcpName prepare the
official MCP registry listing; sync-plugin.mjs keeps its version.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The agent skill only reached Claude Code: injection wrote
.claude/skills/codeman, which no other CLI reads, and the per-session
preamble was seeded for claude alone. A Codex, Gemini, OpenCode,
Antigravity, Grok, DeepSeek or OMP session inside Codeman never learned it
could start and drive workers.
- Registry field capabilities.agentSkillDir (a closed enum, since
clis.json can set it and it decides where the server writes) names the
skills directory each CLI scans. Claude keeps .claude/skills; those seven
CLIs get the same skill in <case>/.agents/skills/codeman (each measured to
scan it; Claude Code does not, so it never sees two copies). pi gets none:
a project skill makes pi ask "Trust project folder?" at startup.
- The preamble is seeded for every local session whose CLI takes the
skill; nothing in it is claude-specific.
- The skill text is CLI-neutral and names the two CLI-side failures to
report instead of retrying: a sandbox that blocks loopback (Codex's
default), and commands that do not carry this session's environment
(Codex 0.162 runs them in a shared app-server daemon).
- POST /api/sessions injects after the launch pre-flight, so a CLI that
cannot start leaves nothing in the workspace.
- codeman skill install gains --mode; uninstall --case removes the copy
from every directory the registry can write.
- The settings toggle moves to Agents & CLIs > All agent CLIs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A Docker case is a local session for the file routes, but `~` inside the
container is the container's own seeded home. Expanding `~/x.png` on the host
opened a different file than the one the agent printed. The attachment POST
and POST …/folders now answer 400 for `~/` when the session has `docker` set
(absolute paths are unaffected: the workspace is a bind mount).
Docs: the resolved-target gate, the route-parameter limit, the listing join
rule and the Docker `~/` refusal in architecture-invariants.md,
security-architecture.md and CLAUDE.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude Code's tracker nulls recache_tokens_if_cold at a compaction or a tool-result clearing and only counts the expected rebuild at the next request, which also closes the window, so no report carried both and the old rule never fired. Stamp on the first warm null report after one that carried a value, carry it through idle re-reports and drop it at the next request; the readout now says "Context compacted at HH:MM" since a clearing opens the same window.
Also persist a changed report so a crash restores the latest one, rename the session field to statusLineExporter (statusLineTelemetry is the registry capability) and document its values, word the stake as re-cached, move the readout block above the SSE staleness heading, and drop keep-warm mentions until it lands.
- Multi-user: a non-admin's folder must resolve inside their own userSpacePath
or the session's workingDir (folderAllowedForUser, the path picker's rule),
checked on the resolved folder before it is read and again on each served
file after symlink resolution; 403 otherwise.
- listFolder probes the local folder with probePathKind({ pastCap: true })
first (unknown -> 503 via describeUnknownPath, never 404), then async
fs.realpath; resolveFolderFile uses probePathKind instead of fs.stat.
- The guard runs on the realpath before readdir.
- FOLDER_PATH_LINK_PATTERN ends a folder at ':' (quadratic `://` rescan on
colon runs; PATH=/a:/b linked as one folder).
- Load counters drop stale preview / folder-grid responses; closing the grid
invalidates a listing still in flight.
- validFolderFileName refuses a leading dot; the remote listing matches
extensions in any case.
- Docs: folder grid in the z-index lists; multi-user rule and bounded probe in
the invariants and security docs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review follow-ups for loading a shell from tmux's rendered history.
tmux adds no history while a pane is in the alternate screen, and a
nested tmux client (remote SSH and Docker cases, or tmux run by hand)
holds its pane there, so a full capture of it is one screen. The
display-message query that reads #{history_size} now also reads
#{alternate_on} (PaneCaptureOptions.capturedAlternateOn), and a request
carrying `alt=recording` (only SHELL_LOAD_QUERY sends it) is answered
the old way on such a pane: the visible frame over the tailed byte
recording. Agents, scroll pulls and Load full history are unchanged.
alternate_on cannot tell a nested tmux from vim or less, so a local
shell with one of those open takes the same fallback while it is open.
A full capture without `lines=` was reported as a 'tail' cut whenever
the pane held more than the configured ceiling (possible on tmux before
3.7), offering a Load full history that could never return more. The
cut is now reported only for a client bound below the ceiling.
The response carries `historyLinesCut` when that bound left lines in
tmux, and the banner then says earlier output may be retained instead
of naming the bytes a later tail cut, which undercount what tmux holds.
A bounded grid tile's shell load sends its own `lines=` (via
SHELL_LOAD_BASE_QUERY). CLAUDE.md, docs/architecture-invariants.md and
docs/api-reference.md describe the shell load, the cut report and the
fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One conflict, in i18n.js SKIP_SELECTOR: master added '.tab-name' itself
(the same fix this branch made for zh-CN tab names), so master's line is
kept and this branch's duplicate comment dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md key pattern and architecture-invariants (File-path links) get
the new path forms (`findFilePathLinks`, `~/` expanded server-side,
`terminalPathLine` join rules) and the folder grid (grant + bare-name
routes, own registry, z-order, captured keys); remote-sessions lists the
folder routes for a remote case; security-architecture states the trust
level of a folder grant.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The paste-image route wrote under session.workingDir via ensureUploadDir
with no bounded probe, so an upload onto a stalled network mount held one
of libuv's few threadpool workers until the mount answered. Probe the
workspace with probePathKind past the stall cap before the first disk
touch and refuse an unknown mount through describeUnknownPath, the way
POST /api/sessions already does.
A session or web-tab ref in the owner tab layout may now carry
`focus: true`. "Add to Focus" in a row's menu (right-click on a web tab)
sets it; the vertical rail then draws a shortcut to that tab in a pinned,
collapsible "Focus" section at its top, while the row itself stays where
it is. A click on a shortcut selects its tab, a right-click or the menu
key offers "Remove from Focus".
Model: `TabRef.focus?: true`, parsed strictly (only `true` or absent) and
carried through normalization, every pure edit and the legacy-order
projection; `setTabFocus()` is the pure helper. TabLayoutService needed
no change and no route was added: the flag rides the existing
`PUT /api/tab-layout`. Browser: `copyRef`/`normalizeLayout` keep `focus`
the way they keep `placement` (the browser writes whole layouts, so a
dropped field would be erased on every save), `setFocus` is a named
operation through `createEditCoordinator` and replays on a 409, and
`focusRefs()` (also on `project()`) lists what the rail can draw.
The section is its own element in #tabRail above #sessionTabs, so it
works on the flat rail (no named groups) as well as the grouped one, and
shortcuts are never rows: no Alt+N badge, no tree item, no lineage
anchor, no drag target. It is a list with one tab stop and
Up/Down/Home/End between shortcuts. Collapse is per-device localStorage
under its own key. A shortcut whose session closed or whose web tab is
not open drops out; the server already prunes deleted refs. zh-CN
strings added.
A phone-only toolbar Attach button hands the agent a photo or a video with the
keyboard down. It owns its own file input, positioned over the button so iOS
anchors the picker popover to it; the picked paths land on the prompt as unsent
text, so Compose adopts them together with anything already typed and Use
terminal keyboard hands a single-line draft back the other way.
Videos take a second path on the same route: ?kind=video streams the file to
disk under MAX_PASTE_VIDEO_BYTES (1GB, env CODEMAN_MAX_PASTE_VIDEO_BYTES)
instead of buffering it, sniffs the container from the first bytes as they
arrive, drains a mislabeled body unwritten and unlinks on any failure. It draws
on its own 3/min per-(IP, session) rate bucket beside the image path's 30/min:
at 1GB each, 30/min would be 30GB of disk a minute. Without the flag the route
is byte-identical to the image-only version, same 50MB cap and same bucket.
One client classifier, _promptAttachKind (MIME first, then the name's
extension), drives every pick and paste surface: the Attach button, terminal
drop, the tile grid's drop, the Compose and paste-dialog pickers and the
clipboard handlers. A pick that lands while the tile grid owns the terminal
goes to the PTY rather than the parked main-terminal overlay.