fix(hermes): detect worker prompt readiness
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 154

This commit is contained in:
jenkins 2026-08-08 22:38:05 -03:00
parent d3097c6bb8
commit 42f5f80146
3 changed files with 38 additions and 10 deletions

View File

@ -24,7 +24,7 @@ spec:
ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code
ai.bstein.dev/model-policy: difficulty-aware low through xhigh, cross-provider fallback ai.bstein.dev/model-policy: difficulty-aware low through xhigh, cross-provider fallback
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
ai.bstein.dev/config-rev: "20260808-herdr-browser-tui-prompt-ready" ai.bstein.dev/config-rev: "20260808-herdr-browser-tui-prompt-marker"
vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: hermes-agent vault.hashicorp.com/role: hermes-agent
vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens

View File

@ -8,7 +8,6 @@ import json
import os import os
import re import re
import subprocess import subprocess
import time
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@ -17,7 +16,10 @@ ALLOWED_EFFORTS = ("low", "medium", "high", "xhigh")
ROUTING_PATH = Path("/opt/data/workspace/coordinator/model-routing.json") ROUTING_PATH = Path("/opt/data/workspace/coordinator/model-routing.json")
HERDR_BIN = Path("/opt/data/tools/bin/herdr") HERDR_BIN = Path("/opt/data/tools/bin/herdr")
CODEX_AUTH = Path("/opt/data/home/.codex/auth.json") CODEX_AUTH = Path("/opt/data/home/.codex/auth.json")
CLAUDE_PROMPT_SETTLE_SECONDS = 3 PROMPT_READY_MARKERS = {
"codex": "OpenAI Codex",
"claude": "accept edits on",
}
def _load_routes(path: Path) -> dict[str, Any]: def _load_routes(path: Path) -> dict[str, Any]:
@ -180,10 +182,25 @@ def launch_worker(
started = _run(command, env) started = _run(command, env)
result = {**plan, "agent": agent_name, "pane": pane, "started": started} result = {**plan, "agent": agent_name, "pane": pane, "started": started}
if prompt: if prompt:
# Claude reports interactive readiness just before its prompt finishes # Herdr can detect the process before the full-screen prompt has finished
# drawing. Give that terminal a moment so the submitted Enter is not lost. # drawing. Wait for a pinned CLI marker so the submitted Enter is not lost.
if plan["worker"] == "claude": _run(
time.sleep(CLAUDE_PROMPT_SETTLE_SECONDS) [
str(HERDR_BIN),
"pane",
"wait-output",
pane,
"--match",
PROMPT_READY_MARKERS[plan["worker"]],
"--source",
"recent",
"--lines",
"120",
"--timeout",
"30000",
],
env,
)
result["prompted"] = _run( result["prompted"] = _run(
[ [
str(HERDR_BIN), str(HERDR_BIN),

View File

@ -55,7 +55,6 @@ def test_claude_worker_waits_for_prompt_readiness(tmp_path: Path, monkeypatch):
project = tmp_path / "project" project = tmp_path / "project"
project.mkdir() project.mkdir()
calls = [] calls = []
sleeps = []
def fake_run(command, env): def fake_run(command, env):
calls.append(command) calls.append(command)
@ -65,7 +64,6 @@ def test_claude_worker_waits_for_prompt_readiness(tmp_path: Path, monkeypatch):
monkeypatch.setattr(dispatch, "HERDR_BIN", herdr) monkeypatch.setattr(dispatch, "HERDR_BIN", herdr)
monkeypatch.setattr(dispatch, "_run", fake_run) monkeypatch.setattr(dispatch, "_run", fake_run)
monkeypatch.setattr(dispatch.time, "sleep", sleeps.append)
plan = { plan = {
"worker": "claude", "worker": "claude",
"model": "claude-haiku-4-5-20251001", "model": "claude-haiku-4-5-20251001",
@ -74,7 +72,20 @@ def test_claude_worker_waits_for_prompt_readiness(tmp_path: Path, monkeypatch):
dispatch.launch_worker(plan, project, "review", "Check the implementation.") dispatch.launch_worker(plan, project, "review", "Check the implementation.")
assert sleeps == [dispatch.CLAUDE_PROMPT_SETTLE_SECONDS] ready = calls[-2]
assert ready[1:] == [
"pane",
"wait-output",
"w2:p1",
"--match",
"accept edits on",
"--source",
"recent",
"--lines",
"120",
"--timeout",
"30000",
]
prompt = calls[-1] prompt = calls[-1]
assert prompt[1:5] == [ assert prompt[1:5] == [
"agent", "agent",