Send sends a message to the agent synchronously. It blocks until the agent has finished processing and returns any error from the underlying write. Returns a validation error immediately if the message is invalid or another message is already being processed.
(messageParts ...st.MessagePart)
| 89 | // from the underlying write. Returns a validation error immediately if |
| 90 | // the message is invalid or another message is already being processed. |
| 91 | func (c *ACPConversation) Send(messageParts ...st.MessagePart) error { |
| 92 | message := "" |
| 93 | for _, part := range messageParts { |
| 94 | message += part.String() |
| 95 | } |
| 96 | |
| 97 | // Validate whitespace BEFORE trimming (match PTY behavior) |
| 98 | if message != strings.TrimSpace(message) { |
| 99 | return st.ErrMessageValidationWhitespace |
| 100 | } |
| 101 | |
| 102 | if message == "" { |
| 103 | return st.ErrMessageValidationEmpty |
| 104 | } |
| 105 | |
| 106 | // Check if already prompting and set state atomically |
| 107 | c.mu.Lock() |
| 108 | if c.prompting { |
| 109 | c.mu.Unlock() |
| 110 | return st.ErrMessageValidationChanging |
| 111 | } |
| 112 | c.messages = append(c.messages, st.ConversationMessage{ |
| 113 | Id: c.nextID, |
| 114 | Role: st.ConversationRoleUser, |
| 115 | Message: message, |
| 116 | Time: c.clock.Now(), |
| 117 | }) |
| 118 | c.nextID++ |
| 119 | // Add placeholder for streaming agent response |
| 120 | c.messages = append(c.messages, st.ConversationMessage{ |
| 121 | Id: c.nextID, |
| 122 | Role: st.ConversationRoleAgent, |
| 123 | Message: "", |
| 124 | Time: c.clock.Now(), |
| 125 | }) |
| 126 | c.nextID++ |
| 127 | c.streamingResponse.Reset() |
| 128 | c.prompting = true |
| 129 | status := c.statusLocked() |
| 130 | c.mu.Unlock() |
| 131 | |
| 132 | // Emit status change to "running" before starting the prompt |
| 133 | c.emitter.EmitStatus(status) |
| 134 | |
| 135 | c.logger.Debug("ACPConversation sending message", "message", message) |
| 136 | |
| 137 | return c.executePrompt(messageParts) |
| 138 | } |
| 139 | |
| 140 | // Start sets up chunk handling and sends the initial prompt if provided. |
| 141 | func (c *ACPConversation) Start(ctx context.Context) { |