Merge pull request #624 from Ark0N/feat/codex-blocked-detection

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>
This commit is contained in:
Codeman maintainer committed 2026-10-16 02:00:29 +02:00
commit c76600315e
75 files changed
+2922 -112

No files matched your search

@@ -635,7 +635,7 @@ Three bounded long-polls. Shared semantics:
| `idle` | output stabilized + prompt detected, heuristic, can flap mid-turn | every mode |
| `working` | session started producing output | every mode |
| `stop` | Claude Code `stop` hook, the definitive end-of-turn | `claude` only |
| `blocked` | `permission_prompt` / `elicitation_dialog` hook, the worker needs an answer | `claude` only |
| `blocked` | `permission_prompt` / `elicitation_dialog` hook, the worker needs an answer | `claude`; also `codex`, whose approval, question and trust dialogs Codeman reads off its screen (answer them in the terminal, never through the inbox) |
| `exit` | PTY exited or session deleted | every mode |
⚠️ **`claude` mode is necessary for `stop`/`blocked`, not sufficient. The real
@@ -391,10 +391,10 @@ the worker remembering to print a token.
## Flow 5: watch for a worker stuck on a prompt
Claude workers can block on a permission dialog. `blocked` is a wait signal
(claude-mode only, and it needs Codeman's hooks in the worker's directory: see Flow 7
step 4), so watch for it and surface the question to the user instead of guessing an
answer. Expect it routinely on a server whose `claudeMode` is not the default bypass
Claude workers can block on a permission dialog. `blocked` is a wait signal (a claude
worker needs Codeman's hooks in its directory for it: see Flow 7 step 4; a codex worker
gets it from its screen, see below), so watch for it and surface the question to the user
instead of guessing an answer. Expect it routinely on a server whose `claudeMode` is not the default bypass
one (the same setting that decides whether the readiness marker in Flow 1 ever
appears):
@@ -409,10 +409,13 @@ if [ "$(jq -r '.data.wait.signal' <<<"$R")" = blocked ]; then
fi
```
Where the worker has no hooks, `blocked` never fires and a stuck worker looks exactly
like a slow one: your marker wait burns its whole bound. The fallback is the same
terminal tail, taken when a bound runs out, and the same rule about not answering it
yourself.
Codex workers are the one exception without hooks: Codeman reads their approval,
question and trust dialogs off the screen, so the same watch works with
`until=blocked,exit` (codex has no `stop`, a `400`), and the dialog is answered in the
terminal, never through the Approvals Inbox. Anywhere else without hooks, `blocked` never
fires and a stuck worker looks exactly like a slow one: your marker wait burns its whole
bound. The fallback is the same terminal tail, taken when a bound runs out, and the same
rule about not answering it yourself.
## Flow 6: claude fan-out over messaging
@@ -377,7 +377,9 @@ only when the workspace actually has them, see [§5.1](#51-where-to-spawn)) **an
`deepseek`** — the one external CLI that reports its own lifecycle, so its `stop` is a
real end-of-turn signal rather than a guess. On
`shell`/`opencode`/`codex`/`gemini`/`antigravity`/`pi`/`grok`/`omp`, requesting them explicitly is a
400, and lifecycle transitions there are coarse (a short shell command may emit **no**
400 (except `blocked` on `codex`, which Codeman reads off its screen when an approval,
question or trust dialog is up; a wait for it answers at once if one already is), and
lifecycle transitions there are coarse (a short shell command may emit **no**
`idle` transition at all, verified live), so synchronize those with markers.
⚠️ A dsh session can still refuse them for a per-SESSION reason: `statusReporting: