Skip to content

feat(settings): configurable provider retry ceiling and first-retry wait - #1381

Open
shabhui wants to merge 1 commit into
vastsa:mainfrom
shabhui:feat/provider-retry-interval
Open

shabhui wants to merge 1 commit into
vastsa:mainfrom
shabhui:feat/provider-retry-interval

Conversation

@shabhui

@shabhui shabhui commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds two optional numeric settings that customize the shared provider retry machinery (429 rate-limit and transient-failure budgets) without removing its guard rails:

  • providerRetryMaxAttempts — any non-negative integer ceiling shared by both budgets. 0 means unlimited, exactly like the existing infiniteProviderRetry switch; the switch keeps winning over any configured value.
  • **providerRetryInitialDelayMs** — any non-negative first-retry wait in milliseconds. 0 retries immediately. Later retries keep doubling from it, and the exponential cap rises with it (max(shipped cap, configured wait)), so a long custom interval is honored rather than truncated. Server Retry-After` keeps precedence in both backoffs.

Design notes

  • Ranges are deliberately loose: the UI and both write boundaries only reject NaN / negative / fractional junk. No artificial upper bound on either field.
  • Both fields normalize on read and validate on write at renderer and main boundaries; stored junk drops back to the shipped defaults (10 retries, 2000 ms).
  • The retrying activity now carries the effective ceiling (maxAttempts), so the transcript status shows attempt 3/7 instead of the hardcoded /10; unlimited shows ∞.
  • Turning infiniteProviderRetry back off restores the configured ceiling (the switch no longer pins the internal budget to 0), and live session reuse updates the policy through the new setProviderRetryPolicy.
  • The stream-retry adapter receives the custom pacing through an optional initialDelayMs controller hook, so pre-stream and mid-stream retries share one pacing.

Files

  • packages/shared: retry settings type + normalize helpers + retrying-activity maxAttempts field.
  • packages/agent-runtime: budget checks honor the ceiling (0 = unlimited); both delay functions accept initialDelayMs and raise their caps; sidecar reuses/constructs with the policy.
  • apps/desktop: settings UI rows (General, next to the infinite-retry switch), write validation on both boundaries, transcript retry status, settings search keys.
  • packages/i18n: 9 locales.
  • Spec docs (03-runtime/02-agent-runtime.md) updated in en + zh-CN.

Test plan

  • packages/shared vitest: 1176 passed (4 new normalize cases)
  • packages/agent-runtime provider-retry.test.ts: 48 passed (5 new custom-pacing cases: scaling, cap raising, Retry-After precedence, zero-immediate, jitter)
  • packages/agent-runtime runtime.test.ts: 276 passed (new: smaller custom ceiling across both budgets, zero-ceiling unlimited, toggle-off restores ceiling, retrying activity reports maxAttempts)
  • apps/desktop settings-roundtrip.test.mjs: 5 passed (round-trip of both fields incl. 0/0, junk rejection at both boundaries, renderer drops stored junk)
  • tsc -p tsconfig.json --noEmit clean for agent-runtime and desktop
  • biome check on all touched files
  • CI full suite

Closes #759 follow-up territory: gives finite custom ceilings and pacing instead of only the unlimited boolean.

Two optional numeric settings customize the shared provider retry
machinery without removing its guard rails:

- providerRetryMaxAttempts: any non-negative integer ceiling shared by
  the 429 and transient budgets; 0 means unlimited, exactly like the
  infiniteProviderRetry switch, which keeps winning over any value.
- providerRetryInitialDelayMs: any non-negative first-retry wait in
  milliseconds; 0 retries immediately. Later retries still double from
  it, and the exponential cap rises with it (max of shipped cap and the
  configured wait), so long custom intervals are honored, never
  truncated. Server Retry-After keeps precedence in both backoffs.

Both fields normalize on read and validate on write at renderer and
main boundaries; junk values drop back to shipped defaults. The
retrying activity now reports the effective ceiling (maxAttempts) so
transcript status reflects the configured budget, and turning
infiniteProviderRetry back off restores the configured ceiling. The
stream-retry adapter receives the custom pacing through its controller,
and live session reuse updates it via setProviderRetryPolicy.
@shabhui
shabhui force-pushed the feat/provider-retry-interval branch from 2585dbf to 7e49172 Compare October 4, 2026 06:37

This branch has not been deployed

No deployments
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