Back to home@syncended

deepseek-harness-retry

No description

Stars
0
Language
TypeScript
Created
Aug 19, 2026
Updated
Aug 29, 2026
GitHub repo

Introduction

DeepSeek Harness Retry

Configurable automatic retries and interrupted-session recovery for DeepSeek Harness.

The plugin handles model failures that are often successful when the same request is tried again. It joins the Host agent/request-error waterfall, lets DSH's built-in recovery policies decide first, and then applies a bounded exponential-backoff fallback for configured provider-neutral error codes.

Features

  • Handles generic PI_AI_ERROR, UNKNOWN, and other configured transient model failures.
  • Retries up to 2 times after the original request by default.
  • Retries explicit Codex servers are currently overloaded failures up to 5 times with a longer delay.
  • Exponential backoff with jitter and provider Retry-After support.
  • Exact provider-route allowlists and denylists.
  • Abort-aware waits that stop on user cancellation or plugin disposal.
  • Defers to adapter-owned and built-in DSH recovery before applying its fallback budget.
  • Emits standard non-surface llm/retry and llm/retry-started session events.
  • Can safely continue only provably unfinished work after a DSH crash or restart.
  • Never removes, reorders, or replaces an existing durable inbox item.

Retry errors and delays are not added to model-visible history. Every retry is a new provider request and may incur additional usage charges.

Requirements

  • Node.js 20 or newer.
  • DeepSeek Harness 0.1.0-rc.7+ or 0.1.1-rc.2+.
  • The built-in @deepseek-ai/dsh-llm-retry plugin. Do not remove it; this plugin extends rather than replaces its policies.
  • pnpm/Corepack only when developing from source.

This is a Host plugin. It has no browser connection step and requires no plugin-specific environment variables.

Install

Install the published package into a profile:

dsh plugin --profile web add @syncended/dsh-retry

Some pnpm-backed profiles require the workspace-root flag:

dsh plugin --profile web add -w @syncended/dsh-retry

Install the current trunk branch from GitHub only for development or testing:

dsh plugin --profile web add github:syncended/deepseek-harness-retry

The package declares a DSH bundle, so the CLI inserts the Host plugin as model-error-retry; no manual plugin entry is required. Restart the selected DSH profile after installing or upgrading.

To remove it:

dsh plugin --profile web remove @syncended/dsh-retry

Current DSH releases already retry EMPTY_RESPONSE, RATE_LIMIT, SERVER, TIMEOUT, and TRANSPORT through @deepseek-ai/dsh-llm-retry. That adapter-owned normal/always policy always runs first. The fallback defaults here primarily cover generic model failures such as PI_AI_ERROR and do not widen a built-in policy's retry budget.

Configuration

The defaults apply immediately after installation. To customize them, edit the existing model-error-retry row in $DSH_HOME/profiles/<profile>/cordis.patch.yml; do not add a duplicate row with the same id.

- id: model-error-retry
  config:
    maxRetries: 3
    overloadMaxRetries: 5
    retryableCodes:
      - PI_AI_ERROR
      - UNKNOWN
      - STREAM_CLOSED
      - EMPTY_RESPONSE
      - RATE_LIMIT
      - SERVER
      - TIMEOUT
      - TRANSPORT
    initialDelayMs: 750
    overloadInitialDelayMs: 2000
    maxDelayMs: 15000
    jitterRatio: 0.15
    respectRetryAfter: true
    resumeInterrupted: true
    resumeDisposed: false
    resumeMaxAgeMs: 86400000
    resumePrompt: >-
      DeepSeek Harness stopped before the previous task produced a complete final
      assistant response, or before a scheduled retry started. Continue the unfinished
      work from the durable session history. Re-check the current workspace and
      external state before acting. Do not blindly repeat tool calls that may have side
      effects; verify their outcome first.
    providers:
      - openai
      - openrouter
    excludeProviders: []

Restart the Host after changing this file.

OptionDefaultDescription
maxRetries2Retries after an ordinary original failure. 0 disables request-error retries but not resumeInterrupted.
overloadMaxRetries5Retries for PI_AI_ERROR with an explicit overload message.
retryableCodessee belowExact provider-neutral codes. * matches any request error.
initialDelayMs500Initial ordinary exponential-backoff delay.
overloadInitialDelayMs2000Initial explicit-overload delay.
maxDelayMs10000Maximum local delay. A larger Retry-After leaves the failure terminal.
jitterRatio0.1Symmetric jitter ratio from 0 to 1.
providers[]Exact provider-route allowlist; empty allows all routes.
excludeProviders[]Exact provider-route denylist; takes precedence over the allowlist.
respectRetryAftertrueHonor provider Retry-After when it does not exceed maxDelayMs.
resumeInterruptedtrueOn cold resume, continue an unmatched plugin retry, a model step with no assistant message, or a durably closed tool boundary before its next model step.
resumeDisposedfalseAlso accept aborted/disposed as an unfinished retry. Disabled because HMR and intentional teardown can produce this reason.
resumeMaxAgeMs86400000Oldest pending retry eligible for automatic continuation; 24 hours by default.
resumePromptbuilt inModel-visible safety instruction for a confirmed interrupted continuation.

Default fallback retry codes:

STREAM_CLOSED, MALFORMED_RESPONSE, PI_AI_ERROR, API_ERROR, MODEL_ERROR,
INTERNAL_ERROR, PROVIDER_ERROR, UNKNOWN

Permanent failures such as AUTH, INVALID_REQUEST, MISSING_CREDENTIAL, UNKNOWN_MODEL, and context overflow are not retried by default. Add an exact code or * only after considering cost and repeated side effects.

Validation rules

  • Retry counts must be non-negative safe integers.
  • initialDelayMs and overloadInitialDelayMs must not exceed maxDelayMs.
  • jitterRatio must be between 0 and 1.
  • retryableCodes cannot be empty while request-error retries are enabled.
  • resumePrompt cannot be empty when interrupted-session recovery is enabled.
  • Provider filters compare exact route strings; they are not regular expressions or prefixes.

How request retries work

  1. The agent loop receives a terminal model request failure.
  2. The plugin calls the next handler in the agent/request-error waterfall.
  3. If a built-in handler already returns { kind: "retry" }, that decision is returned unchanged.
  4. Otherwise, the plugin checks the provider route, failure code, and local budget.
  5. It appends llm/retry to the session log and waits through abort-aware backoff.
  6. It appends llm/retry-started and returns { kind: "retry" }; DSH rebuilds the request from durable history.

Partially received chunks are excluded from the next model-visible request because retry happens at the closed request-error boundary.

Recovery after a DSH restart

  1. After llm/retry, the plugin requests a persistence checkpoint before backoff. If checkpoints are unavailable, the live retry proceeds but restart recovery for that attempt is best-effort.
  2. After a crash, DSH persistence balances an open tail with synthetic tool errors, step/end, and turn/end events whose reason is interrupted.
  3. When Web/API later resumes the cold session through agents.resume, the plugin accepts one of three durable proofs: its own llm/retry without llm/retry-started; the latest crash-interrupted model step containing durable user input but no assistant/message; or a tool-calling assistant message for which every call has a durable real or synthetic result and no next model step started.
  4. A completed text-only assistant message, manual aborted/user interrupt, tool call missing any durable result, foreign policy key, already-started retry, subagent, or existing inbox item fails closed. aborted/disposed also fails closed by default.
  5. Detection and enqueue run inside agent.runMaintenance() with the exact live Agent checked again.
  6. Only an empty inbox receives one deterministic model-visible continuation notice, which opens a new turn over the persisted history.

A provider stream and old JavaScript promise cannot survive a process restart. Recovery is therefore a semantic continuation in a new turn, not a continuation of the same response bytes. The plugin does not scan and launch archived sessions at startup; it checks lazily when a session is reattached.

Observability and troubleshooting

Retries appear as llm/retry and llm/retry-started entries in the durable session log and as Host warning logs. If a request is not retried:

  1. Confirm the package is installed in the active profile and the Host was restarted.
  2. Inspect the failure's provider-neutral code and provider route.
  3. Check retryableCodes, providers, and excludeProviders for an exact match.
  4. Check whether built-in recovery already consumed the decision or the local retry budget was exhausted.
  5. Check whether Retry-After exceeded maxDelayMs.

There is no dedicated UI, so a screenshot would not add useful setup information; session events and Host logs are the authoritative diagnostics.

Development

pnpm install
pnpm check
npm pack --dry-run

Tag-driven npm publication is documented in RELEASING.md.

License

MIT — see LICENSE.