In case anyone else is wondering, I figured out how to do it. I'm not sure it's the most elegant solution, but it works. I figured all I need to do is isolate the last row of text and find the width of that. So if I can have a text object that contains just the last row of that text, I can use textwidth to find the width of the bottom row of text.
So essentially I have a clone object of the text object I want to find the end position of. So same size, font, etc. We'll call this LastRow. Because textwidth doesn't update until the Text object is redrawn, we can't just run a loop, we have to run it frame by frame. Since text wrapping is on a word-by-word basis, it makes sense to run it word-by-word. So we transfer the text from the displayed text object to LastRow, one word per tick, using tokenat() with " " as the separator. Then another event checks when textwidth deviates from the normal value, the height of one line of text (in my case it's 20). When it does, we need to cut off that entire first row of text; so we do this by using tokenat() again, this time setting the text to the last word of text (and a space). Once it's gone through the entire length of text, you'll have the bottom row isolated, and you can use textwidth to find the width of that, and textheight of the display text, and use that for the x and y of the caret sprite or whatever you intend to put there (with an offset according to where that display box is on the screen of course).