Skip to content

fix: wait for agent prompt readiness - #2537

Open
Pimpmuckl wants to merge 2 commits into
herdrdev:masterfrom
Pimpmuckl:issue/2410-agent-start-trust-readiness
Open

fix: wait for agent prompt readiness#2537
Pimpmuckl wants to merge 2 commits into
herdrdev:masterfrom
Pimpmuckl:issue/2410-agent-start-trust-readiness

Conversation

@Pimpmuckl

Copy link
Copy Markdown
Contributor

What changed

herdr agent start now waits for positive prompt-ready evidence when the active agent manifest provides it. The initial fallback Idle or Blocked state no longer makes those launches ready. Agents without a positive idle rule keep the existing fallback behavior, and readiness-only changes are published to API clients.

Why

agent start previously treated a generic detected Idle or Blocked state as equivalent to proof that the agent's normal input prompt was visible after the settle window. Those are not always the same thing. Herdr can identify the process and publish an initial fallback state while the agent is still in setup.

The concrete #2410 reproduction is Codex showing its first-run Trust this directory prompt. Herdr returned interactive_ready: true before the normal Codex prompt was visible, so an immediate agent prompt could be sent before Codex was ready.

With this change, a managed launch stays pending when the active detector can provide positive prompt evidence. It becomes ready only after that evidence arrives or a full lifecycle hook reports an authoritative idle state. Otherwise the caller's timeout still applies. Prompt evidence observed before the managed launch is also ignored.

Agent coverage

The policy comes from the active agent manifest. There is no hard-coded Codex path. With the current bundled manifests:

  • fixed by this change: amp, claude, codex, devin, grok, hermes, kiro, and maki. Their detectors already expose positive idle evidence, so generic startup Idle or Blocked no longer counts as ready
  • already covered when their Herdr lifecycle integration is active: pi, omp, mastracode, opencode, kilo, and kimi. This PR keeps their existing fallback behavior when hook authority is not active
  • not fixed by this change: gemini, cursor, agy, cline, copilot, droid, and qodercli. They do not currently expose positive idle evidence or full lifecycle authority, so they retain the existing generic readiness fallback

Adding reliable positive readiness for the last group needs per-agent evidence and is outside #2410.

Validation

  • reproduced early interactive_ready before a Codex prompt was visible on Windows
  • repeated the same startup path after the change and confirmed it stayed pending until the deliberate 8 second timeout
  • cargo test --bin herdr managed_agent_ -j 2
  • just check with Rust tests serialized

The Windows media-player test is quite flaky in the parallel batch. It passed alone and in the serialized full check. Might be worth a follow up fix eventually.

refs #2410

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Agent detection now initializes new agents as unknown and emits a dedicated process-detection event. Codex trust prompts enter a blocked state. Managed launches remain pending until idle detection. CLI startup returns agent_not_ready for blocked agents, with tests and documentation updated.

Changes

Managed-agent blocked startup

Layer / File(s) Summary
Detection initialization and Codex blocker rules
src/events.rs, src/pane.rs, src/terminal/state.rs, src/detect/manifests/codex.toml, website/agent-detection/codex.toml, src/detect/manifest/tests.rs
New detections start as unknown and publish AgentProcessDetected. Codex directory-trust prompts are detected as visible blockers.
Blocked managed-agent reconciliation
src/terminal/state.rs
Blocked launches remain pending without deadlines, preserve their names, activate on matching idle detection, and clear after process exit.
Process acquisition and completion handling
src/app/actions.rs
Managed launches suppress the initial idle completion notification. Later process-exit completions remain observable.
CLI startup contract and validation
src/cli/agent.rs, tests/cli/agent_transport.rs, docs/next/website/src/content/docs/...
Blocked startup returns agent_not_ready. Tests and English, Japanese, and Chinese documentation describe addressability until idle readiness.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Detector
  participant AppEvent
  participant TerminalState
  participant PaneState
  participant AgentCLI
  Detector->>AppEvent: publish AgentProcessDetected
  AppEvent->>TerminalState: begin process acquisition
  Detector->>TerminalState: report blocked state
  TerminalState->>PaneState: retain pending launch and suppress initial completion
  AgentCLI->>TerminalState: poll startup state
  TerminalState-->>AgentCLI: return agent_not_ready
  Detector->>TerminalState: report matching idle state
  TerminalState-->>AgentCLI: report interactive readiness
Loading

Possibly related PRs

Suggested labels: coderabbit-review

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: waiting for agent prompt readiness.
Description check ✅ Passed The description directly explains the readiness behavior, affected agents, motivation, and validation for the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@kangal-bot kangal-bot added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 9, 2026
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/terminal/state.rs (1)

1816-1945: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Activation gating correctly separates fallback agents from positive-idle-rule agents.

begin_managed_agent seeds prompt_ready/allow_blocked from require_visible_idle, and reconcile_managed_agent_at requires prompt_ready (or live full-lifecycle hook authority) for Idle activation, and allow_blocked for Blocked activation. This matches the objective that generic initial Idle/Blocked states no longer indicate readiness for agents with a positive idle rule, while agents without one keep existing fallback behavior.

Consider adding a short doc comment above ManagedAgentPhase::Pending explaining what prompt_ready and allow_blocked represent and how require_visible_idle seeds them, since the interaction between these two flags and the manifest rule is not obvious from the field names alone.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 42cbfeaa-aa3d-4d81-9c2c-1581a4c35972

📥 Commits

Reviewing files that changed from the base of the PR and between 1777e9b and 1996ced.

📒 Files selected for processing (12)
  • src/app/actions.rs
  • src/app/agents.rs
  • src/app/api.rs
  • src/app/api/agents.rs
  • src/app/input/mouse.rs
  • src/app/mod.rs
  • src/detect/manifest.rs
  • src/events.rs
  • src/pane.rs
  • src/persist/snapshot.rs
  • src/server/headless.rs
  • src/terminal/state.rs

@Pimpmuckl
Pimpmuckl force-pushed the issue/2410-agent-start-trust-readiness branch from 1996ced to f09eeac Compare August 9, 2026 00:38
@Pimpmuckl

Copy link
Copy Markdown
Contributor Author

🧹 Nitpick comments (1)

src/terminal/state.rs (1)> 1816-1945: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Activation gating correctly separates fallback agents from positive-idle-rule agents.
begin_managed_agent seeds prompt_ready/allow_blocked from require_visible_idle, and reconcile_managed_agent_at requires prompt_ready (or live full-lifecycle hook authority) for Idle activation, and allow_blocked for Blocked activation. This matches the objective that generic initial Idle/Blocked states no longer indicate readiness for agents with a positive idle rule, while agents without one keep existing fallback behavior.
Consider adding a short doc comment above ManagedAgentPhase::Pending explaining what prompt_ready and allow_blocked represent and how require_visible_idle seeds them, since the interaction between these two flags and the manifest rule is not obvious from the field names alone.

Sure, why not, added the comment in f09eeac

@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR makes managed agent startup wait for positive prompt-ready evidence when the active manifest supports it, preventing generic startup states from being treated as interactive readiness.

  • Tracks process-detection evidence separately from lifecycle state and ignores prompt evidence predating a managed launch.
  • Publishes readiness-only transitions to API clients and suppresses misleading startup-completion notifications.
  • Adds Codex trust-prompt detection, readiness tests, transport coverage, and localized documentation updates.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/terminal/state.rs Adds prompt-evidence timestamps and manifest-aware managed-launch reconciliation so positive readiness must postdate launch.
src/app/actions.rs Processes agent-detection events, reconciles managed launches, and suppresses startup transitions from appearing as completed work.
src/server/headless.rs Forwards readiness-relevant pane updates while avoiding misleading startup completion notifications.
src/app/api.rs Publishes managed-agent readiness-only changes to API subscribers.
src/pane.rs Emits explicit process-detection events separately from screen-derived lifecycle state.
src/detect/manifests/codex.toml Classifies Codex’s trust-directory prompt as blocked rather than prompt-ready.
tests/cli/agent_transport.rs Extends transport coverage for delayed positive readiness and blocked startup behavior.

Sequence Diagram

sequenceDiagram
  participant CLI as agent start
  participant API as App/API
  participant Detector as Agent detector
  participant State as TerminalState
  CLI->>API: start managed agent
  API->>State: begin pending launch
  Detector->>State: process detected
  Detector->>State: lifecycle state
  alt positive prompt evidence or authoritative idle
    State->>State: mark launch active
    API-->>CLI: "interactive_ready = true"
  else blocked
    API-->>CLI: agent_not_ready
  else timeout
    API-->>CLI: timeout
  end
Loading

Reviews (7): Last reviewed commit: "Merge branch 'master' into issue/2410-ag..." | Re-trigger Greptile

Comment thread src/terminal/state.rs
@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

@ogulcancelik ogulcancelik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i feel like a better approach would be to keep startup readiness based on the detection engine’s public state instead of making agent start inspect manifest details.

if detection reports blocked, startup should never set interactive_ready: true. ideally it should return immediately with a blocked/not-ready result rather than waiting for the 30-second timeout (we can also lower it to 10 imo 30 feels long). idle succeeds, while unknown keeps waiting.

if the codex trust screen is not reliably classified as blocked—or falls back to idle—that seems like a detection problem we should fix there. this would be smaller and keep visible_idle as detector evidence rather than turning it into startup policy.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/server/headless.rs (1)

11064-11068: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider covering handle_internal_event_with_pane_updates in the bypass guard test.

The guard test only matches self.app.handle_internal_event( and self.app.handle_internal_event_with_prefix_sync(. The forwarding method now also uses self.app.handle_internal_event_with_pane_updates(. A future direct call to that method outside handle_internal_event_with_forwarding would bypass forwarding without failing this test.

♻️ Proposed guard extension
             } else if (line.contains("self.app.handle_internal_event(")
-                || line.contains("self.app.handle_internal_event_with_prefix_sync("))
+                || line.contains("self.app.handle_internal_event_with_prefix_sync(")
+                || line.contains("self.app.handle_internal_event_with_pane_updates("))
                 && !line.trim().starts_with("///")
                 && !line.contains("contains(")
tests/live_handoff.rs (1)

1477-1497: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound the marker-file wait loops in the helper scripts.

Both scripts spin on while [ ! -f ... ]; do sleep 0.01; done with no upper bound. If the counterpart process never creates its marker, for example when server.live_handoff fails before spawning the importer, the script spins until the test harness terminates it. A bounded loop turns that case into a fast, diagnosable failure instead of a busy spin.

♻️ Proposed bounded waits
     fs::write(
         &fake_codex,
         "#!/bin/sh\n\
          exec env HERDR_AGENT=codex /bin/sh -c '\n\
            printf \"\\033[2J\\033[H> You are in /tmp\\n\\n  Do you trust the contents of this directory?\\n\"\n\
            touch \"$HERDR_TEST_AGENT_STARTED\"\n\
-           while [ ! -f \"$HERDR_TEST_IMPORTER_STARTED\" ]; do sleep 0.01; done\n\
+           i=0\n\
+           while [ ! -f \"$HERDR_TEST_IMPORTER_STARTED\" ] && [ \"$i\" -lt 3000 ]; do sleep 0.01; i=$((i+1)); done\n\
            touch \"$HERDR_TEST_AGENT_EXITED\"\n\
          '\n",
     )
     .unwrap();
     fs::write(
         &import_wrapper,
         "#!/bin/sh\n\
          touch \"$HERDR_TEST_IMPORTER_STARTED\"\n\
-         while [ ! -f \"$HERDR_TEST_AGENT_EXITED\" ]; do sleep 0.01; done\n\
+         i=0\n\
+         while [ ! -f \"$HERDR_TEST_AGENT_EXITED\" ] && [ \"$i\" -lt 3000 ]; do sleep 0.01; i=$((i+1)); done\n\
          exec \"$HERDR_TEST_HERDR_EXE\" \"$@\"\n",
     )
     .unwrap();

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ed86ff94-d654-4ecc-af8b-ed300f76d16c

📥 Commits

Reviewing files that changed from the base of the PR and between f09eeac and b42fd4b.

📒 Files selected for processing (23)
  • docs/next/website/src/content/docs/agent-automation.mdx
  • docs/next/website/src/content/docs/cli-reference.mdx
  • docs/next/website/src/content/docs/ja/agent-automation.mdx
  • docs/next/website/src/content/docs/ja/cli-reference.mdx
  • docs/next/website/src/content/docs/zh-cn/agent-automation.mdx
  • docs/next/website/src/content/docs/zh-cn/cli-reference.mdx
  • src/app/actions.rs
  • src/app/agent_resume.rs
  • src/app/agents.rs
  • src/app/api.rs
  • src/cli/agent.rs
  • src/detect/manifest/tests.rs
  • src/detect/manifests/codex.toml
  • src/events.rs
  • src/pane.rs
  • src/persist/restore.rs
  • src/persist/snapshot.rs
  • src/server/headless.rs
  • src/terminal/runtime.rs
  • src/terminal/state.rs
  • tests/cli/agent_transport.rs
  • tests/live_handoff.rs
  • website/agent-detection/codex.toml

Comment thread src/detect/manifests/codex.toml Outdated
Comment thread src/server/headless.rs
Comment thread src/terminal/state.rs
@Pimpmuckl
Pimpmuckl force-pushed the issue/2410-agent-start-trust-readiness branch 2 times, most recently from e471cab to 4bfd61c Compare August 9, 2026 13:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: df92ee09-234d-46b0-a32c-2fb31078f049

📥 Commits

Reviewing files that changed from the base of the PR and between e471cab and 4bfd61c.

📒 Files selected for processing (4)
  • src/app/actions.rs
  • src/events.rs
  • src/pane.rs
  • src/terminal/state.rs

Comment thread src/app/actions.rs
@Pimpmuckl
Pimpmuckl force-pushed the issue/2410-agent-start-trust-readiness branch from 4bfd61c to 28c4a9b Compare August 9, 2026 13:55
@Pimpmuckl

Copy link
Copy Markdown
Contributor Author

Thanks, yep agreed. I reworked this around the detector's public state.

The race was earlier than startup policy. Process detection published a temporary Idle, then skipped screen scans for the same three seconds used as the startup settle window. Startup could therefore complete before detection had classified the actual screen.

When Herdr sees the process now, it publishes the agent identity as Unknown during that grace period. Idle succeeds, Blocked returns agent_not_ready immediately while keeping the name usable, and Working or Unknown keep waiting.

The first live Idle after process acquisition means ready, not completed work. A process exit still counts as completion, and later Unknown to Idle transitions for the same known agent keep their existing completion behavior. The same suppression now reaches the headless notification path, so sound and toast forwarding follow the same rule.

I also reproduced the Codex trust screen, including the compact seven-row layout. The detector now anchors on the live header and question, handles wrapping, and does not match trust text copied into a conversation.

This removes the manifest introspection and visible_idle startup policy from the previous version.

I also looked at 30s vs 10s. We do not currently record enough startup timing data to have super strong data. The detector already spends a fixed three seconds in acquisition grace, so 10 seconds leaves roughly seven seconds for the agent itself to start and reach a known state. So to me 10s sounds a bit too tight.

Now that Blocked returns immediately, the 30-second wait only applies while detection is still Working or Unknown. Callers that want faster failure can already use --timeout 10000. So I kept 30s overall, but can be tightened down eventually.

@Pimpmuckl
Pimpmuckl requested a review from ogulcancelik August 9, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Trigger automated AI reviews for pull requests admitted by the PR gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants