Add no-progress turn detector that inspects the most recent assistant
message: if finish is 'unknown', all token counts are zero, and there
is no meaningful content beyond step-start/step-finish markers, the
turn is classified as no-progress.
Integrate the check at all three idle/completion/error continuation
points in the event handler so the loop stops cleanly with a warning
toast instead of injecting another internal prompt.
/start-work was unresponsive under Atlas because ralph-loop and todo-continuation-enforcer normalized the inherited agent to a config key (e.g. 'atlas') while OpenCode only accepts the registered display name (e.g. 'Atlas (Plan Executor)'), producing 'Agent not found' on dispatch.
Prefer resolveRegisteredAgentName(agent) and fall back to normalizeAgentForPromptKey only when no registration exists, mirroring the start-work hook's resolution chain.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Atlas task() with run_in_background=false crashed with 'The "path" property must be of type string, got object' from Node's path.isAbsolute validation upstream of the SDK.
All callers (sync-prompt-sender, boulder-continuation-injector, idle-event, session-route, model-suggestion-retry) already route through dispatchInternalPrompt, so centralizing the compatibility shim in prompt-async-gate.ts covers every Bug 1 site without touching individual hooks.
On TypeError matching the object-path signature, retry once with path collapsed to its id string. Types broaden PromptSessionPath to string | { id }.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Remove non-store tool names from the metadata recovery warning gate so call_omo_agent and dead aliases fail open without warning spam. Keep warnings for tools backed by the recovery store: background_output, edit, and task.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Root cause: tool.execute.after attempted metadata recovery for every tool, including built-in tools that never publish recoverable OMO metadata. On Windows this produced repeated recovery warnings across read/bash/glob/grep/todowrite/lsp/apply_patch and amplified stalled-session diagnostics, while metadata-linked tools still needed diagnostic visibility when their linkage was stale.
Limit the warning to tools expected to publish recoverable metadata and keep all other built-in tools fail-open after missing metadata.
Fixes#4449
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Filter host config agent aliases with the same protected builtin-name rules used for external agent sources so stale display-name entries cannot replace resolved user-configured models when team mode is enabled.
Fixes#4429
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add explicit .helpOption('-h, --help') call to the Commander program configuration so the help option is explicitly defined rather than relying on Commander.js lazy initialization. Add corresponding test to pin the behavior.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
After the prompts-core migration the TypeScript prompt sources were
deleted; the only mechanism delivering markdown prompts to npm users
is bun build inlining via bunfig.toml [loader] ".md" = "text"
and import attributes. bun test runs from src/index.ts, not from
dist/index.js, so a future Bun upgrade that silently regresses
markdown inlining would pass source tests green while the published
bundle is broken with Cannot find module ../prompts/atlas/default.md
at first agent load.
Add a smoke test that scans the built dist/index.js for unique
signature strings from each migrated prompt file (15 signatures:
3 ultrawork + 5 atlas + 3 prometheus + 4 mode prompts). Skips
gracefully if dist/index.js does not exist (local bun test before
build). Wire into the existing CI Verify dist bundle tests step in
.github/workflows/ci.yml so the regression catches in CI build.
Closes pre-publish blocker V1 and V33.
The double-fire race fix (#4256) had handlePendingVerification return
early when verification_attempt_id was set but verification_session_id
was not, since the oracle dispatch is still in flight. That guard
introduced a permanent-stall failure mode: if tool-execute-after never
runs (oracle session hangs, crashes, OOM-killed, or tmux killed
externally), verification_session_id stays undefined forever and the
ralph-loop never escapes the pending-verification state.
Track verification_attempt_started_at as a state field, clear it on
restart/clear/setVerificationSessionID, and fall through to
handleFailedVerification when the attempt has been pending past
STUCK_VERIFICATION_TIMEOUT_MS (30 minutes). Legacy persisted states
without the timestamp continue to defer (no timeout to evaluate),
matching pre-fix behavior for that edge case.
Closes pre-publish blocker V25.
shouldDeferParentWakeForSessionHistory previously had only one escape
path from the defer state: stale pending tool call. If the assistant
had unfinished text but no pending tool call (session crashed
mid-stream, model errored after partial text, network died), the
escape never fired and parent-wake deferred forever. Background-agent
completions never woke the parent.
Add a second escape: when the assistant text blocks but no tool wait
is pending, dispatch the wake after toolCallDeferMaxMs anyway. The
prompt-async-gate still defends if the assistant text turns out to be
live; we just stop deferring indefinitely.
Closes pre-publish blocker V11.
extractRetryableSignal returns the raw isRetryable hint from up to 5
nested AI SDK error paths. isRetryableError previously trusted any
true result blindly, which would burn every configured fallback model
in an infinite loop if a provider mis-tagged a 401, 403, or other
non-transient 4xx as retryable.
Honor the signal only when the status code is absent, in the configured
retry_on_errors list, in 5xx, or in {408, 425, 429}. Reject the signal
when the status code is a non-transient 4xx and log the rejection so
operators can debug provider mis-classifications.
Closes pre-publish blocker V8.