llm: make Tool.Run return ToolOut

This is preliminary work towards
allowing tools to add additional information.
No functional changes (at least, that's the intent).
diff --git a/claudetool/patch.go b/claudetool/patch.go
index 252455a..815cf88 100644
--- a/claudetool/patch.go
+++ b/claudetool/patch.go
@@ -20,9 +20,9 @@
 
 // PatchCallback defines the signature for patch tool callbacks.
 // It runs after the patch tool has executed.
-// It receives the patch input, the tool call results (both content and error),
-// and returns a new, possibly altered tool call result.
-type PatchCallback func(input PatchInput, result []llm.Content, err error) ([]llm.Content, error)
+// It receives the patch input and the tool output,
+// and returns a new, possibly altered tool output.
+type PatchCallback func(input PatchInput, output llm.ToolOut) llm.ToolOut
 
 // Patch creates a patch tool. The callback may be nil.
 func Patch(callback PatchCallback) *llm.Tool {
@@ -30,13 +30,13 @@
 		Name:        PatchName,
 		Description: strings.TrimSpace(PatchDescription),
 		InputSchema: llm.MustSchema(PatchInputSchema),
-		Run: func(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
+		Run: func(ctx context.Context, m json.RawMessage) llm.ToolOut {
 			var input PatchInput
-			result, err := patchRun(ctx, m, &input)
+			output := patchRun(ctx, m, &input)
 			if callback != nil {
-				return callback(input, result, err)
+				return callback(input, output)
 			}
-			return result, err
+			return output
 		},
 	}
 }
@@ -112,17 +112,17 @@
 
 // patchRun implements the guts of the patch tool.
 // It populates input from m.
-func patchRun(ctx context.Context, m json.RawMessage, input *PatchInput) ([]llm.Content, error) {
+func patchRun(ctx context.Context, m json.RawMessage, input *PatchInput) llm.ToolOut {
 	if err := json.Unmarshal(m, &input); err != nil {
-		return nil, fmt.Errorf("failed to unmarshal user_patch input: %w", err)
+		return llm.ErrorfToolOut("failed to unmarshal user_patch input: %w", err)
 	}
 
 	// Validate the input
 	if !filepath.IsAbs(input.Path) {
-		return nil, fmt.Errorf("path %q is not absolute", input.Path)
+		return llm.ErrorfToolOut("path %q is not absolute", input.Path)
 	}
 	if len(input.Patches) == 0 {
-		return nil, fmt.Errorf("no patches provided")
+		return llm.ErrorToolOut(fmt.Errorf("no patches provided"))
 	}
 	// TODO: check whether the file is autogenerated, and if so, require a "force" flag to modify it.
 
@@ -135,11 +135,11 @@
 			switch patch.Operation {
 			case "prepend_bof", "append_eof", "overwrite":
 			default:
-				return nil, fmt.Errorf("file %q does not exist", input.Path)
+				return llm.ErrorfToolOut("file %q does not exist", input.Path)
 			}
 		}
 	case err != nil:
-		return nil, fmt.Errorf("failed to read file %q: %w", input.Path, err)
+		return llm.ErrorfToolOut("failed to read file %q: %w", input.Path, err)
 	}
 
 	likelyGoFile := strings.HasSuffix(input.Path, ".go")
@@ -168,7 +168,7 @@
 			buf.Replace(0, len(orig), patch.NewText)
 		case "replace":
 			if patch.OldText == "" {
-				return nil, fmt.Errorf("patch %d: oldText cannot be empty for %s operation", i, patch.Operation)
+				return llm.ErrorfToolOut("patch %d: oldText cannot be empty for %s operation", i, patch.Operation)
 			}
 
 			// Attempt to apply the patch.
@@ -231,23 +231,23 @@
 			patchErr = errors.Join(patchErr, fmt.Errorf("old text not found:\n%s", patch.OldText))
 			continue
 		default:
-			return nil, fmt.Errorf("unrecognized operation %q", patch.Operation)
+			return llm.ErrorfToolOut("unrecognized operation %q", patch.Operation)
 		}
 	}
 
 	if patchErr != nil {
-		return nil, patchErr
+		return llm.ErrorToolOut(patchErr)
 	}
 
 	patched, err := buf.Bytes()
 	if err != nil {
-		return nil, err
+		return llm.ErrorToolOut(err)
 	}
 	if err := os.MkdirAll(filepath.Dir(input.Path), 0o700); err != nil {
-		return nil, fmt.Errorf("failed to create directory %q: %w", filepath.Dir(input.Path), err)
+		return llm.ErrorfToolOut("failed to create directory %q: %w", filepath.Dir(input.Path), err)
 	}
 	if err := os.WriteFile(input.Path, patched, 0o600); err != nil {
-		return nil, fmt.Errorf("failed to write patched contents to file %q: %w", input.Path, err)
+		return llm.ErrorfToolOut("failed to write patched contents to file %q: %w", input.Path, err)
 	}
 
 	response := new(strings.Builder)
@@ -256,7 +256,7 @@
 	if parsed {
 		parseErr := parseGo(patched)
 		if parseErr != nil {
-			return nil, fmt.Errorf("after applying all patches, the file no longer parses:\n%w", parseErr)
+			return llm.ErrorfToolOut("after applying all patches, the file no longer parses:\n%w", parseErr)
 		}
 	}
 
@@ -265,7 +265,7 @@
 	}
 
 	// TODO: maybe report the patch result to the model, i.e. some/all of the new code after the patches and formatting.
-	return llm.TextContent(response.String()), nil
+	return llm.ToolOut{LLMContent: llm.TextContent(response.String())}
 }
 
 func parseGo(buf []byte) error {