Documentation
¶
Overview ¶
Package claudecode wraps the claude CLI binary as a provider.
Index ¶
Constants ¶
const ( DefaultBackgroundGrace = 2 * time.Second DefaultBackgroundWait = 30 * time.Minute DefaultTerminateGrace = 5 * time.Second )
Background-policy defaults.
The wait is generous on purpose. Its job is to cover the case this feature exists for — a build, a test suite or a training run that the agent started and should have waited for — and those finish in minutes, not seconds. The cost of waiting too long is a slow task; the cost of waiting too briefly is a task marked incomplete and retried when it would have succeeded, which is both more expensive and more confusing.
const ProviderName = "claudecode"
Variables ¶
var ErrAuthFailure = errors.New("claude CLI authentication failure")
ErrAuthFailure marks a CLI invocation the API rejected for credential reasons (HTTP 401 / authentication_error). Callers use errors.Is to tell it apart from the other fatal CLI failures — 403, 429, 5xx, HTML error pages — which a credential refresh cannot fix and which must therefore not be retried here.
Functions ¶
func CredentialRefresherWired ¶
func CredentialRefresherWired() bool
CredentialRefresherWired reports whether a refresher has been installed. Exists so a test can assert the cmd/providers.go wiring is still present: losing it degrades the retry silently — it would still fire, but could only recover races a peer process happened to win, not a credential this process must rotate itself.
func SetCredentialRefresher ¶
func SetCredentialRefresher(fn func() string)
SetCredentialRefresher installs the OAuth refresh used before an authentication retry. Called once from cmd/providers.go; also used by tests to substitute a stub for the real credentials and network.
Types ¶
type BackgroundPolicy ¶
type BackgroundPolicy struct {
// Disabled turns detection off, restoring the previous behaviour of
// trusting a harness that reports success while its work is still
// running. An escape hatch for a deployment where the tradeoff is wrong,
// not a default.
Disabled bool
// Grace is how long a surviving process may live before it counts as
// background work rather than ordinary teardown. Zero selects
// DefaultBackgroundGrace.
Grace time.Duration
// Wait bounds how long cloop blocks for that work to finish. Zero selects
// DefaultBackgroundWait; negative means report it without waiting.
Wait time.Duration
// TerminateGrace is how long a terminated group has to exit on SIGTERM
// before SIGKILL. Zero selects DefaultTerminateGrace.
TerminateGrace time.Duration
// KeepOrphans leaves work that outlived the budget running instead of
// terminating it. The task is still reported incomplete either way.
KeepOrphans bool
}
BackgroundPolicy governs what happens to work the harness leaves running in its process group after it exits. The zero value is the recommended behaviour: detect it, wait for it, and terminate whatever outlives the wait.
type Provider ¶
type Provider struct {
// Background governs the handling of work the CLI leaves running after it
// exits. The zero value detects, waits and terminates; see
// BackgroundPolicy.
Background BackgroundPolicy
}
func NewWithBackground ¶
func NewWithBackground(policy BackgroundPolicy) *Provider
NewWithBackground builds a provider with an explicit policy for work the CLI leaves running. Zero-valued fields resolve to the documented defaults, so a caller that configures nothing still gets detection, the wait, and cleanup.
func (*Provider) Complete ¶
func (p *Provider) Complete(ctx context.Context, prompt string, opts provider.Options) (*provider.Result, error)
Complete runs the CLI once and, if the API rejected our credentials, refreshes the OAuth token and retries exactly once.
Why a retry belongs here (Task 20204): a 401 from this provider is usually not a dead credential but a *lost race* for one. The CLI subprocesses refresh OAuth independently of each other and of cloop, and Claude.ai refresh tokens are single-use — so when a parallel round of tasks straddles the token's expiry, several CLI processes exchange the same refresh token at once. One wins; the losers get invalid_grant and surface "API Error: 401". The task after them succeeds because the winner has meanwhile written a fresh credential to disk, which is precisely the reported symptom: an isolated 401 with a healthy task on either side. A transient 401 from the edge (observed: a Cloudflare 502 body delivered under a 401 status) has the same shape and the same remedy.
Retrying is therefore scoped strictly to authentication failures. 429 and 5xx are deliberately excluded: an immediate retry cannot help a rate limit and would feed the loop that keeps it alive, and the orchestrator's MaxFailures gate already handles sustained upstream outages.