Background and Goal
Jan Agent recovers context-window overflow and avoids executing truncated tool-call arguments, but transient provider failures, ordinary output truncation, and dropped MCP connections still end a run without a bounded recovery path. Add safe, observable recovery for those cases without replaying output or side-effecting tools.
Approach
- Add one typed transient-retry policy at the model invocation boundary. Retry only pre-output transport failures, timeouts,
429, and retryable 5xx responses.
- Keep existing context-overflow compaction separate. Treat cancellation, request validation or other
4xx responses, malformed tool arguments, and unsupported requests as terminal.
- Apply existing credential rotation before the ordinary retry path. Honor an upstream
Retry-After value when present; otherwise use bounded exponential backoff with jitter.
- Gate replay on emitted state. A request that has produced visible text, an image, a tool call, or a completed tool result must not be resent automatically.
- Normalize
finish_reason: "length" for ordinary text into an incomplete-output result carrying the partial text and continuation metadata. Continue only with an append-safe follow-up, never by replaying the original request.
- Replace polling-only MCP recovery with bounded reconnect-on-transport-close. A tool call may retry once only when it is idempotent or explicitly marked retry-safe; mutating calls must surface their error instead.
Acceptance Criteria
- A transient provider failure before visible output retries at most three times with cancellation-aware exponential backoff, jitter, and
Retry-After support.
- Context overflow continues through the existing compaction path and is never handled by the transient retry policy.
- A
400 validation error, malformed tool-call arguments, user cancellation, or a failure after visible output/tool emission does not automatically replay the request.
- Credential rotation remains the first response for configured-key authentication or rate-limit handling; a single-key provider follows the bounded transient-retry policy only when the error is retryable.
- A normal text response ending at the output limit retains the emitted text and uses an append-safe continuation without duplicating it. Truncated tool-call arguments remain non-executable.
- A dropped MCP transport reconnects with a capped attempt/window policy. At most one retry is made for idempotent or explicitly retry-safe calls; mutating calls are never replayed automatically.
- Focused tests cover retry classification, retry limits and cancellation, replay-safety gates,
Retry-After, text-length continuation, and MCP reconnect/retry boundaries.
Non-goals
- New provider credentials, model fallback chains, or retrying arbitrary
4xx failures.
- Automatic replay of write, edit, exec, or other side-effecting tool calls.
- Changes to the context-compaction algorithm beyond preserving its existing recovery boundary.
Migrated from janhq/jan-internal#326.
Background and Goal
Jan Agent recovers context-window overflow and avoids executing truncated tool-call arguments, but transient provider failures, ordinary output truncation, and dropped MCP connections still end a run without a bounded recovery path. Add safe, observable recovery for those cases without replaying output or side-effecting tools.
Approach
429, and retryable5xxresponses.4xxresponses, malformed tool arguments, and unsupported requests as terminal.Retry-Aftervalue when present; otherwise use bounded exponential backoff with jitter.finish_reason: "length"for ordinary text into an incomplete-output result carrying the partial text and continuation metadata. Continue only with an append-safe follow-up, never by replaying the original request.Acceptance Criteria
Retry-Aftersupport.400validation error, malformed tool-call arguments, user cancellation, or a failure after visible output/tool emission does not automatically replay the request.Retry-After, text-length continuation, and MCP reconnect/retry boundaries.Non-goals
4xxfailures.Migrated from janhq/jan-internal#326.