Commit Graph
325 Commits
Author SHA1 Message Date
Devvyn 7a3629e890 fix(picker): do not offer New folder inside a cases directory
A folder made in a cases directory is already a case (no scaffold), which Link
Existing then refuses by name and which deleting would take the linked folder
with. The route refuses a cases directory as the parent (the shared one and the
caller's own), a leading tilde in the name, and maps a vanished parent to 404.
Docs now say every name it accepts is one Link Existing accepts, and the wiki
describes the button.
2026-10-31 21:12:46 +08:00
Codeman maintainer c04fa97f78 chore: replace the em-dashes this release added with plain punctuation
The merged PRs (mostly #714's git fetch) added about 120 em-dashes in
comments, docs, test names and three settings strings. Each one is now a
comma, colon, semicolon or parentheses. The English settings strings in
index.html and their i18n.js keys change together, and the zh-CN value
for the fetch interval uses full-width parentheses instead of a double
dash. The one left is the regex in constants.js that matches a dash
typed as a word, which is behaviour, not punctuation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:11:19 +01:00
Codeman maintainer 93691e6096 fix: bring the skill, plugin, README and API docs in line with the merged agent behaviour (release review)
The self-gate now says the skill reads saved Codex conversations offline outside a session; the staging
reference says only tagged input gets 409 during a guarded submit (the rest is held), and the API
reference notes a managed write past a full hold queue falls back to a direct write. Also: missing
reference rows, agent --help naming inspect/stage/submit, the tunnel opt-in in Remote Access, MCP wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:04:14 +01:00
Codeman maintainer 0f1fca6ea0 Merge pull request #712: feat(tabs): start a session beside any tab from an opt-in + in its actions 2026-10-27 02:24:17 +01:00
Codeman maintainer 98526527c8 Merge pull request #711: fix(mobile): hold the PTY rows while the on-screen keyboard is up 2026-10-27 02:24:17 +01:00
Codeman maintainer 5908cc51fd Merge pull request #694: feat(agent): COD-529 add bounded inspection and guarded submission
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>
2026-10-27 02:14:44 +01:00
Codeman maintainer 01cd509c2c Merge pull request #708: feat(agent): COD-549 launch workers in existing project directories 2026-10-27 02:12:50 +01:00
Codeman maintainer 6612a89ebc Merge pull request #714: feat(git): a Fetch button in the Git status header, and an interval for it app-wide and per case
Conflict in CLAUDE.md (Adding Features, App setting) resolved by keeping
both sides: #714's sentence that a setting the server acts on must be
synced, after the `.strict()` clause, and #719's mutateSettingsFile() rule
at the end of the bullet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:11:49 +01:00
Codeman maintainer cad8621324 Merge pull request #707: feat(history): COD-549 read saved Codex conversations offline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:11:29 +01:00
Codeman maintainer 4b3eebd0ed fix: apply the review items for #708 at merge
- test/cli-agent-project-spawn.test.ts:345: the doc-sync test no longer reads .changeset/project-worker-directories.md (changeset version deletes it at release); README, API reference and wiki stay.
- CLAUDE.md:132 + src/cli-agent.ts @fileoverview (invariant 5): the directory spawn (POST /api/v1/sessions plus /interactive or /shell, no Agent-Origin header, so no marker) and its refusals (empty --working-dir, bare spawn from a HOME cwd); the invariants now name test/cli-agent-project-spawn.test.ts too.
- test/cli-agent-project-spawn.test.ts:322: "allows a cwd below HOME" mocks the cwd to <temp HOME>/src/app, so it covers the case the guard must leave alone.
- docs/wiki/Driving-Codeman-From-An-Agent.md:59: split the sentence so exit 4 covers only the empty --working-dir; spawn "" reaches quick-start and exits 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:48:21 +01:00
Aamer Akhter 003398e209 fix(agent): refuse an implicit HOME cwd for a directory spawn
`codeman agent spawn` always sends its directory as an explicit
workingDir, so the server cannot apply its no-workingDir rule and
installs workspace hooks (and an injected skill) into ~/.claude when the
caller's cwd is HOME. A bare spawn whose cwd resolves to the home
directory now refuses with exit 4 before any request and names
`--working-dir ~`; an explicit `--working-dir ~` is still sent. Paths
are compared by realpath, so a symlinked HOME matches, and an
unresolvable HOME or cwd falls back to the resolved path without
throwing.

The directory-spawn --json shape now reads the same in the README, the
API reference, the wiki page and the changeset: `ready` when a
readiness wait ran, `readinessError` when that call failed, and
`startupError` when the start failed. A test pins all four.
2026-10-26 08:51:15 -04:00
JD fc0ecc6051 fix(tunnel): give the unauthenticated tunnel its own acknowledgement
The network acknowledgement that install.sh writes for every passwordless LAN install also satisfied the tunnel guard, so one click on the welcome screen's tunnel link published full terminal control to a public URL with no login and no confirm. The tunnel now needs CODEMAN_ALLOW_UNAUTHENTICATED_TUNNEL or --allow-unauthenticated-tunnel (carried by web -d and service install), and with no login the browser asks before every enable, reading a new loginConfigured field from GET /api/tunnel/status.

The guard is now decided once per request and required again where the tunnel starts, so a save that found the tunnel running can no longer restart it unguarded after a concurrent stop.

Closes #706
2026-10-26 03:13:25 -04:00
Devvyn 3411d7f35a fix(git): the fetch timeout reaches the whole tree, a tick covers every session on its folder, and the rest of the review (review)
`execFile` signals the `git` parent only, so a fetch that times out can leave its http helper
still running, and on the interval that repeats every run with nobody watching. `runFetch` now
spawns `detached`, so the fetch and the helper it spawns share one process group, and the kill
is SIGTERM to the negative pid then SIGKILL `FETCH_KILL_ESCALATION_MS` later; the escalation is
not cancelled when the promise settles, because the caller is answered at the timeout while the
group is still dying. The output cap is kept, and the clone's slot queue is not needed here.

The tick is two passes: every enabled local session is collected by folder, a folder is due when
ANY session open on it is due, the folder is probed once for the group, and the run is published
to every session on it. A single pass decided by the order the sessions were read, so [long,
short] published only the short one and the 3600 s session's row never updated again while its
folder was fetched every 30 s.

`gitNonInteractiveEnv` writes BatchMode ssh as a default, not an override: `core.sshCommand` is
read inside the same slot and the same bound as the fetch it serves, and `fetchEnvFor` keeps the
repository's answer when it has one, because that is usually the setup that works on a machine
with several ssh setups. The `accept-new` host key it can write is stated next to the BatchMode
note: a prompt is stopped, a write is not.

The write claim is scoped to the Git panel and to the timer, with cloning and worktree management
named as the writes that happen when the user asked for them, and the zh keys sit with the group
of the UI surface their English string belongs to.

Tests: the process-group test proves the tree by group membership, because the helper is a
grandchild (`git fetch` runs `git remote-http`, which spawns `git-remote-http`) and because the
group is what the negative pid reaches, and it remembers each process once found instead of
re-scanning a group it had already emptied. The non-zero-exit test changed premises: `git fetch
--quiet` in a repository with no remote exits 0 on git 2.47.3, so resolving empty stdout there was
correct, and the case that has to reject is a remote pointing at a path that is not a repository.
The join test asserts after the run and does not await the request before the held run is
released, which would wait on the run the test is holding.
2026-10-25 22:31:25 +08:00
Aamer Akhter 2832a037d6 fix(agent): verify staged text on a live codex pane and refuse popups
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.
2026-10-25 09:22:35 -04:00
Aamer Akhter 17b2edce22 fix(history): count compressed rollouts, read each file once, degrade an unreadable parent
Codex's local_thread_store_compression rewrites cold rollouts to
*.jsonl.zst, which the store scan skipped without a trace. Each one is now
counted in compressedFiles (complete: false), skipped only when its plain
.jsonl sibling exists, as Codex's own discovery does, and a lookup that
misses names the compressed file whose name carries the wanted id. A
Zstandard file given to --file, or sitting in the store under a .jsonl
name, is recognised by its magic number.

Each selected rollout is parsed once into every view later needed of it,
so a legacy copy's boundary proof reuses the parent's own read instead of
charging it to --scan-bytes a second time (and counting its malformed or
inherited records twice).

A parent read that fails for any reason other than a budget now leaves
the boundary unproven (cut at the last candidate, unverifiedPrefixes,
complete: false) instead of failing a child whose own file is intact. A
budget error still fails the read.

--max-bytes is validated before the first file is read, next to the
other budgets, and every budget flag accepts plain decimal integers only.
2026-10-25 09:08:54 -04:00
Aamer Akhter b26234d6c6 fix(agent): refuse an empty --working-dir instead of spawning in cwd
`codeman agent spawn --working-dir "$WT"` with WT empty (a failed
`git worktree add`, say) resolved '' to the caller's cwd and started an
editing worker in the caller's own checkout. An empty or
whitespace-only --working-dir now refuses with exit 4 before any
request.

The emitJson comment names the directory-spawn exception to "the
envelope's data": `{ sessionId, workingDir, created: true, ready }`.

Tests: unit (`''`, spaces, tab: exit 4, no calls) and through the real
argument parser and HTTP transport (nothing sent, exit 4).

Docs: README, api-reference, wiki, SKILL.md (plugin mirror synced) and
the changeset note the refusal.
2026-10-25 09:03:15 -04:00
Devvyn ea4318947b fix(git): the interval fetches one run per folder, and the rest of the review (review)
The scheduler is the part that runs with nobody watching, so it is decided per folder
rather than per session: sessions sharing a folder are the same clone and share one run
that both see, `inFlight`/`lastRunAt` are keyed by the resolved folder, the schedule is
stamped when a folder is picked instead of when its run starts, and a tick is skipped
while the previous one is still going. The click route joins a run already going for the
same folder rather than starting a second fetch in one clone.

The tick keeps ONE bound for the git processes of the whole run and hands it to every
folder, and bounds its folders with a bound of its own: the same semaphore cannot serve
both levels, because a folder holding a slot would then wait for a slot its own fetch
needs. Both bounds are `FETCH_CONCURRENCY`, so the ceiling is unchanged. The bound claim
in CLAUDE.md, the `Limiter` jsdoc and docs/api-reference.md now describes it as it is.

Before anything is fetched the folder is probed with the bounded path probe, so a case on
a mount that stopped answering is skipped and never touched.

The rest of the review: the hooks are named as they really are (`reference-transaction`,
`pre-auto-gc` through the maintenance a fetch triggers, config commands such as
`credential.helper`) and `FETCH_CONFIG_ARGS` turns maintenance and gc off for an
unattended run; the claim that an agent in the same repository is not disturbed is
replaced by the ref-lock collision it actually is; the click route publishes its own run
as `trigger: 'click'`; `autoSaveGitFetch` uses `_apiJson` so a 400 shows the error toast
and applies the returned settings; and the two wording fixes.

Tests: the scheduler pins one run per folder, the two bounds, the probe before anything
runs, and that closing the session holding the override keeps the folder while another
session is still open on it; the route test pins the click publish and the join, asserting
the join before the held run is released — releasing first lets the run finish and clears
the lane, so the joiner would be entitled to its own fetch and the test would pass for the
wrong reason.
2026-10-25 20:04:01 +08:00
Devvyn 9432d9f9e2 feat(git): a Fetch button in the Git status header, and an interval for it app-wide and per case
Fetch is the only git command Codeman runs that writes, and the only one that runs a
repository's own fetch-post hook, so it stays behind a click and off by default. The Git
status window's header row gains Fetch; App Settings -> Bottom bar gains the switch and
the interval, synced because the scheduler acts on them, and Session Options -> Git
overrides both per case. The scheduler reads settings fresh each tick, times the interval
from the start of a run, never overlaps runs, and re-checks the Docker and remote rules
every tick before any git runs. Nothing but remote-tracking refs moves: HEAD, the index
and the working tree are proven unchanged against real git while origin/main advances.

Tests: src/git-fetch.ts and the scheduler, the per-case settings route, the synced/display
setting split, the two new i18n rows, and the Fetch block of the Git status browser suite.
2026-10-25 17:23:55 +08:00
Aamer Akhter 44fa2d4757 docs(history): document the offline reader next to codeman agent
Replace docs/offline-history.md, which repeated the skill reference and was
linked from nowhere, with a README section and a wiki section beside the
codeman agent commands. Bring the skill reference, its plugin mirror, the
Codeman Skill wiki page and the changeset in line with the reader: forks,
the separate budgets and their flags, UTC dates, phases and complete:false.
2026-10-24 23:00:50 -04:00
Aamer Akhter c78635a3d3 feat(agent): make coordination routes opt-in and address review
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.
2026-10-24 22:54:17 -04:00
Aamer Akhter c5ffea0ed2 fix(agent): treat only an omitted case as a directory spawn, and document the directory launch
`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.
2026-10-24 22:39:16 -04:00
Aamer Akhter a24b0ec7bb feat(agent): add bounded inspection and guarded submission 2026-10-24 22:37:06 -04:00
Aamer Akhter b2e96ddfaf feat(agent): COD-549 start workers in existing project directories 2026-10-24 20:44:15 -04:00
JD c7835a159c feat(tabs): start a session beside any tab from an opt-in + in its actions
Opening another session in the case you are already working in meant re-picking the case and the mode in the toolbar. An opt-in + now sits with each tab's gear and close icons (App Settings, Tabs, New Session Button on Tabs: per device, off by default everywhere). It opens a menu of the CLIs that tab's case can start, each shown with its logo and name as in the Run menus, and a pick starts one session in that tab's case, or the managed worktree it runs in, and in its named group, without changing the Run button's mode or case.

The CLIs offered follow the Run menu's own rule for the case, so a Docker case lists what its container has rather than what the host has. A tab outside any named group sends no group, so the case's default session group places the new session the way it places a toolbar Run. Wherever a tab has a row menu (the vertical rail, the sidebar) that menu gains "New session here…", the keyboard route to the same picker, since Enter on the + itself does nothing. On a phone the + joins gear and close on the active tab only, and the label reserve grows to 68px while the setting is on, so the tab's centre stays off the icons.

The shared tab layout menu no longer closes when focus leaves an item for nowhere: Safari never focuses a clicked button, so pressing an item blurred the focused one with no related target and the click was lost. This is the tile count menu's rule, and it fixes the named-group menu as well.
2026-10-24 20:29:00 -04:00
JD 3e334357e8 fix(mobile): hold the PTY rows while the on-screen keyboard is up
Every keyboard open and close resized the PTY, and Claude Code's inline renderer answers each height change by clearing the screen and reprinting only the transcript tail that fits, so rows above that tail were lost from tmux for good. Measured on Claude Code 2.1.306 with tmux 3.2a: five keyboard cycles (62x38 to 62x15 and back) left 75 of 100 numbered lines in the pane's history; with the rows held the PTY stays 62x38, no SIGWINCH reaches the pane and all 100 remain (#672).

While the keyboard is up the rows captured at the show transition are held through getTerminalDimensions, columns still follow the box, and the screen, now taller than its box, is translated up by --kb-clip so the last content row sits at the bottom of the visible area. This is the one documented exception to the columns-only geometry rule: there the extra rows came from another device and nothing kept the composer in view, here they are this device's own rows held on purpose and the clip keeps the composer visible. The local-echo overlay's setRowLimit narrows the renderer's existing totalRows bound, so one place decides where a long draft stops growing downward.

Only inline renderers hold, through a new registry capability keyboardHold: 'inline' (Claude alone) published to the page as window.__codemanKeyboardHoldModes. Full-height layouts such as codex, Claude's fullscreen mode (mouse tracking on, unmeasured), and remote or docker panes resize as before. The hold is released on keyboard close, a shape change, a tab switch, a session update, a reconnect, or the setting turned off. Per-device setting Keep Size Under Keyboard, default on.
2026-10-24 20:17:45 -04:00
Codeman maintainer f8029c2a9e fix: refuse a case folder move while a session is still being created in it (release review)
The rename lock is checked once, in the Session constructor, but a session
reaches the server's map (where the rename route builds its list of open
sessions) only later, after the create awaits the owner's layout lock and a
fresh settings read (and, for POST /api/sessions, inferLocalCaseName). A
rename in that gap neither listed nor stopped the session, moved the folder,
and the create then registered a session whose pane starts with
`cd "<old path>"`, skipping the 409 requiresStop / stopSessions guarantee.

- case-rename.ts keeps a record of sessions constructed but not yet
  registered (`noteCaseSessionStarting` / `caseSessionSettled` /
  `isCaseSessionStarting`), with a 30 s TTL for one that never registers
  (a failed create, a one-shot prompt), swept on every note and check.
- The Session constructor records itself (local sessions only, as the
  route ignores remote ones); registerSessionWithLayout()'s register
  callback, the boot-restore registration and stop() settle it.
- The rename route answers 409 CONFLICT with renameCode BUSY ("A session is
  starting in this case") for a folder move while one is recorded at or
  under the folder. The check runs in the same tick that takes the lock, so
  every interleaving is either refused here or by the constructor. An
  alias-only rename is not refused.

A create whose path was validated before the move but whose Session is
constructed after it is still the ordinary check-then-use race of any
create path; CLAUDE.md now says so instead of "nothing starts mid-move".

Tests: unit (record, settle, stop, TTL), route (BUSY, then requiresStop once
registered; alias-only unaffected) and an end-to-end WebServer test that
holds the create between constructor and registration (it fails without
the route check: the folder moves under the session).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 16:57:20 +02:00
Codeman maintainer fb83a5c8df fix: keep the host readings' labels under header density, hide ringless ones at Compact tiny (release review)
#689's density steps hide every `.header-system-stats .stat-label` once the
toolbar is narrowed, and #692's readings (GPU, VRAM, GPU MHz, CPU MHz) have
nothing else naming them: at tight, dense and tiny the header showed "40%"
beside CPU's "12%", "3.2G" beside MEM's "7.9G", and two bare "1800 MHz" /
"4200 MHz" with no way to tell the GPU clock from the CPU one. At Compact +
tiny, where values are hidden too, the two clocks (no ring) were left as
0-px items that still took an 8 px flex gap each. Neither PR shows it alone.

- The four host items carry `stat-item--host`, excluded from the density
  label hide, so they keep their labels at every step.
- `stat-item--ringless` (static on the two clocks, toggled by
  `_updateHostSensors` on VRAM while its total, and so its ring, is unknown)
  is hidden at Compact + tiny instead of staying an empty item.

Tests: the browser test drives every density in all three styles against
the real markup and styles.css (it fails on the previous files), and the
unit test pins the VRAM ringless toggle.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 16:45:05 +02:00
Codeman maintainer e739b2e9d4 Merge pull request #690: feat(agent): per-session agent inbox (discussion #558)
Conflict in src/web/routes/session-routes.ts (the import block) resolved
by keeping both sides: #680's claudeProjectDirName import stays, and the
fastify import takes #690's form without FastifyReply, since #690 moved
abortOnClientHangUp (its only user here) into route-helpers.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 15:29:15 +02:00
Codeman maintainer 2fdbe786b3 Merge pull request #680: feat(cases): rename a case and its folder, with a lock and live-session handling 2026-10-24 15:27:46 +02:00
Codeman maintainer c7b6226cb0 Merge pull request #692: feat(header): opt-in GPU and CPU clock readings after CPU and MEM
Conflict in config/test-suites.ts resolved by keeping both sides: #689's
header-divider and #692's header-host-sensors browser tests are both
listed in BROWSER_TEST_GLOBS.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 15:25:02 +02:00
Codeman maintainer aad67f2056 Merge pull request #689: feat(header): resizable toolbar divider that collapses the usage readings as it narrows 2026-10-24 15:23:39 +02:00
Codeman maintainer 395d8293a7 Merge pull request #685: feat(palette): choose the agent when starting a session from Cmd+K 2026-10-24 15:23:11 +02:00
Codeman maintainer 4dfe978d3f Merge pull request #682: fix(mobile): swipe through sessions in the order the strip draws them 2026-10-24 15:16:49 +02:00
Codeman maintainer d1e0e0aea6 fix: address the release-prep review of #680
Claude history merges, never overwriting, into a project directory a deleted case of the new name left behind, instead of staying orphaned under the old key.
The free-target pre-check also refuses a dangling symlink (lstat), so stopSessions no longer ends sessions before a 409 EXISTS.
The rename dialog says stopped sessions are closed (their tabs go away) and, after a failed rename, how many were already closed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:43:13 +02:00
Codeman maintainer 9ba24a02df fix: apply the review items for #690 at merge
Boot prune keeps the inboxes of sessions on the reboot-restore offer (their records are already pruned and a rebuild reuses the id), with a case in test/agent-inbox-server-wiring.test.ts.
docs/wiki: post, inbox and ack in the codeman agent command block of Driving-Codeman-From-An-Agent, and "Keep agent inbox across restarts" in Settings-Reference.
codeman agent inbox prints message text through terminalSafeText (ANSI stripped, other C0, DEL and C1 controls shown as \xHH); --json stays as stored.
Shutdown flushes the inbox snapshot again after app.close(), for a post that lands after the first flush.
POST /api/cleanup-state drops the inboxes of the records it removes.
InboxPostSchema derives its fence from MAX_MESSAGE_BYTES instead of the literal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:15:49 +02:00
Codeman maintainer 7783fcd0ce fix: apply the review items for #692 at merge
host-sensors: never await the PowerShell RAM probe on a read; keep the resolved total and show the memory in use alone until it answers (test with a held PowerShell)
test: rename the Windows sampler test that said the GPU counters are added "for good"
test: make host-sensors.test.ts's @fileoverview describe what that file covers
host-sensors: make the nvidiaSkipUntil JSDoc describe the back-off deadline
docs: Settings Reference says the host readings live inside System Stats and nothing is fetched while it is off

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:15:22 +02:00
Codeman maintainer b7897b6d0e fix: apply the review items for #684 at merge
Multi-user: add a (username, IP) failure bucket checked before verifyPassword, capped at AUTH_FAILURE_MAX, with the same decay as the other buckets; the per-IP pre-gate stays at AUTH_IP_FAILURE_MAX and the per-username bucket stays after the verify
A correct password now also clears that account's (username, IP) bucket, beside the IP and per-username buckets
The new bucket is disposed on server stop like the others; the comments above the gate and the constants' JSDoc describe the three buckets
Tests: 10 wrong then the correct password from one IP is a 429, the same account logs in from another IP, another account behind that IP is unaffected, and a correct password clears the pair bucket
test/multiuser-auth.test.ts: the spray test loops to AUTH_IP_FAILURE_MAX instead of a literal 50
Reworded the rate-limit lines in auth-config.ts, security-architecture.md, README.md, README.zh-CN.md, wiki/Security.md, endpoints.md (plus the plugin mirror), CLAUDE.md and architecture-invariants.md
docs/wiki/Remote-Access.md: add the multi-user note to the tunnel page

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:15:21 +02:00
Codeman maintainer 34a2966c19 fix: apply the review items for #689 at merge
Clamp the stored toolbar width to MIN_WIDTH in apply() and step the arrow keys from the width on screen, so a drag to the window edge or a held ArrowRight no longer stores a negative width or leaves an ArrowLeft dead zone (browser test added)
Drop Escape as a reset key on the handle: the global capture-phase Escape handler already closes every panel on it (static pin added)
Say in the changeset, the wiki and the densityFor comment that the percentages go at the narrowest step in the Compact style only, and drop Escape from the changeset

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:08:37 +02:00
Codeman maintainer d16172c4b8 fix: apply the review items for #685 at merge
palette agent switch: aria-hidden on .command-palette-agent, arrows lose role="button" and aria-label (no nested buttons in the row)
i18n: zh-CN entry for the "Agent for the new session (Left/Right to change)" tooltip
docs: Keyboard-Shortcuts Ctrl+K row says Left/Right change the agent on the New session row
test: pin the aria-hidden switch and the zh-CN tooltip entry in command-palette-ui.test.ts

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:05:12 +02:00
Codeman maintainer ac6d4aa2db fix: apply the review items for #680 at merge
Restore settings.json (caseOrder, lastUsedCase) when linked-cases.json cannot be written, with the rollback test extended
List Claude Code's per-project ~/.claude.json entry as not migrated in Core-Concepts.md and CLAUDE.md
Say in the 500 that the folder is now at the new path when moving it back fails
Describe the rename dialog's folder checkbox in Core-Concepts.md, carry stoppedSessions on errors, count 5 case SSE events

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:01:33 +02:00
Devvyn e1d6471808 feat(header): opt-in GPU and CPU clock readings after CPU and MEM 2026-10-23 21:36:10 +08:00
Devvyn f3c90abeb0 fix(header): settle the divider when readings change, keep density to the header layout, restore the chosen width 2026-10-23 19:58:40 +08:00
Aamer Akhter 175e3b8320 fix(auth): keep the single-user login throttle at 10, widen only the multi-user pre-gate
Single-user mode has no per-username bucket, so its per-IP bucket is the
only limit on guesses against the one password, and behind a tunnel every
client shares 127.0.0.1. Put it back on AUTH_FAILURE_MAX (10).

AUTH_IP_FAILURE_MAX (50) now gates only the multi-user per-IP pre-gate,
which runs before the scrypt verify and so refuses even a correct password;
the per-username bucket still caps each account at 10.

Move the headroom test out of the single-user suite and add a multi-user
test that sprays wrong passwords across usernames (9 each) and expects 401
for the first 50, then 429 for the 51st and for a correct password.

Update the docs that still described 10 per IP for every mode, and resync
the plugin mirror of the codeman skill.
2026-10-22 21:21:49 -04:00
Devvyn 1cad4dd848 fix(cases): address review of the rename route
Only an admin's rename touches the server-wide settings.json, which is now
written before linked-cases.json with the registry restored on failure. The
Resume list follows a renamed folder, the owner's default session group is
carried over, remote sessions are never listed or stopped, errorCode maps
EXISTS and BUSY, and the unmigrated items are documented.
2026-10-23 04:15:46 +08:00
Devvyn ab0bc0aecd feat(cases): rename a case and its folder, with a lock and live-session handling 2026-10-23 04:12:37 +08:00
JD 116961f3a8 fix(mobile): swipe through sessions in the order the strip draws them
nextSession/prevSession walk the canonical sessionOrder, but the case and state tab layouts, a sorted rail and named groups reorder the strip, so with any of them a swipe jumped to tabs that were not next to the current one on screen. The swipe now asks for the drawn order, read with the same walk the strip's arrow keys already use (factored out as _visibleTabRows); Alt+[ / Alt+], Ctrl+Tab and Alt+1..9 keep the canonical order. An active session the list does not draw steps in at the first or last row, and an empty drawn list goes nowhere instead of falling back to a hidden session.
2026-10-22 15:11:21 -04:00
Codeman maintainer a85da87378 Merge pull request #674: fix(mobile): give a sideways drag on the terminal to the session swipe alone
Second merge of #674: the author pushed the thumb-arc handback from the review themselves (e8fc08be), byte-identical in code to the merge-time fix already here, so this brings their round in and keeps the browser test and docs added at merge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	docs/wiki/Mobile-Guide.md
#	src/web/public/terminal-ui.js
#	test/session-swipe-gesture.test.ts
2026-10-22 20:32:13 +02:00
Codeman maintainer 463a083a23 Merge pull request #676: feat(tabs): per-case default session group
Conflict in CLAUDE.md's State Files line resolved by keeping #661's new
config-backup entries and this PR's "case default groups" in state.json.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:54:54 +02:00
Codeman maintainer 3b6818449e Merge pull request #661: feat(config): timestamped config backups with a configurable folder and retention
Conflicts in CLAUDE.md, README.md, docs/api-reference.md and
src/web/server.ts were additions beside #662's Copilot quota read and
#666's keep-warm sections; both sides kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:54:40 +02:00
JD e8fc08beb3 fix(mobile): hand a swipe-locked drag back to the scroll past the swipe's drift limit
A scroll whose first move past the tap slop leaned sideways (a thumb arc) locked as 'swipe' for the whole contact, and SwipeHandler drops a swipe at 50 px of vertical drift, so the gesture did nothing. Past that limit the drag is now a 'y' scroll. Also brings the mobile-handlers fileoverview to the current 500 ms / 50 px limits and notes in the Mobile Guide that a selection pauses the swipe and a wide terminal pans instead.
2026-10-22 13:53:48 -04:00