fix: retry sends after stale agent stop#1128
Conversation
…tore' into fix/send-after-stop-composer-restore # Conflicts: # packages/server/src/server/agent/agent-manager.test.ts # packages/server/src/server/session.ts
|
| 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
Reviews (1): Last reviewed commit: "Merge branch 'main' into fix/send-after-..." | Re-trigger Greptile
| private shouldRetrySendAfterStartFailure(agentId: string): boolean { | ||
| const agent = this.agentManager.getAgent(agentId); | ||
| if (!agent || agent.session == null) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; |
There was a problem hiding this comment.
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.
| private async retrySendAgentMessageAfterSessionReload(input: { | ||
| agentId: string; | ||
| text: string; | ||
| prompt: AgentPromptInput; | ||
| messageId?: string; | ||
| runOptions?: AgentRunOptions; | ||
| }): Promise<void> { |
There was a problem hiding this comment.
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.
| 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> { |
No description provided.