agent plumbing: convert outbox to subscribers and an iterator

WaitForMessage() could only work for one thread, because it was using a
singular channel for outboxes. This was fine when we only had one user,
but WaitForMessageCount() was kinda similar, and had its own thing, and
I want to rework how polling works and need another user.

Anyway, this one is hand-coded, because Sketch really struggled
with getting the iterator convincingly safe. In a follow-up commit,
I'll try to get Sketch to write some tests.
diff --git a/termui/termui.go b/termui/termui.go
index 932f07f..5a3eca3 100644
--- a/termui/termui.go
+++ b/termui/termui.go
@@ -117,7 +117,7 @@
 	return nil
 }
 
-func (ui *termUI) LogToolUse(resp loop.AgentMessage) {
+func (ui *termUI) LogToolUse(resp *loop.AgentMessage) {
 	inputData := map[string]any{}
 	if err := json.Unmarshal([]byte(resp.ToolInput), &inputData); err != nil {
 		ui.AppendSystemMessage("error: %v", err)
@@ -132,6 +132,7 @@
 }
 
 func (ui *termUI) receiveMessagesLoop(ctx context.Context) {
+	it := ui.agent.NewIterator(ctx, 0)
 	bold := color.New(color.Bold).SprintFunc()
 	for {
 		select {
@@ -139,7 +140,10 @@
 			return
 		default:
 		}
-		resp := ui.agent.WaitForMessage(ctx)
+		resp := it.Next()
+		if resp == nil {
+			return
+		}
 		// Typically a user message will start the thinking and a (top-level
 		// conversation) end of turn will stop it.
 		thinking := !(resp.EndOfTurn && resp.ParentConversationID == nil)