Skip to content

Commit 75369f3

Browse files
committed
fix(cli): MCP agentChat slim-wire payload
The MCP `send_agent_message` was building wire payloads with a legacy `messages: [...]` array, but the agent's run loop now destructures `payload.message` (singular) per the slim-wire contract. Result: turn 1 entered `ai.streamText` with an empty messages array and threw `AI_InvalidPromptError: messages must not be empty`. Migrates all three send paths (session.in append, triggerTask fallback, and `trigger:upgrade-required` continuation) plus the `trigger: "close"` chunk to the slim-wire shape, and aligns the local `ChatInputChunk` type with the canonical one in ai-shared.ts.
1 parent a90949b commit 75369f3

1 file changed

Lines changed: 26 additions & 9 deletions

File tree

packages/cli-v3/src/mcp/tools/agentChat.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,23 @@ type ChatSession = {
2929
const activeSessions = new Map<string, ChatSession>();
3030

3131
// ─── ChatInputChunk serialization (mirrors TriggerChatTransport) ──
32-
32+
//
33+
// Slim-wire: one delta `message` per chunk, not a full `messages[]` array.
34+
// The agent's run loop destructures `payload.message` (singular). Sending
35+
// a `messages: [...]` array makes `message` undefined and turn 1 calls
36+
// `streamText({ messages: [] })`, which throws
37+
// `AI_InvalidPromptError: messages must not be empty`.
3338
type ChatInputChunk =
3439
| {
3540
kind: "message";
3641
payload: {
37-
messages: ChatMessage[];
42+
message?: ChatMessage;
3843
chatId: string;
3944
trigger: "submit-message" | "close" | "preload" | "regenerate-message" | "action";
4045
metadata?: unknown;
46+
sessionId?: string;
47+
continuation?: boolean;
48+
previousRunId?: string;
4149
};
4250
}
4351
| { kind: "stop"; message?: string };
@@ -203,16 +211,19 @@ export const sendAgentMessageTool = {
203211
// Track the outgoing user message
204212
session.messages.push(userMessage);
205213

214+
// Slim-wire: one delta `message` per trigger. Prior turns live in the
215+
// session.out snapshot+replay; we only ship the new user message.
206216
const wirePayload = {
207-
messages: [userMessage],
217+
message: userMessage,
208218
chatId: session.chatId,
209219
trigger: "submit-message" as const,
210220
metadata: session.clientData,
211221
};
212222

213223
// If we have an active run, send via session.in. If that fails
214224
// (run ended, token expired, etc.) fall back to triggering a new
215-
// run on the same session with the full history.
225+
// run on the same session — the new run replays prior turns from the
226+
// snapshot and picks up `message` as turn N's user delta.
216227
if (session.runId) {
217228
try {
218229
await session.apiClient.appendToSessionStream(
@@ -223,7 +234,7 @@ export const sendAgentMessageTool = {
223234
} catch (sendErr: any) {
224235
const result = await session.apiClient.triggerTask(session.agentId, {
225236
payload: {
226-
messages: session.messages,
237+
message: userMessage,
227238
chatId: session.chatId,
228239
sessionId: session.sessionId,
229240
trigger: "submit-message",
@@ -298,7 +309,8 @@ export const closeAgentChatTool = {
298309
serializeInputChunk({
299310
kind: "message",
300311
payload: {
301-
messages: [],
312+
// `trigger: "close"` carries no message delta — the agent
313+
// looks at `trigger` and exits without touching `message`.
302314
chatId: session.chatId,
303315
trigger: "close",
304316
},
@@ -368,12 +380,17 @@ async function collectAgentResponse(
368380
}
369381

370382
if (chunk.type === "trigger:upgrade-required") {
371-
// Agent requested upgrade — trigger continuation with full history.
372-
// Same session, new run — reuse sessionId, swap runId.
383+
// Agent requested upgrade — trigger continuation. Same session,
384+
// new run — reuse sessionId, swap runId. Slim-wire: ship only
385+
// the latest user message as the turn-N delta; prior turns
386+
// come back via snapshot+replay on the new run's boot.
387+
const lastUserMessage = [...session.messages]
388+
.reverse()
389+
.find((m) => m.role === "user");
373390
const previousRunId = session.runId;
374391
const result = await session.apiClient.triggerTask(session.agentId, {
375392
payload: {
376-
messages: session.messages,
393+
message: lastUserMessage,
377394
chatId: session.chatId,
378395
sessionId: session.sessionId,
379396
trigger: "submit-message",

0 commit comments

Comments
 (0)