Overview
The prompt method is the core of your agent’s functionality. It handles the entire lifecycle of processing a user’s request, from receiving the initial messages to executing tool calls and returning a final response.
async prompt(
params: PromptRequest
): Promise<PromptResponse>
The session ID to process the prompt in
The user messages with optional context (files, images, etc.)
The reason the prompt turn ended: “end_turn”, “cancelled”, or “error”
See protocol docs: Prompt Turn
Prompt Lifecycle
The prompt method handles the complete lifecycle of a turn:
Receive Messages
The client sends user messages with optional context (files, images, etc.)
Process with Language Model
Send the messages to your language model and receive a response
Stream Updates to Client
Send real-time updates as the model generates content using sessionUpdate
Handle Tool Calls
If the model wants to use tools, request permission (if needed) and execute them
Return Stop Reason
Return the final stop reason indicating why the turn ended
Sending Session Updates
Use the sessionUpdate method on your AgentSideConnection to send real-time updates during prompt processing:
Message Chunks
Send text content as it’s generated by the language model:
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "agent_message_chunk",
content: {
type: "text",
text: "I'll help you with that task.",
},
},
});
Report when the model wants to execute a tool:
// Initial tool call
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "tool_call",
toolCallId: "call_1",
title: "Reading project files",
kind: "read",
status: "pending",
locations: [{ path: "/project/README.md" }],
rawInput: { path: "/project/README.md" },
},
});
// Update with results
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "tool_call_update",
toolCallId: "call_1",
status: "completed",
content: [
{
type: "content",
content: {
type: "text",
text: "# My Project\n\nThis is a sample project...",
},
},
],
rawOutput: { content: "# My Project\n\nThis is a sample project..." },
},
});
When the language model wants to execute a sensitive operation, request permission from the user:
// Send the tool call
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "tool_call",
toolCallId: "call_2",
title: "Modifying critical configuration file",
kind: "edit",
status: "pending",
locations: [{ path: "/project/config.json" }],
rawInput: {
path: "/project/config.json",
content: '{"database": {"host": "new-host"}}',
},
},
});
// Request permission
const permissionResponse = await this.connection.requestPermission({
sessionId: params.sessionId,
toolCall: {
toolCallId: "call_2",
title: "Modifying critical configuration file",
kind: "edit",
status: "pending",
locations: [{ path: "/home/user/project/config.json" }],
rawInput: {
path: "/home/user/project/config.json",
content: '{"database": {"host": "new-host"}}',
},
},
options: [
{
kind: "allow_once",
name: "Allow this change",
optionId: "allow",
},
{
kind: "reject_once",
name: "Skip this change",
optionId: "reject",
},
],
});
// Handle the response
if (permissionResponse.outcome.outcome === "cancelled") {
// User cancelled the entire prompt turn
return { stopReason: "cancelled" };
}
switch (permissionResponse.outcome.optionId) {
case "allow":
// Execute the tool call
await this.executeToolCall("call_2");
break;
case "reject":
// Skip the tool call
break;
}
If the client cancels the prompt turn via session/cancel, the requestPermission call MUST respond with RequestPermissionOutcome::Cancelled.
Stop Reasons
Return one of the following stop reasons when the prompt completes:
The language model completed its turn naturally
The prompt was cancelled by the client via session/cancel
An error occurred during processing
Handling Cancellation
Implement proper cancellation handling using AbortController:
interface AgentSession {
pendingPrompt: AbortController | null;
}
async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
const session = this.sessions.get(params.sessionId);
if (!session) {
throw new Error(`Session ${params.sessionId} not found`);
}
// Cancel any existing prompt
session.pendingPrompt?.abort();
session.pendingPrompt = new AbortController();
try {
// Pass the abort signal to your LLM client
await this.processWithLLM(params, session.pendingPrompt.signal);
return { stopReason: "end_turn" };
} catch (err) {
if (session.pendingPrompt.signal.aborted) {
return { stopReason: "cancelled" };
}
throw err;
} finally {
session.pendingPrompt = null;
}
}
async cancel(params: acp.CancelNotification): Promise<void> {
this.sessions.get(params.sessionId)?.pendingPrompt?.abort();
}
Continue accepting tool call updates even after receiving a session/cancel notification, as you may need to send final updates before responding with the cancelled stop reason.
Complete Example
Here’s a complete example from the SDK’s example agent:
async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
const session = this.sessions.get(params.sessionId);
if (!session) {
throw new Error(`Session ${params.sessionId} not found`);
}
session.pendingPrompt?.abort();
session.pendingPrompt = new AbortController();
try {
// Send initial text chunk
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "agent_message_chunk",
content: {
type: "text",
text: "I'll help you with that. Let me start by reading some files.",
},
},
});
// Send a tool call
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "tool_call",
toolCallId: "call_1",
title: "Reading project files",
kind: "read",
status: "pending",
locations: [{ path: "/project/README.md" }],
rawInput: { path: "/project/README.md" },
},
});
// Execute and update
await this.connection.sessionUpdate({
sessionId: params.sessionId,
update: {
sessionUpdate: "tool_call_update",
toolCallId: "call_1",
status: "completed",
content: [
{
type: "content",
content: {
type: "text",
text: "# My Project\n\nThis is a sample project...",
},
},
],
rawOutput: { content: "# My Project\n\nThis is a sample project..." },
},
});
return { stopReason: "end_turn" };
} catch (err) {
if (session.pendingPrompt.signal.aborted) {
return { stopReason: "cancelled" };
}
throw err;
} finally {
session.pendingPrompt = null;
}
}
See the full example agent for more details.