Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"features": {
"ghcr.io/devcontainers-extra/features/bun:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers-extra/features/bun@sha256:0624284ecaead9dd4c6654616a7f939cfa4ebcbc60593700a74e35b1767befa5",
"integrity": "sha256:0624284ecaead9dd4c6654616a7f939cfa4ebcbc60593700a74e35b1767befa5"
},
"ghcr.io/devcontainers/features/python:1": {
"version": "1.8.0",
"resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511",
"integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511"
}
}
}
23 changes: 23 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/debian
{
"name": "Debian",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/base:bookworm",
"features": {
"ghcr.io/devcontainers-extra/features/bun:1": {},
"ghcr.io/devcontainers/features/python:1": {}
}

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
46 changes: 19 additions & 27 deletions packages/opencode/src/session/retry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { NamedError } from "@opencode-ai/core/util/error"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Cause, Clock, Duration, Effect, Schedule } from "effect"
import { MessageV2 } from "./message-v2"
import { iife } from "@/util/iife"
import { isRecord } from "@/util/record"

Expand Down Expand Up @@ -42,36 +41,29 @@ function cap(ms: number) {
}

export function delay(attempt: number, error?: SessionV1.APIError) {
if (error) {
const headers = error.data.responseHeaders
if (headers) {
const retryAfterMs = headers["retry-after-ms"]
if (retryAfterMs) {
const parsedMs = Number.parseFloat(retryAfterMs)
if (!Number.isNaN(parsedMs)) {
return cap(parsedMs)
}
}
const headers = error?.data.responseHeaders
if (!headers) return backoff(attempt, RETRY_MAX_DELAY_NO_HEADERS)
return retryAfter(headers) ?? backoff(attempt, RETRY_MAX_DELAY)
}

const retryAfter = headers["retry-after"]
if (retryAfter) {
const parsedSeconds = Number.parseFloat(retryAfter)
if (!Number.isNaN(parsedSeconds)) {
// convert seconds to milliseconds
return cap(Math.ceil(parsedSeconds * 1000))
}
// Try parsing as HTTP date format
const parsed = Date.parse(retryAfter) - Date.now()
if (!Number.isNaN(parsed) && parsed > 0) {
return cap(Math.ceil(parsed))
}
}
function backoff(attempt: number, maximum: number) {
return cap(Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), maximum))
}

return cap(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1))
}
function retryAfter(headers: Record<string, string>) {
const milliseconds = headers["retry-after-ms"]
if (milliseconds) {
const parsed = Number.parseFloat(milliseconds)
if (!Number.isNaN(parsed)) return cap(parsed)
}

return cap(Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS))
const value = headers["retry-after"]
if (!value) return
const seconds = Number.parseFloat(value)
if (!Number.isNaN(seconds)) return cap(Math.ceil(seconds * 1000))

const date = Date.parse(value) - Date.now()
if (!Number.isNaN(date) && date > 0) return cap(Math.ceil(date))
}

export function retryable(error: Err, provider: string) {
Expand Down
Loading