Move questions and reviews into the transcript; agents answer findings
This commit is contained in:
@@ -364,9 +364,11 @@ func findingsMessage(r api.Review) string {
|
||||
res = *r.Result
|
||||
}
|
||||
b := &strings.Builder{}
|
||||
fmt.Fprintf(b, "A reviewer looked at snapshot #%d of your work and raised the findings below. Reviewers can be wrong or miss context: check each one against the code and your brief, act on the ones that hold up, and in your final message say briefly which you acted on and which you're leaving and why.", r.SnapshotN)
|
||||
fmt.Fprintf(b, "A reviewer looked at snapshot #%d of your work and raised the findings below. Reviewers can be wrong or miss context: check each one against the code and your brief. "+
|
||||
"Before you change anything, answer each finding with respond_to_finding (snapshot %d and the finding's number): fixing if it holds up and you'll act on it, ignoring with your reason if you're leaving it. "+
|
||||
"The human sees your answers on the findings and can overrule an ignore.", r.SnapshotN, r.SnapshotN)
|
||||
findings, _ := res["findings"].([]any)
|
||||
for _, f := range findings {
|
||||
for i, f := range findings {
|
||||
fm, _ := f.(map[string]any)
|
||||
where := ""
|
||||
if p, ok := fm["path"].(string); ok && p != "" {
|
||||
@@ -376,11 +378,74 @@ func findingsMessage(r api.Review) string {
|
||||
}
|
||||
where = " (`" + where + "`)"
|
||||
}
|
||||
fmt.Fprintf(b, "\n\n**[%v] %v**%s\n\n%s", fm["severity"], fm["title"], where, strings.TrimSpace(fmt.Sprint(fm["detail"])))
|
||||
fmt.Fprintf(b, "\n\n**Finding %d: [%v] %v**%s\n\n%s", i+1, fm["severity"], fm["title"], where, strings.TrimSpace(fmt.Sprint(fm["detail"])))
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type findingResponseArgs struct {
|
||||
Snapshot int `json:"snapshot"`
|
||||
Finding int `json:"finding"`
|
||||
Response api.FindingResponseResponse `json:"response"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
func (m *Manager) respondToFindingTool(l *live) mcp.Tool {
|
||||
return mcp.Tool{
|
||||
Name: "respond_to_finding",
|
||||
Description: "Answer one finding of a review of your work, as soon as you've decided what to do about it: fixing when you'll act on it, ignoring when you're leaving it. " +
|
||||
"The human sees your answer on the finding; an ignore keeps the human's fix button, so they can overrule you. Call it again to change your answer.",
|
||||
InputSchema: json.RawMessage(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"snapshot": {"type": "integer", "description": "The reviewed snapshot's number."},
|
||||
"finding": {"type": "integer", "description": "The finding's number in the review, from 1."},
|
||||
"response": {"type": "string", "enum": ["fixing", "ignoring"]},
|
||||
"reason": {"type": "string", "description": "One line: what you'll do about it, or why you're leaving it."}
|
||||
},
|
||||
"required": ["snapshot", "finding", "response", "reason"]
|
||||
}`),
|
||||
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
|
||||
args := findingResponseArgs{}
|
||||
err := json.Unmarshal(raw, &args)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("malformed respond_to_finding call: %w", err)
|
||||
}
|
||||
if !args.Response.Valid() {
|
||||
return "", fmt.Errorf("%w: response must be fixing or ignoring", ErrInvalid)
|
||||
}
|
||||
if args.Finding < 1 {
|
||||
return "", fmt.Errorf("%w: finding numbers start at 1", ErrInvalid)
|
||||
}
|
||||
reason := strings.TrimSpace(args.Reason)
|
||||
if reason == "" {
|
||||
return "", fmt.Errorf("%w: reason must not be empty", ErrInvalid)
|
||||
}
|
||||
reviews, err := m.store.ListReviews(ctx, l.taskID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
reviewID := ""
|
||||
for _, r := range reviews {
|
||||
if r.SnapshotN == args.Snapshot && r.State == api.ReviewStateDone {
|
||||
reviewID = r.Id
|
||||
}
|
||||
}
|
||||
if reviewID == "" {
|
||||
return "", fmt.Errorf("%w: snapshot %d has no finished review", ErrInvalid, args.Snapshot)
|
||||
}
|
||||
err = m.store.SetFindingResponse(ctx, reviewID, args.Finding-1, api.FindingResponse{Response: args.Response, Reason: reason}, agentActor)
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return "", fmt.Errorf("%w: the review of snapshot %d has no finding %d", ErrInvalid, args.Snapshot, args.Finding)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("Recorded: %s finding %d of snapshot %d's review.", args.Response, args.Finding, args.Snapshot), nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type reviewImage struct {
|
||||
entry map[string]any
|
||||
contentType string
|
||||
|
||||
Reference in New Issue
Block a user