83bae00e45
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
1.2 KiB
Go
48 lines
1.2 KiB
Go
package ai
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Verdict is the drift judge's call on a single window.
|
|
type Verdict struct {
|
|
OnTask bool
|
|
Reason string
|
|
}
|
|
|
|
// ErrInvalidVerdict marks a parseable-but-unusable judge response.
|
|
var ErrInvalidVerdict = errors.New("ai: invalid verdict")
|
|
|
|
type rawVerdict struct {
|
|
OnTask bool `json:"on_task"`
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
// parseVerdict extracts and validates a Verdict from raw CLI output. It reuses
|
|
// extractJSON and the shared empty/no-JSON sentinels. A drifting verdict with no
|
|
// reason is unusable for the UI and is rejected as ErrInvalidVerdict.
|
|
func parseVerdict(s string) (Verdict, error) {
|
|
if strings.TrimSpace(s) == "" {
|
|
return Verdict{}, ErrEmptyResponse
|
|
}
|
|
if strings.IndexByte(s, '{') < 0 {
|
|
return Verdict{}, ErrNoJSON
|
|
}
|
|
jsonStr, err := extractJSON(s)
|
|
if err != nil {
|
|
return Verdict{}, fmt.Errorf("%w: %v", ErrInvalidVerdict, err)
|
|
}
|
|
var raw rawVerdict
|
|
if err := json.Unmarshal([]byte(jsonStr), &raw); err != nil {
|
|
return Verdict{}, fmt.Errorf("%w: %v", ErrInvalidVerdict, err)
|
|
}
|
|
v := Verdict{OnTask: raw.OnTask, Reason: strings.TrimSpace(raw.Reason)}
|
|
if !v.OnTask && v.Reason == "" {
|
|
return Verdict{}, ErrInvalidVerdict
|
|
}
|
|
return v, nil
|
|
}
|