Skip to content

Commit 6c624d9

Browse files
committed
fix(sdk,cli): server-to-agent chat preload — trigger: "preload" + messages: [] in basePayload
Server-to-agent flows (`AgentChat` SDK class + cli-v3 MCP `start_agent_chat`) were building `triggerConfig.basePayload` without the `trigger: "preload"` and `messages: []` fields the agent runtime branches on. Result: the auto-triggered first run had `payload.trigger === undefined`, neither `onPreload` nor `onChatStart` fired, and `onTurnStart`'s DB-write blew up with PrismaClient "No record found" because no Chat row had been created. Browser-mediated flows already had this right (`chat.createStartSessionAction` in `ai.ts:6951`); the server-side path now mirrors that shape. - packages/trigger-sdk/src/v3/chat-client.ts — `AgentChat.ensureStarted` adds the two fields to `basePayload`. `chat-client-test`'s `pong` orchestrator now returns the assistant text instead of an empty string. - packages/cli-v3/src/mcp/tools/agentChat.ts — same fix on `start_agent_chat`'s `createSession` call. Also drops the redundant separate `apiClient.triggerTask(...)` call: `POST /api/v1/sessions` now auto-triggers the first run and returns its runId, so a second trigger from the MCP would have produced a competing run on the same session. Use `session.runId` from the create response. The `preload` input flag becomes a no-op signal (response message wording only) since session-create always triggers a run now. Verified end-to-end against local: - `chat-client-test` orchestrator returns `{ text: "pong" }` - MCP `start_agent_chat` → `send_agent_message` x2 → `close_agent_chat` succeeds, both turns reuse the same runId
1 parent 9aaf595 commit 6c624d9

2 files changed

Lines changed: 28 additions & 52 deletions

File tree

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

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -118,67 +118,35 @@ export const startAgentChatTool = {
118118
// Sessions are now task-bound: taskIdentifier + triggerConfig are
119119
// required, and the server reuses them for every run scheduled by
120120
// this session (initial + continuations after run termination).
121+
//
122+
// basePayload mirrors the browser-mediated `chat.createStartSessionAction`
123+
// shape so the auto-triggered first run hits `onPreload` (not
124+
// `onChatStart` with `preloaded: true`). Without `trigger: "preload"`
125+
// + `messages: []`, the agent runtime bypasses both lifecycle hooks
126+
// and `onTurnStart`'s DB write fails with "No record found".
127+
//
128+
// POST /api/v1/sessions auto-triggers the first run and returns its
129+
// runId, so we don't need a separate triggerTask call. The `preload`
130+
// flag on this MCP tool is kept as a no-op signal (true=default) for
131+
// backwards compat — a Session is always created with a live run now.
121132
const session = await apiClient.createSession({
122133
type: "chat.agent",
123134
externalId: chatId,
124135
taskIdentifier: input.agentId,
125136
triggerConfig: {
126-
basePayload: { chatId, ...(input.clientData ?? {}) },
137+
basePayload: {
138+
messages: [],
139+
trigger: "preload",
140+
chatId,
141+
...(input.clientData ? { metadata: input.clientData } : {}),
142+
},
127143
tags: [`chat:${chatId}`],
128144
},
129145
});
130146

131-
if (input.preload) {
132-
// Trigger a preload run. The agent opens the session via
133-
// `sessions.open(payload.sessionId)` on startup.
134-
const payload = {
135-
messages: [],
136-
chatId,
137-
sessionId: session.id,
138-
trigger: "preload",
139-
metadata: input.clientData,
140-
};
141-
142-
const result = await apiClient.triggerTask(input.agentId, {
143-
payload,
144-
options: {
145-
payloadType: "application/json",
146-
tags: [`chat:${chatId}`, "preload:true"],
147-
},
148-
});
149-
150-
activeSessions.set(chatId, {
151-
sessionId: session.id,
152-
runId: result.id,
153-
chatId,
154-
agentId: input.agentId,
155-
apiClient,
156-
clientData: input.clientData,
157-
messages: [],
158-
});
159-
160-
return {
161-
content: [
162-
{
163-
type: "text",
164-
text: [
165-
`Agent chat started and preloaded.`,
166-
`- Chat ID: ${chatId}`,
167-
`- Session ID: ${session.id}`,
168-
`- Agent: ${input.agentId}`,
169-
`- Run ID: ${result.id}`,
170-
``,
171-
`Use send_agent_message with chatId "${chatId}" to send messages.`,
172-
].join("\n"),
173-
},
174-
],
175-
};
176-
}
177-
178-
// No preload — register the session, first sendMessage will trigger.
179147
activeSessions.set(chatId, {
180148
sessionId: session.id,
181-
runId: "",
149+
runId: session.runId,
182150
chatId,
183151
agentId: input.agentId,
184152
apiClient,
@@ -191,12 +159,13 @@ export const startAgentChatTool = {
191159
{
192160
type: "text",
193161
text: [
194-
`Agent chat created (not yet preloaded).`,
162+
`Agent chat started${input.preload ? " and preloaded" : ""}.`,
195163
`- Chat ID: ${chatId}`,
196164
`- Session ID: ${session.id}`,
197165
`- Agent: ${input.agentId}`,
166+
`- Run ID: ${session.runId}`,
198167
``,
199-
`Use send_agent_message with chatId "${chatId}" to send the first message (this will trigger the run).`,
168+
`Use send_agent_message with chatId "${chatId}" to send messages.`,
200169
].join("\n"),
201170
},
202171
],

packages/trigger-sdk/src/v3/chat-client.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,13 @@ export class AgentChat<TAgent = unknown> {
513513

514514
const triggerConfig: SessionTriggerConfig = {
515515
basePayload: {
516+
// `trigger: "preload"` + empty `messages` mirror the browser-mediated
517+
// `chat.createStartSessionAction` shape so the agent runtime fires
518+
// `onPreload` (not `onChatStart` with `preloaded: true`). Without
519+
// this, AgentChat's first run skips both preload and start hooks,
520+
// which is where customer apps typically upsert their Chat row.
521+
messages: [],
522+
trigger: "preload",
516523
...(this.triggerConfigDefault?.basePayload ?? {}),
517524
chatId: this.chatId,
518525
...(this.clientData ? { metadata: this.clientData } : {}),

0 commit comments

Comments
 (0)