The opencode readiness and marker measurements were made on Linux, not
on a Mac, and the docs now say so. A worker whose pane ends after it
wrote its answer file but before the marker matched answers 3 with no
answer: the rc 3 row now says the file may still be in its folder. The
example's input folder is named input, so the prompt reads cleanly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on opencode 1.3.0 in a private tmux socket, with a fake
OpenAI-compatible endpoint streaming the reply a character or two at a
time: the marker was on the screen, but the stripped client stream read
D, spinner, O, spinner, N, ... because opencode redraws its spinner
between the streamed pieces, so wait-output never matched it. sendmark
then waited its whole timeout and answered 2 for a reply that had
finished, with the answer file already written (opencode's write tool
wrote it without asking).
The marker wait now runs in rounds of at most 10 s, and between rounds
the answer file is a way out too: a non-empty regular file whose
checksum held still for one round is read, with matched false. The
marker stays the fast path. The answer-file wait after a marker no
longer spins when the exit wait gets no answer, and treats an ended
wait as the worker gone.
The same run measured the readiness mark: with a fresh home and a
custom provider, the first composer bar came with the composer and a
prompt pasted with its Enter at that moment was submitted, so the docs
say so instead of "not measured". The example keeps its input in a
subfolder, apart from the case's CLAUDE.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sendmark's comment and verbs.md 5.5 listed six external modes by name,
and test/agent-skill-mode-lists.test.ts refuses any partial list of them
(the one a ninth CLI would silently miss). Both now say what the class
is: a hook-less claude, a worker on the local model, and every external
agent mode but deepseek.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SKILL.md gets a subsection after the DeepSeek Harness workers: why to hand
work to the model on this Mac (bulk work with no per-token bill, files
that stay on the machine, a second opinion, a worker that needs no
network), the copy-paste block (local_model, spawn_worker local, sendmark,
delete_session), and the limits as the code has them: a person installs
the model, one request at a time, slower than the cloud, markers not
hooks, a spawn can start a runtime the person stopped, admin only and
experimental.
The description now names the local model, so "use the local model"
reaches the skill. Section 2 gains a row, the hook-less row points at
sendmark, and reference/verbs.md gets 5.16 with every local_model reason,
the spawn steps and the sendmark exit codes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved by keeping both sides:
- CLAUDE.md (Agent CLI): #694's opening (an HTTP client, the experimental
agent-session-routes behind agentCoordinationEnabled) with #708's clause
on the directory spawn and its two refusals.
- docs/api-reference.md (codeman agent): #708's spawn paragraph and --json
shape, with #694's inspect/stage/submit in the verb list and the guarded
endpoints named before "adds no route for the other verbs".
- src/cli-agent.ts (@fileoverview): #708's quick-start / directory spawn
routes and #694's experimental routes in one endpoint list; the two
node:fs/promises imports (#708's realpath+stat, #694's open+stat) merged
into one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stage now re-reads the pane after typing (every 50 ms, up to 1 s) and
issues a receipt only for a frame with the same generation, the input
revision its own write moved, and a composer hash equal to the
message's. Codex repaints 30 to 60 ms after the keys, so the first
capture was often stale and stage refused with the text already typed.
Every refusal after typing now says the message may be in the composer
and points at the Ctrl+U recovery. A visible-frame capture reads the
screen and the caret in one tmux invocation, with the old two calls as
the fallback.
Codex draws its suggestion popups above the composer and keeps the
footer, so a staged /stat verified and submit ran /status. Stage now
refuses a leading / or ! and a last word starting with @ or $ before
typing, and stage and submit refuse a frame with popup rows just above
the composer. The pattern and characters are registry data
(composerPopupLine, composerCommandPrefixes, composerCompletionSigils),
read from the codex 0.162.0 bottom_pane sources.
Submit waits until 250 ms after the staged keys: codex turns an Enter
within 120 ms of a typed burst into a newline. POST .../input answers
409 with its seq returned while a guarded submit holds the input lease,
instead of 200 for bytes it never wrote.
Also align the attribution memo comment with the code (a verified file
whose incremental scan fails is not re-walked, since the full walk
reads a superset), and list stage as a fromUser source in CLAUDE.md.
Gate inspect, stage and submit behind the synced agentCoordinationEnabled
setting (default OFF, App Settings > Cross-session features, zh-CN
strings). While off, all three routes answer 403 FORBIDDEN naming the
setting, checked before session lookup; the CLI verbs print it verbatim.
Remember the attributed Codex rollout per session and re-verify only its
head on later calls, rescanning only date directories not older than it
for a /new conflict. The first walk reads 1 KiB prefixes first, so a
store of 2,100+ unrelated rollouts still attributes.
Answer unexpected inspection errors with 422 OPERATION_FAILED and log
the session id and error code only.
Read the guarded composer glyph and footer from the CLI registry: codex
workDetect.promptGlyph and a new workDetect.composerFooterLine
capability, compiled through compileVersionRegex. Codex footer effort
words are one shared list with modelDetect.screenLine.
Fix the wiki API reference link, frame the versioning policy entry as a
case of the experimental rule, join the CLAUDE.md Agent CLI paragraph,
and update the changeset, skill reference and architecture notes.
`codeman agent spawn ""` (for example `spawn "$CASE"` with CASE unset)
used to launch a worker in the caller's cwd. Both case checks now test
`caseName !== undefined`, so an empty name reaches quick-start and is
refused by its schema as before.
Drop the `{ id }` fallback on the create answer: `POST /api/sessions`
answers `{ session: { id } }`, and the test fakes now use that shape.
Tests: an empty case name (unit and through the real argument parser
and HTTP transport), the claude `--ready` order create, /interactive,
wait-output on the created id, and a non-JSON failed start (id alone on
stdout, reason on stderr, exit 1).
Docs: the spawn mapping and directory-launch JSON shape in
docs/api-reference.md, the README and wiki `codeman agent` sections, the
cli-agent @fileoverview and the skill reference tables; reflow the new
SKILL.md paragraph (plugin mirror synced); the changeset notes the
local exit-4 refusal of an unknown mode for named cases.
- codeman agent post: accept text that starts with "-" (markdown from a
file), --json and -h stay options.
- DELETE /api/sessions/:id on a persisted-only row drops the inbox a detach
kept.
- inbox --wait: wait.ended is exit 3 only when the session list no longer has
the session; a restart or detach ending the wait is exit 2 (wait again).
- A read whose client hung up during ?wait= marks nothing seen.
- InboxPostSchema bound is 4x the byte cap, so every oversize message gets
the store's "post a file path" answer.
- Store: restore sorts oldest first; the TTL sweep checks every message,
not only the first.
- Docs: admins can act as any session of the receiver's owner (now tested
for admin and non-admin); a peek with ?wait= does not block; exit codes.
- Tests: partial PUT /api/settings keeps persistence on.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A coordinator can leave text for a worker without typing into its pane.
- Store (src/web/agent-inbox.ts): read until ack by id, seen only for the
receiver, 16 KiB/message, 512 KiB + 200 messages per inbox, full = 409
CONFLICT (never drops the oldest), 12 h TTL from each message's createdAt.
- Routes (routes/inbox-routes.ts): POST/GET /api/sessions/:id/inbox
(?wait= parks while nothing is unseen), POST .../inbox/ack (receiver
only, 403 otherwise), GET /api/agent-inbox/summary (owner-scoped counts,
not in toState()). Caller = resolveParentSessionId().
- Inbox waiters are the wait registry's third kind: same caps, ended by
cancelAll/cancelEverything.
- Opt-in snapshot (agentInboxPersistEnabled, synced, default off):
agent-inbox.json via dataPath(), 0600 tmp+rename, off deletes it, boot
prune only after a successful restore. Dropped on delete, kept on detach.
- CLI: codeman agent post | inbox [--wait] | ack, INBOX column in ls.
- waitCapacityResponse/abortOnClientHangUp moved to route-helpers.ts
unchanged.
- Docs: api-reference, README, CLAUDE.md, architecture-invariants, skill +
plugin mirror.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A prompt made only of blank lines joined to nothing, and sendwait then sent
a bare Enter onto whatever sat on the worker's composer. It now refuses
with "sendwait: empty prompt" (the test fails without the check).
reference/verbs.md still said sendwait takes one line and a multi-line
prompt can sit unsent; it now says sendwait joins the lines with a space,
which loses their layout, so long multi-line content belongs in a file.
Plugin mirror regenerated.
Co-Authored-By: Christian Haberl <116439789+christianhaberl@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
verbs.md section 5.3 (both copies): sendwait's Enter loop and the server's submit
check cannot reliably recognise a multi-line paste, so join the lines or use a file
(section 5.9). docs/tui.md gives the same reason instead of the composer one.
Co-Authored-By: Christian Haberl <116439789+christianhaberl@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
The tab layout's 512 refs also count pinned sessions kept as stopped records, so
the ceiling holds at the default MAX_WEBVIEWS and with no pinned closed sessions.
SKILL.md, verbs.md (both copies) and the cron guide no longer give a fixed 50/25.
Co-Authored-By: Christian Haberl <116439789+christianhaberl@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
- sendInput's docstring said all text without `\r` is typed; that now holds
for single-line text only.
- The skill's big-input section still listed "the newline stripping" among
the hazards a workspace file avoids; it is the unconfirmed multi-line
paste now. Rewrapped, mirrored in plugins/codeman.
- The mobile composer's hand-back comment said the prompt transport strips
newlines. The hand-back writes to the terminal prompt, whose text reaches
the PTY as typed input, where a newline is a key; the behaviour (a
multi-line draft stays parked) is unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The first commit left the old rule standing in places: the gotchas list in
extending-codeman.md still said newlines were stripped and that
"echo A\necho B\r" ran `echo Aecho B`, and so did both READMEs and the
skill's big-input section. Those now describe the paste.
Newlines are only kept on the mux path, so the docs that describe the paste
say "with useMux": without it a multi-line body is not a plain prompt and is
written straight into the pane.
Cron keeps rejecting multi-line prompts, but its docs and the code comments
in cron-service.ts, schemas.ts and respawn-controller.ts gave a reason this
change removes (writeViaMux joining lines, a newline submitting in Ink). The
reason that remains is the submit check, which cannot recognise a
multi-line paste in the composer, and for cron's paste mode the newlines
reaching the CLI as keys.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TmuxManager.sendInput() removed every `\n` before the text reached tmux, so a
multi-line prompt sent with `useMux` (or a multi-line respawn prompt) arrived as
one line with its lines run together: `"echo A\necho B\r"` ran `echo Aecho B`.
The text half of a submit has gone in as one bracketed paste
(buildPasteTextCommands) since the Codex paste-burst fix, and inside the paste
markers Claude Code and Codex insert a pasted line break into the composer
instead of submitting. So the `\n` is now kept (`\r\n` loses only its `\r`) and
the existing paste carries the lines; the Enter stays a separate tmux call.
Multi-line text without an Enter is pasted too rather than typed with
`send-keys -l`, where every line break would reach the pane as a key.
Single-line input is unchanged.
The test-mode gate moves from the top of sendInput() into inputRunner(), as
readPaneRows() does, so a test subclass can record the commands sendInput()
would run instead of pinning the branch shapes on the source.
SubmitVerifier still compares the composer's last glyph line with the prompt
minus its newlines, so it does not reliably recognise a multi-line paste and
may not re-press an Enter that Claude Code ignored. `agent send` and the TUI
therefore keep refusing or joining multi-line input; the docs that described
the stripping now describe the paste and that limit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GitHub Copilot CLI (`copilot`) becomes a run mode: Run menu, welcome
screen, cron and CLI management once it is installed, tmux-backed like
the other agent CLIs, working/idle and the model read off its footer,
launch options through `copilotConfig` (model, resume, continue, the
multi-user-clamped allowAll), Custom Model Endpoints via Copilot's BYOK
variables, MCP sync of its mcp-config.json, a status probe, and the
`codeman agent` verbs (spawn waits for its composer, read returns its
last answer).
The plan-usage quota row that earlier rounds carried was split out on
review and is not part of this merge.
Merge-time commits on top: the wider COPILOT_* clamp and the zh-CN
README rows (maintainer), the Docker sign-in docs, divider rows inside
an answer kept by `agent read`, route tests for the screen-read
last-response branch, and doc drift (Past Sessions, wheelForward JSDoc,
COPILOT_/OMP_ in the env prefix lists).
Verified before merge on Copilot CLI 1.0.98: injected keystrokes,
bracketed paste and Enter reach the TUI in tmux (github/copilot-cli#4180
does not reproduce), a shell tool call completes inside tmux (#4223 does
not reproduce), and a session driven through this branch answered over
send-and-wait.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add pinCliSessionName (synced, default OFF), re-read at each spawn and respawn.
Off keeps the #457 behaviour; on pins every non-blank tab name as --name.
Update the docs, JSDoc and changeset that stated the old rule.
Copilot (`copilot`, npm @github/copilot) as a stock CLI entry with the same launch options as the other agent CLIs: model (--model), resume (--resume), continue (--continue) and allow-all (--yolo, clamped off for non-granted multi-user owners). Adds a resolver and GET /api/copilot/status probe, the agent-image package, Run-menu/mobile/logo entries, skill docs and tests.
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>
- hooks-config: a probe the bulk cap refused gets ONE bounded re-probe past the
cap (probeBeforeTouching), and whatever is still unknown is skipped. The
per-spawn hook and statusLine helpers used to fall back to an unbounded
lstat/readFile there, which on a dead workspace never settled and could take
the last threadpool workers (and hang the boot hook sweep). New test: cap
engaged, stat/lstat/readFile hanging on two more paths; both helpers return.
- describeUnknownPath()/unknownPathReason(): POST /api/sessions, quick-start and
GET /api/cases/:name now say a folder was not checked (other mounts are still
not answering) instead of blaming a healthy folder at the stall ceiling.
errorCodes unchanged.
- #535 x #516: Create in a custom folder probes the parent through the bounded
probe before realpath/stat/lstat/readdir touch it; an unknown parent is 422
OPERATION_FAILED (UNREACHABLE) within the probe timeout. New test.
- Docs: MAX_STALLED default is 2 (follows UV_THREADPOOL_SIZE), CaseInfo
.unreachable covers a refused probe, the boot sweep skips an unanswering
workspace, a CLAUDE.md gotcha for bounded probes, verbs.md documents the 422
(plugin mirror synced), api-reference documents the custom-folder 422.
- Tests: the launcher case-lookup describe is no longer nested in the Grok
block, and the cap-below-ceiling test no longer depends on an inherited
UV_THREADPOOL_SIZE / CODEMAN_PATH_PROBE_MAX_STALLED.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(sessions): stop pinning the w1-myapp placeholder as Claude's /resume title
Local claude spawns passed the tab name as `--name`. That flag is not only the
cross-session peer name: it is also the prompt-box label, the `/resume` picker
entry and the terminal title, and a pinned title stops Claude generating its own
(`customTitle ?? aiTitle`). So every conversation of a case was listed in
`/resume` as the same `w1-myapp`, and none of them got a generated title. On one
workspace, 34 of 34 conversations spawned with `--name` had no ai-title, while
every conversation spawned without it had one.
Only a name the user chose is pinned now: `Session.cliPinnedName` is the name
when `nameSource === 'manual'`, carried to the builders as a separate `cliName`
so the tab/mux name is untouched. Placeholder and auto names let Claude title
the conversation again.
A rename in Codeman also reaches `/resume`: the new name is appended to the
conversation's transcript as the `custom-title` row `/rename` writes (never
creating the file, never writing an empty title). For a pane spawned without
`--name` this holds immediately; a pane spawned with one re-appends its own
title each turn, so there the new name holds from the next spawn.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sessions): skip no-op renames and docker sessions when syncing the /resume title
A same-name PUT (the Session Options field saves on blur and recomposes the
unchanged placeholder) no longer flips nameSource to manual or appends a
custom-title row, and docker sessions skip the host transcript scan since their
transcript lives in the container. The skill pages no longer use a w<N>- name
as the peer-name example, and the changeset notes the re-append caveat.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs: record that nameSource decides --name and renames reach /resume
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: codeman-local <codeman@local>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
With the repo root as the plugin root, `claude plugin install codeman@codeman`
copied the whole checkout into its cache and, because that root carries a
package.json, ran an npm install there: 832 MB, 511 packages and this repo's
postinstall build on every installer's machine (measured from a clean worktree
of the previous commit). A plugin root must be a directory without one.
The plugin is now `plugins/codeman/`: its manifest, a README, and a MIRROR of
`skills/codeman/`. A mirror rather than a symlink because the install copies
the plugin directory and a link pointing outside it would dangle; a mirror
rather than the source because every install path, injector and doc already
names `skills/codeman/`. `scripts/sync-plugin.mjs` (replacing
sync-plugin-version.mjs) mirrors the skill and syncs both manifest versions
inside `version-packages`; `test/plugin-manifest.test.ts` pins byte-identity,
the versions, the absence of a package.json in the plugin root and that the
repo root `.claude-plugin/` holds only the marketplace manifest.
`claude plugin validate --strict` now passes for both the plugin and the repo
root. Install commands are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>