Add daemon-attached goal recovery operator
Resume goals after transient API failures with persisted exponential backoff while respecting pauses and interruptions.
This commit is contained in:
parent
a4522e5ed6
commit
e4b84d9802
1 changed files with 1595 additions and 0 deletions
113
codex-keep/protocol.go
Normal file
113
codex-keep/protocol.go
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package main
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type rpcError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *rpcError) Error() string { return e.Message }
|
||||
|
||||
type message struct {
|
||||
ID json.RawMessage `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
Error *rpcError `json:"error"`
|
||||
}
|
||||
|
||||
type turnError struct {
|
||||
Info errorInfo `json:"codexErrorInfo"`
|
||||
}
|
||||
|
||||
type errorInfo struct {
|
||||
Kind string
|
||||
HTTP *int
|
||||
}
|
||||
|
||||
func (e errorInfo) MarshalJSON() ([]byte, error) {
|
||||
if e.HTTP == nil {
|
||||
return json.Marshal(e.Kind)
|
||||
}
|
||||
return json.Marshal(map[string]struct {
|
||||
HTTP *int `json:"httpStatusCode"`
|
||||
}{e.Kind: {e.HTTP}})
|
||||
}
|
||||
|
||||
func (e *errorInfo) UnmarshalJSON(data []byte) error {
|
||||
*e = errorInfo{}
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
if json.Unmarshal(data, &e.Kind) == nil {
|
||||
return nil
|
||||
}
|
||||
var variants map[string]struct {
|
||||
HTTP *int `json:"httpStatusCode"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &variants); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(variants) == 1 {
|
||||
for kind, value := range variants {
|
||||
e.Kind, e.HTTP = kind, value.HTTP
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e errorInfo) retryable() bool {
|
||||
switch e.Kind {
|
||||
case "serverOverloaded", "rateLimitExceeded", "internalServerError", "flexUnavailable":
|
||||
return true
|
||||
case "httpConnectionFailed", "responseStreamConnectionFailed", "responseStreamDisconnected", "responseTooManyFailedAttempts":
|
||||
return e.HTTP == nil || *e.HTTP == 408 || *e.HTTP == 429 || *e.HTTP >= 500 && *e.HTTP <= 599
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type turn struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Error *turnError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type params struct {
|
||||
ThreadID string `json:"threadId"`
|
||||
Thread struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"thread"`
|
||||
Turn turn `json:"turn"`
|
||||
Goal *goal `json:"goal"`
|
||||
}
|
||||
|
||||
type goalResult struct {
|
||||
Goal *goal `json:"goal"`
|
||||
}
|
||||
type turnsResult struct {
|
||||
Data []turn `json:"data"`
|
||||
}
|
||||
type loadedResult struct {
|
||||
Data []string `json:"data"`
|
||||
Next string `json:"nextCursor"`
|
||||
}
|
||||
|
||||
type threadParams struct {
|
||||
ThreadID string `json:"threadId"`
|
||||
}
|
||||
type setParams struct {
|
||||
ThreadID string `json:"threadId"`
|
||||
Status goalStatus `json:"status"`
|
||||
Origin string `json:"origin"`
|
||||
}
|
||||
|
||||
type initParams struct {
|
||||
Client struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
} `json:"clientInfo"`
|
||||
Capabilities struct {
|
||||
Experimental bool `json:"experimentalApi"`
|
||||
} `json:"capabilities"`
|
||||
}
|
||||
Loading…
Reference in a new issue