Skip to content

fix: retry sends after stale agent stop#1128

Open
yaukwan wants to merge 5 commits into
getpaseo:mainfrom
yaukwan:fix/send-after-stop-composer-restore
Open

fix: retry sends after stale agent stop#1128
yaukwan wants to merge 5 commits into
getpaseo:mainfrom
yaukwan:fix/send-after-stop-composer-restore

Conversation

@yaukwan
Copy link
Copy Markdown
Contributor

@yaukwan yaukwan commented May 21, 2026

No description provided.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 27, 2026

Greptile Summary

This PR adds automatic retry logic to two send_agent_message code paths so that a message is re-dispatched when the underlying provider session becomes stale after an agent stop. A new waitForAgentRunStartOrThrow helper inlines the existing waitForAgentRunStartWithTimeout utility, removing the need for that import.

  • session.ts: Extracts dispatchAgentMessagePrompt, retrySendAgentMessageAfterSessionReload, and waitForAgentRunStartOrThrow helpers, then wires them into both the RPC (handleSendAgentMessage) and WebSocket request (handleSendAgentMessageRequest) paths with a single-shot reload-and-retry on failure.
  • agent-manager.test.ts: Adds a focused unit test using a FailsOnceBeforeTurnSession stub that verifies reloadAgentSession recovers the stale state and the next runAgent succeeds.
  • daemon-client.e2e.test.ts: Removes two unused imports left over from a previous refactor.

Confidence Score: 4/5

Safe to merge — the retry path is single-shot with proper error surfacing on failure, and the new test covers the core recovery flow end-to-end.

The retry guard fires for any active agent regardless of error type, so non-stale-session failures also trigger a session reload and run cancellation. The effect is bounded but the guard could be more precise. There is also a dead text parameter in two helper method signatures.

packages/server/src/server/session.ts — specifically the shouldRetrySendAfterStartFailure condition and the unused text fields in the two new helper input types.

Important Files Changed

Filename Overview
packages/server/src/server/session.ts Adds retry logic (with session reload) for two send-message paths; the guard condition is broader than the targeted scenario and two helper methods carry an unused text parameter.
packages/server/src/server/agent/agent-manager.test.ts Adds a well-structured test that verifies reloadAgentSession recovers a stale session and that a subsequent runAgent succeeds; no issues found.
packages/server/src/server/daemon-client.e2e.test.ts Removes two unused imports (AgentSession, AgentStreamEvent) — clean housekeeping with no functional impact.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Session
    participant AgentManager
    participant Provider

    Client->>Session: send_agent_message
    Session->>AgentManager: sendPromptToAgent / waitForAgentRunStart

    alt Success
        AgentManager->>Provider: startTurn()
        Provider-->>AgentManager: "{ turnId }"
        AgentManager-->>Session: run started
        Session-->>Client: "{ ok: true }"
    else Failure (stale session)
        Provider-->>AgentManager: throws session unavailable
        AgentManager-->>Session: error
        Session->>Session: shouldRetrySendAfterStartFailure?
        note right of Session: checks agent.session != null
        Session->>AgentManager: reloadAgentSession()
        AgentManager->>AgentManager: cancel in-flight run
        AgentManager->>Provider: resumeSession / createSession
        Provider-->>AgentManager: new session
        Session->>AgentManager: sendPromptToAgent (retry)
        AgentManager->>Provider: startTurn()
        Provider-->>AgentManager: "{ turnId }"
        AgentManager-->>Session: run started
        Session-->>Client: "{ ok: true }"
    else Retry also fails
        AgentManager-->>Session: retryError
        Session->>Session: handleAgentRunError
        Session-->>Client: "{ ok: false, error }"
    end
Loading

Reviews (1): Last reviewed commit: "Merge branch 'main' into fix/send-after-..." | Re-trigger Greptile

Comment on lines +1232 to +1238
private shouldRetrySendAfterStartFailure(agentId: string): boolean {
const agent = this.agentManager.getAgent(agentId);
if (!agent || agent.session == null) {
return false;
}

return true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Retry condition is unconditionally true for all active agents

agent.session != null holds for virtually every active agent, so this guard returns true for any error thrown by sendPromptToAgent — including storage failures, lifecycle state violations, and transient errors that have nothing to do with a stale provider session. When one of those unrelated errors triggers the condition, reloadAgentSession is still called, which cancels any in-flight run and tears down the current session before re-dispatching. A more targeted check (e.g. matching a specific error type/message emitted when the provider session becomes unavailable, or inspecting a dedicated lifecycle field) would confine the reload to the scenario the PR actually intends to fix.

Comment on lines +1241 to +1247
private async retrySendAgentMessageAfterSessionReload(input: {
agentId: string;
text: string;
prompt: AgentPromptInput;
messageId?: string;
runOptions?: AgentRunOptions;
}): Promise<void> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The text field is declared in the input shapes for both helpers but is never referenced inside their bodies — only prompt, agentId, messageId, and runOptions are forwarded to sendPromptToAgent. This is dead code at the parameter level and conflicts with the project's noUnusedLocals / noUnusedParameters discipline.

Suggested change
private async retrySendAgentMessageAfterSessionReload(input: {
agentId: string;
text: string;
prompt: AgentPromptInput;
messageId?: string;
runOptions?: AgentRunOptions;
}): Promise<void> {
private async retrySendAgentMessageAfterSessionReload(input: {
agentId: string;
prompt: AgentPromptInput;
messageId?: string;
runOptions?: AgentRunOptions;
}): Promise<void> {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant