package executor import ( "bufio" "encoding/json" "io" "log/slog" ) // extractAskUserQuestion parses a stream-json line and returns the tool_use_id and input // if the line is an assistant event containing an AskUserQuestion tool_use. func extractAskUserQuestion(line []byte) (string, json.RawMessage) { var event struct { Type string `json:"type"` Message struct { Content []struct { Type string `json:"type"` ID string `json:"id"` Name string `json:"name"` Input json.RawMessage `json:"input"` } `json:"content"` } `json:"message"` } if err := json.Unmarshal(line, &event); err != nil { return "", nil } if event.Type != "assistant" { return "", nil } for _, block := range event.Message.Content { if block.Type == "tool_use" && block.Name == "AskUserQuestion" { return block.ID, block.Input } } return "", nil } // streamAndParseWithQuestions reads streaming JSON, writes to w, parses cost, // and calls onQuestion for each detected AskUserQuestion tool_use. func streamAndParseWithQuestions(r io.Reader, w io.Writer, _ *slog.Logger, onQuestion func(string, json.RawMessage)) float64 { tee := io.TeeReader(r, w) scanner := bufio.NewScanner(tee) scanner.Buffer(make([]byte, 1024*1024), 1024*1024) var totalCost float64 for scanner.Scan() { line := scanner.Bytes() if toolUseID, input := extractAskUserQuestion(line); toolUseID != "" { if onQuestion != nil { onQuestion(toolUseID, input) } } var msg map[string]interface{} if err := json.Unmarshal(line, &msg); err != nil { continue } if costData, ok := msg["cost_usd"]; ok { if cost, ok := costData.(float64); ok { totalCost = cost } } } return totalCost } // buildToolResultMessage builds a tool_result message to feed back to Claude // as the answer to an AskUserQuestion tool_use. func buildToolResultMessage(toolUseID, answer string) []byte { answerJSON, _ := json.Marshal(map[string]interface{}{ "answers": map[string]string{"answer": answer}, }) msg := map[string]interface{}{ "message": map[string]interface{}{ "role": "user", "content": []map[string]interface{}{ { "type": "tool_result", "tool_use_id": toolUseID, "content": string(answerJSON), }, }, }, } result, _ := json.Marshal(msg) return result }