mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
chore: version packages
1.24.2. Also corrects two numbers in the CLAUDE.md trust-dialog paragraph that was written while the fix was still uncommitted: the keystroke cap is 6, not 3, and the scan now schedules its own follow-up read rather than waiting on PTY output that a static dialog never produces.
This commit is contained in:
@@ -1,5 +1,41 @@
|
||||
# aicodeman
|
||||
|
||||
## 1.24.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix every new claude session dying on Claude Code 2.1.252's rewritten folder-trust dialog.
|
||||
|
||||
That dialog used to offer `❯ 1. Yes, I trust this folder` / `2. No, exit`, so Codeman
|
||||
answered it by pressing Enter on the highlighted default. 2.1.252 dropped the numbers,
|
||||
reversed the options and highlights `No, exit`, so the same Enter now answers _exit_: a
|
||||
session in any directory claude had not seen before died (`Pane is dead (status 1)`)
|
||||
about six seconds after it started, before the agent ever drew a composer.
|
||||
- `trustDialogNextKey()` (`src/session-trust-dialog.ts`) now reads the `❯` marker off
|
||||
the rendered pane and returns ONE keystroke at a time: an arrow while the cursor is on
|
||||
the wrong option, Enter only once the screen shows it on the trust option. A frame it
|
||||
cannot read presses nothing. Both the 2.1.252 and the older numbered layout are
|
||||
handled, and the direction is derived from the frame rather than assumed, so a further
|
||||
reordering costs a repaint instead of a session.
|
||||
- The scan schedules its own follow-up read. It had only ever run from the PTY data
|
||||
handler, which was enough while one Enter answered the dialog; the arrow that moves the
|
||||
cursor is the last output the pane produces, so a two-keystroke answer would otherwise
|
||||
stall with the cursor sitting on the right option forever. The keystroke cap goes from
|
||||
3 to 6 for the same reason.
|
||||
- The bundled `codeman` agent skill gets the same treatment (preamble 1.21.0): its
|
||||
`_accept_trust` fallback reads `terminal?full=1`, steers onto the trust option and
|
||||
confirms only after re-reading, instead of posting a blind `\r`. It sends those
|
||||
keystrokes under its own `clientId`, because input sequence numbers are monotonic per
|
||||
client and spending prompt numbers on dialog keys would make the next send-and-wait
|
||||
look like a stale duplicate and vanish silently.
|
||||
- Readiness recipes in `docs/extending-codeman.md`, `docs/api-reference.md` and the
|
||||
skill's own reference carry the corrected answer and a new symptom-table entry for a
|
||||
worker whose pane is dead seconds after the spawn.
|
||||
|
||||
Also included: a CLAUDE.md audit against the tree, correcting counted drift (route
|
||||
modules, handler counts, frontend module count and app.js size, install.sh size) and
|
||||
documenting several subsystems that had no entry.
|
||||
|
||||
## 1.24.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -75,7 +75,7 @@ When user says "COM":
|
||||
|
||||
CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed.
|
||||
|
||||
**Version**: 1.24.1 (must match `package.json`)
|
||||
**Version**: 1.24.2 (must match `package.json`)
|
||||
|
||||
## Project Overview
|
||||
|
||||
@@ -195,7 +195,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
|
||||
|
||||
⚠️ **A `❯` sighting is NOT the end of a turn, and neither is silence.** Claude redraws the composer (`❯`) about once a second all through a turn, so the old "saw a ❯, wait 2s → idle" rule flipped every working session to idle two seconds in (measured: a session mid-tool-call at 17 minutes reporting `status:"idle"`). Its working indicator is `✻ Actualizing… (13m 23s · ↓ 47.5k tokens)`: the glyph animates through `· ✢ ✳ ∗ ✻ ✽`, the gerund is randomized, and the finished line (`✻ Cooked for 2m 49s`) carries the same glyph, so neither `SPINNER_PATTERN` (braille, not what current versions draw) nor a keyword list can see it. Matching the new line in the STREAM does not work either: tmux ships partial repaints, so the whole line reaches the PTY only every few tens of seconds. So: `_confirmIdle()` (session.ts) requires the pane to go quiet, and then asks the SCREEN via `capturePaneText()` + `CLAUDE_WORKING_LINE_PATTERN` before believing it; a sustained run of repaints (`session-activity.ts`, pure + unit tested) is what marks a turn as started, with the same screen probe vetoing keystroke echo. Idle now lands ~3-5s after a turn ends instead of 2s into one. Claude-mode only, since an external CLI has no `❯`, so nothing would ever arm the confirmation and the session would latch busy.
|
||||
|
||||
**Workspace-trust dialog auto-accept** (`session-trust-dialog.ts`, pure + unit tested): Claude Code asks once per directory ("Is this a project you created or one you trust?") before it will read or edit anything, and since Codeman sessions run permission-skipping or classifier-guarded modes the answer is always yes, so a session parked on that dialog is simply stuck. ⚠️ **Match the compacted SCREEN, never the stream.** tmux repaints a row by writing each word and then a cursor-forward (`\x1b[C`) instead of a space, and Ink colours each word separately, so the wire carries `I\x1b[Ctrust\x1b[Cthis\x1b[Cfolder`; stripping the escapes leaves `Itrustthisfolder`, because the spaces are not there to strip, they were never sent. A plain `includes('trust this folder')` therefore never matched a single chunk and the auto-accept was silently DEAD for every session that hit the dialog. `compactScreenText()` removes ALL whitespace instead (plus the `ESC ( B` charset selects that `stripAnsi` does not cover, which would otherwise land inside a phrase as a literal `(B`), which survives both that repaint style and the spaced full-screen redraw. ⚠️ **Never answer it with a blind `\r`.** The layout has changed under us at least twice, and Claude Code 2.1.252 dropped the option numbers, put "No, exit" FIRST and highlights IT by default, so the Enter that answered the old dialog now picks *exit* and the pane dies (`Pane is dead (status 1)`) seconds after the session starts. `trustDialogNextKey()` reads the `❯` marker and returns ONE step at a time (an arrow while the cursor is on the wrong option, Enter only once the screen shows it on the trust option), with the pane re-read between steps, so a dropped arrow costs a repaint instead of the session; a frame that does not say which option is highlighted returns null and waits for the next repaint. ⚠️ The LAST marked option in the text wins, because the direct-PTY fallback reads an append-only buffer where every repaint since launch is still present and an older frame must not out-vote the freshest one. ⚠️ Answering types into a live session, so THREE guards must all hold and none is redundant: a **startup-only window** (`TRUST_DIALOG_WINDOW_MS`, 90s, since the dialog renders before the main UI and leaving it open forever would let an agent transcript that merely QUOTES the dialog trigger an Enter, this file being an example), a **two-marker match** requiring a trust phrase AND one of the dialog's own confirm affordances (`isTrustDialogScreen`), and an **attempt cap** (`TRUST_DIALOG_MAX_ATTEMPTS`, 3: a keystroke can land while Ink is still mounting the widget and be dropped, which is the other half of why sessions got stuck here, but retrying forever would hammer keys into whatever came next). ⚠️ It reads `capturePaneText()` and falls back to a deliberately SHORT tail of the terminal buffer only on a direct-PTY session, which has no pane: that buffer is append-only, so a longer tail would keep re-matching a dialog answered minutes ago.
|
||||
**Workspace-trust dialog auto-accept** (`session-trust-dialog.ts`, pure + unit tested): Claude Code asks once per directory ("Is this a project you created or one you trust?") before it will read or edit anything, and since Codeman sessions run permission-skipping or classifier-guarded modes the answer is always yes, so a session parked on that dialog is simply stuck. ⚠️ **Match the compacted SCREEN, never the stream.** tmux repaints a row by writing each word and then a cursor-forward (`\x1b[C`) instead of a space, and Ink colours each word separately, so the wire carries `I\x1b[Ctrust\x1b[Cthis\x1b[Cfolder`; stripping the escapes leaves `Itrustthisfolder`, because the spaces are not there to strip, they were never sent. A plain `includes('trust this folder')` therefore never matched a single chunk and the auto-accept was silently DEAD for every session that hit the dialog. `compactScreenText()` removes ALL whitespace instead (plus the `ESC ( B` charset selects that `stripAnsi` does not cover, which would otherwise land inside a phrase as a literal `(B`), which survives both that repaint style and the spaced full-screen redraw. ⚠️ **Never answer it with a blind `\r`.** The layout has changed under us at least twice, and Claude Code 2.1.252 dropped the option numbers, put "No, exit" FIRST and highlights IT by default, so the Enter that answered the old dialog now picks *exit* and the pane dies (`Pane is dead (status 1)`) seconds after the session starts. `trustDialogNextKey()` reads the `❯` marker and returns ONE step at a time (an arrow while the cursor is on the wrong option, Enter only once the screen shows it on the trust option), with the pane re-read between steps, so a dropped arrow costs a repaint instead of the session; a frame that does not say which option is highlighted returns null and waits for the next repaint. ⚠️ The LAST marked option in the text wins, because the direct-PTY fallback reads an append-only buffer where every repaint since launch is still present and an older frame must not out-vote the freshest one. ⚠️ Answering types into a live session, so THREE guards must all hold and none is redundant: a **startup-only window** (`TRUST_DIALOG_WINDOW_MS`, 90s, since the dialog renders before the main UI and leaving it open forever would let an agent transcript that merely QUOTES the dialog trigger an Enter, this file being an example), a **two-marker match** requiring a trust phrase AND one of the dialog's own confirm affordances (`isTrustDialogScreen`), and an **attempt cap** (`TRUST_DIALOG_MAX_ATTEMPTS`, 6: a keystroke can land while Ink is still mounting the widget and be dropped, which is the other half of why sessions got stuck here, but retrying forever would hammer keys into whatever came next; it was 3 while one Enter answered the dialog, and answering now costs at least two keystrokes). ⚠️ It reads `capturePaneText()` and falls back to a deliberately SHORT tail of the terminal buffer only on a direct-PTY session, which has no pane: that buffer is append-only, so a longer tail would keep re-matching a dialog answered minutes ago. ⚠️ **The scan must schedule its own next read** (`_trustDialogTimer`, cleared in `_clearAllTimers()`): it runs from the PTY `onData` handler, which was enough while one Enter answered the dialog, but the arrow that moves the cursor is the LAST output the pane produces, so a two-keystroke answer waiting on more output parks forever with the cursor sitting on the right option (measured on a live 2.1.252 spawn: cursor moved at 6 s, then nothing).
|
||||
|
||||
**Process-tree walks are bounded** (`proc-tree.ts`, pure + unit tested): `collectDescendants(pid, byParent)` is the ONE descendant traversal, fed by a single cached `ps -eo pid=,ppid=` snapshot (`refreshProcSnapshot()` in tmux-manager.ts: in-flight-shared, async because `execSync`'s timeout cannot return at all while spawnSync waits on an unkillable child, and ANY error discards the result rather than caching a truncated `ps`, which would make whole subtrees invisible to the kill path). ⚠️ **The unbounded version took a machine down** (2026-07-30): it ran `pgrep -P <pid>` once per node and recursed with no visited set, no depth limit and no node cap, so across ~28 adopted tmux trees the fan-out exploded while each `pgrep` blocked in the WSL kernel reading `/proc/<pid>/cgroup`, ending at ~13,000 `pgrep` processes in D-state, a load average above 13,000, and a machine recoverable only by restarting WSL, which cost every running session. Three properties make that impossible and each has a test: a cycle terminates (a real tree has none, a stale snapshot can still produce one), depth is capped (`PROC_WALK_MAX_DEPTH`), node count is capped (`PROC_WALK_MAX_NODES`). The fourth is structural: the function takes a snapshot and cannot spawn anything at all. ⚠️ It lives in its own module because as a private method of `tmux-manager.ts` the regression test had to keep its own COPY of the algorithm, which is a test that passes while the shipped code rots. ⚠️ Truncation is reported through `onTruncated` rather than silently, with BOTH caps named: a silent depth cap hides a deep tree exactly as effectively as a silent node cap hides a wide one.
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "aicodeman",
|
||||
"version": "1.24.1",
|
||||
"version": "1.24.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "aicodeman",
|
||||
"version": "1.24.1",
|
||||
"version": "1.24.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aicodeman",
|
||||
"version": "1.24.1",
|
||||
"version": "1.24.2",
|
||||
"description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
Reference in New Issue
Block a user