v2.1.1: harden Decixa resolve() call path with timeout + retry#4
Conversation
Addresses @koki-socialgist review feedback from Discord thread. ## Behavior - AbortController-based request timeout (default 4000ms) - Retry on transient failures: network errors, AbortError/timeouts, 5xx - No retry on 4xx (deterministic client errors \u2014 bad input, auth, not-found) - Exponential backoff: 250ms \u00d7 2^(attempt-1), default 2 retries (3 total attempts) - Response includes `discovery_attempts` so consumers see how many tries were needed - Fallback response includes `discovery_error` with the last failure reason ## Tunable via env - `DECIXA_RESOLVE_TIMEOUT_MS` (default: 4000) - `DECIXA_RESOLVE_MAX_RETRIES` (default: 2) - `DECIXA_RESOLVE_RETRY_BASE_MS` (default: 250) ## Verified - Happy path: returns Decixa response with discovery_source='decixa', attempts=1 - Forced timeout (TIMEOUT_MS=1): returns local fallback with discovery_source='local_fallback', discovery_attempts=2, discovery_error='AbortError: This operation was aborted' - 4xx non-retry: breaks retry loop immediately (logs noted in stderr)
koki-socialgist
left a comment
There was a problem hiding this comment.
Reviewed end-to-end. Implementation is clean — AbortController + retry classification + env tunables. Good shape.
On your three questions
1. Defaults (4s timeout, 2 retries) — OK as-is.
Decixa /api/agent/resolve p95 sits well under 4s for typical intent queries. Worst-case envelope ~12.7s is at the edge of MCP UX tolerance but reasonable — leaves headroom for Vercel cold starts on our end. Wouldn't tighten.
2. 429 — yes, please retry.
Decixa doesn't emit 429 today, but if Vercel platform throttling kicks in at scale, 429 + Retry-After is the standard contract. Treat as retryable, honor Retry-After if present (cap to your max envelope), exponential otherwise. Forward-compatible, zero cost today.
3. Jitter — small win, worth adding.
At current volume not strictly needed, but ±25% jitter on the backoff (delay * (0.75 + Math.random() * 0.5)) is a 1-liner that future-proofs against thundering herd if multiple agents hit the same Decixa cold start. I'd add it.
One tiny nit
lastStatus is captured but never surfaced. Either drop the variable or include discovery_last_http_status in the fallback response for debugging.
Approving as-is. Up to you whether to fold jitter / 429 into this PR or ship in v2.1.2.
🙏
…e 3 upgrades) # Conflicts: # package.json
|
Thanks Koki — your three calls landed exactly as you scoped them: Defaults (4s / 2 retries) — kept as-is. Decixa p95 + Vercel cold start headroom matches what we measure on our side too. 429 retry — added. Honors Jitter — folded the one-liner in: The All three plus your nit shipped in v2.1.2 on npm — same release that wired in Decixa Phase 3 (intent-driven Appreciate the deep review. Enjoy family time — happy Monday. |
Addresses @koki-socialgist's review feedback from the Discord thread ("timeout / retry around the Decixa call path"). Takes a first pass so review can focus on what's actually worth changing.
What changed
src/index.ts—resolvetool now wraps the Decixa call in anAbortController-based timeout with a retry loop.Behavior
DECIXA_RESOLVE_TIMEOUT_MS(default 4000ms)AbortError/ timeouts, 5xx responsesDECIXA_RESOLVE_RETRY_BASE_MS × 2^(attempt-1)discovery_attemptson success; fallback response addsdiscovery_errorwith the last failure reasonTunable via env
Verified locally
discovery_source=decixa,discovery_attempts=1✓DECIXA_RESOLVE_TIMEOUT_MS=1,MAX_RETRIES=1)discovery_source=local_fallback,discovery_attempts=2,discovery_error=AbortError: This operation was aborted✓Questions for you
Once this merges, I'll tag v2.1.1 and push to npm.