mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Remove cruft
This commit is contained in:
parent
e6b1d20bd6
commit
22fa95f692
19 changed files with 0 additions and 6561 deletions
|
|
@ -1,287 +0,0 @@
|
|||
import type * as acp from "@agentclientprotocol/sdk"
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest"
|
||||
|
||||
import { TerminalManager } from "../terminal-manager.js"
|
||||
|
||||
// Mock the ACP SDK
|
||||
vi.mock("@agentclientprotocol/sdk", () => ({
|
||||
TerminalHandle: class {
|
||||
id: string
|
||||
constructor(id: string) {
|
||||
this.id = id
|
||||
}
|
||||
async currentOutput() {
|
||||
return { output: "test output", truncated: false }
|
||||
}
|
||||
async waitForExit() {
|
||||
return { exitCode: 0, signal: null }
|
||||
}
|
||||
async kill() {
|
||||
return {}
|
||||
}
|
||||
async release() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
// Type definitions for mock objects
|
||||
interface MockTerminalHandle {
|
||||
id: string
|
||||
currentOutput: ReturnType<typeof vi.fn>
|
||||
waitForExit: ReturnType<typeof vi.fn>
|
||||
kill: ReturnType<typeof vi.fn>
|
||||
release: ReturnType<typeof vi.fn>
|
||||
}
|
||||
|
||||
interface MockConnection {
|
||||
createTerminal: ReturnType<typeof vi.fn>
|
||||
mockHandle: MockTerminalHandle
|
||||
}
|
||||
|
||||
// Create a mock connection
|
||||
function createMockConnection(): MockConnection {
|
||||
const mockHandle: MockTerminalHandle = {
|
||||
id: "term_mock123",
|
||||
currentOutput: vi.fn().mockResolvedValue({ output: "test output", truncated: false }),
|
||||
waitForExit: vi.fn().mockResolvedValue({ exitCode: 0, signal: null }),
|
||||
kill: vi.fn().mockResolvedValue({}),
|
||||
release: vi.fn().mockResolvedValue({}),
|
||||
}
|
||||
|
||||
return {
|
||||
createTerminal: vi.fn().mockResolvedValue(mockHandle),
|
||||
mockHandle,
|
||||
}
|
||||
}
|
||||
|
||||
describe("TerminalManager", () => {
|
||||
describe("parseCommand", () => {
|
||||
let manager: TerminalManager
|
||||
|
||||
beforeEach(() => {
|
||||
const mockConnection = createMockConnection()
|
||||
manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
})
|
||||
|
||||
it("parses a simple command without arguments", () => {
|
||||
const result = manager.parseCommand("ls")
|
||||
expect(result.executable).toBe("ls")
|
||||
expect(result.args).toEqual([])
|
||||
expect(result.fullCommand).toBe("ls")
|
||||
expect(result.cwd).toBeUndefined()
|
||||
})
|
||||
|
||||
it("parses a command with arguments", () => {
|
||||
const result = manager.parseCommand("ls -la /tmp")
|
||||
expect(result.executable).toBe("ls")
|
||||
expect(result.args).toEqual(["-la", "/tmp"])
|
||||
expect(result.fullCommand).toBe("ls -la /tmp")
|
||||
})
|
||||
|
||||
it("parses cd + command pattern", () => {
|
||||
const result = manager.parseCommand("cd /home/user && npm install")
|
||||
expect(result.cwd).toBe("/home/user")
|
||||
expect(result.executable).toBe("npm")
|
||||
expect(result.args).toEqual(["install"])
|
||||
})
|
||||
|
||||
it("handles cd with complex path", () => {
|
||||
const result = manager.parseCommand("cd /path/to/project && git status")
|
||||
expect(result.cwd).toBe("/path/to/project")
|
||||
expect(result.executable).toBe("git")
|
||||
expect(result.args).toEqual(["status"])
|
||||
})
|
||||
|
||||
it("wraps commands with shell operators in a shell", () => {
|
||||
const result = manager.parseCommand("echo hello | grep h")
|
||||
expect(result.executable).toBe("/bin/sh")
|
||||
expect(result.args).toEqual(["-c", "echo hello | grep h"])
|
||||
})
|
||||
|
||||
it("wraps commands with && in a shell", () => {
|
||||
const result = manager.parseCommand("npm install && npm test")
|
||||
expect(result.executable).toBe("/bin/sh")
|
||||
expect(result.args).toEqual(["-c", "npm install && npm test"])
|
||||
})
|
||||
|
||||
it("wraps commands with semicolons in a shell", () => {
|
||||
const result = manager.parseCommand("echo a; echo b")
|
||||
expect(result.executable).toBe("/bin/sh")
|
||||
expect(result.args).toEqual(["-c", "echo a; echo b"])
|
||||
})
|
||||
|
||||
it("wraps commands with redirects in a shell", () => {
|
||||
const result = manager.parseCommand("echo hello > output.txt")
|
||||
expect(result.executable).toBe("/bin/sh")
|
||||
expect(result.args).toEqual(["-c", "echo hello > output.txt"])
|
||||
})
|
||||
|
||||
it("handles whitespace-only input", () => {
|
||||
const result = manager.parseCommand(" ")
|
||||
expect(result.executable).toBe("")
|
||||
expect(result.args).toEqual([])
|
||||
})
|
||||
|
||||
it("trims leading and trailing whitespace", () => {
|
||||
const result = manager.parseCommand(" ls -la ")
|
||||
expect(result.executable).toBe("ls")
|
||||
expect(result.args).toEqual(["-la"])
|
||||
})
|
||||
|
||||
it("handles npm commands", () => {
|
||||
const result = manager.parseCommand("npm run test")
|
||||
expect(result.executable).toBe("npm")
|
||||
expect(result.args).toEqual(["run", "test"])
|
||||
})
|
||||
|
||||
it("handles npx commands", () => {
|
||||
const result = manager.parseCommand("npx vitest run src/test.ts")
|
||||
expect(result.executable).toBe("npx")
|
||||
expect(result.args).toEqual(["vitest", "run", "src/test.ts"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("terminal lifecycle", () => {
|
||||
it("creates a terminal and tracks it", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
const result = await manager.createTerminal("ls -la", "/home/user")
|
||||
|
||||
expect(mockConnection.createTerminal).toHaveBeenCalledWith({
|
||||
sessionId: "session123",
|
||||
command: "ls",
|
||||
args: ["-la"],
|
||||
cwd: "/home/user",
|
||||
})
|
||||
|
||||
expect(result.terminalId).toBe("term_mock123")
|
||||
expect(manager.hasTerminal("term_mock123")).toBe(true)
|
||||
expect(manager.activeCount).toBe(1)
|
||||
})
|
||||
|
||||
it("releases a terminal and removes from tracking", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
await manager.createTerminal("ls", "/tmp")
|
||||
expect(manager.hasTerminal("term_mock123")).toBe(true)
|
||||
|
||||
const released = await manager.releaseTerminal("term_mock123")
|
||||
expect(released).toBe(true)
|
||||
expect(manager.hasTerminal("term_mock123")).toBe(false)
|
||||
expect(manager.activeCount).toBe(0)
|
||||
})
|
||||
|
||||
it("releases all terminals", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
let terminalCount = 0
|
||||
|
||||
// Mock multiple terminal creations
|
||||
mockConnection.createTerminal = vi.fn().mockImplementation(() => {
|
||||
terminalCount++
|
||||
return Promise.resolve({
|
||||
id: `term_${terminalCount}`,
|
||||
currentOutput: vi.fn().mockResolvedValue({ output: "", truncated: false }),
|
||||
waitForExit: vi.fn().mockResolvedValue({ exitCode: 0, signal: null }),
|
||||
kill: vi.fn().mockResolvedValue({}),
|
||||
release: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
})
|
||||
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
await manager.createTerminal("ls", "/tmp")
|
||||
await manager.createTerminal("pwd", "/home")
|
||||
|
||||
expect(manager.activeCount).toBe(2)
|
||||
|
||||
await manager.releaseAll()
|
||||
|
||||
expect(manager.activeCount).toBe(0)
|
||||
})
|
||||
|
||||
it("returns null for unknown terminal operations", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
const output = await manager.getOutput("unknown_terminal")
|
||||
expect(output).toBeNull()
|
||||
|
||||
const exitResult = await manager.waitForExit("unknown_terminal")
|
||||
expect(exitResult).toBeNull()
|
||||
|
||||
const killResult = await manager.killTerminal("unknown_terminal")
|
||||
expect(killResult).toBe(false)
|
||||
|
||||
const releaseResult = await manager.releaseTerminal("unknown_terminal")
|
||||
expect(releaseResult).toBe(false)
|
||||
})
|
||||
|
||||
it("gets terminal info", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
await manager.createTerminal("ls -la", "/home/user", "tool-123")
|
||||
|
||||
const info = manager.getTerminalInfo("term_mock123")
|
||||
expect(info).toBeDefined()
|
||||
expect(info?.command).toBe("ls -la")
|
||||
expect(info?.cwd).toBe("/home/user")
|
||||
expect(info?.toolCallId).toBe("tool-123")
|
||||
})
|
||||
|
||||
it("gets active terminal IDs", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
let terminalCount = 0
|
||||
|
||||
mockConnection.createTerminal = vi.fn().mockImplementation(() => {
|
||||
terminalCount++
|
||||
return Promise.resolve({
|
||||
id: `term_${terminalCount}`,
|
||||
currentOutput: vi.fn().mockResolvedValue({ output: "", truncated: false }),
|
||||
waitForExit: vi.fn().mockResolvedValue({ exitCode: 0, signal: null }),
|
||||
kill: vi.fn().mockResolvedValue({}),
|
||||
release: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
})
|
||||
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
await manager.createTerminal("ls", "/tmp")
|
||||
await manager.createTerminal("pwd", "/home")
|
||||
|
||||
const ids = manager.getActiveTerminalIds()
|
||||
expect(ids).toHaveLength(2)
|
||||
expect(ids).toContain("term_1")
|
||||
expect(ids).toContain("term_2")
|
||||
})
|
||||
|
||||
it("waits for terminal exit and returns result", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
await manager.createTerminal("ls", "/tmp")
|
||||
|
||||
const result = await manager.waitForExit("term_mock123")
|
||||
|
||||
expect(result).toEqual({
|
||||
exitCode: 0,
|
||||
signal: null,
|
||||
output: "test output",
|
||||
})
|
||||
})
|
||||
|
||||
it("kills a terminal", async () => {
|
||||
const mockConnection = createMockConnection()
|
||||
const manager = new TerminalManager("session123", mockConnection as unknown as acp.AgentSideConnection)
|
||||
|
||||
await manager.createTerminal("sleep 60", "/tmp")
|
||||
|
||||
const killed = await manager.killTerminal("term_mock123")
|
||||
expect(killed).toBe(true)
|
||||
expect(mockConnection.mockHandle.kill).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
# Agent Plan
|
||||
|
||||
> How Agents communicate their execution plans
|
||||
|
||||
Plans are execution strategies for complex tasks that require multiple steps.
|
||||
|
||||
Agents may share plans with Clients through [`session/update`](./prompt-turn#3-agent-reports-output) notifications, providing real-time visibility into their thinking and progress.
|
||||
|
||||
## Creating Plans
|
||||
|
||||
When the language model creates an execution plan, the Agent **SHOULD** report it to the Client:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "plan",
|
||||
"entries": [
|
||||
{
|
||||
"content": "Analyze the existing codebase structure",
|
||||
"priority": "high",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"content": "Identify components that need refactoring",
|
||||
"priority": "high",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"content": "Create unit tests for critical functions",
|
||||
"priority": "medium",
|
||||
"status": "pending"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="entries" type="PlanEntry[]" required>
|
||||
An array of [plan entries](#plan-entries) representing the tasks to be
|
||||
accomplished
|
||||
</ParamField>
|
||||
|
||||
## Plan Entries
|
||||
|
||||
Each plan entry represents a specific task or goal within the overall execution strategy:
|
||||
|
||||
<ParamField path="content" type="string" required>
|
||||
A human-readable description of what this task aims to accomplish
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="priority" type="PlanEntryPriority" required>
|
||||
The relative importance of this task.
|
||||
|
||||
- `high`
|
||||
- `medium`
|
||||
- `low`
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="status" type="PlanEntryStatus" required>
|
||||
The current [execution status](#status) of this task
|
||||
|
||||
- `pending`
|
||||
- `in_progress`
|
||||
- `completed`
|
||||
</ParamField>
|
||||
|
||||
## Updating Plans
|
||||
|
||||
As the Agent progresses through the plan, it **SHOULD** report updates by sending more `session/update` notifications with the same structure.
|
||||
|
||||
The Agent **MUST** send a complete list of all plan entries in each update and their current status. The Client **MUST** replace the current plan completely.
|
||||
|
||||
### Dynamic Planning
|
||||
|
||||
Plans can evolve during execution. The Agent **MAY** add, remove, or modify plan entries as it discovers new requirements or completes tasks, allowing it to adapt based on what it learns.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
# Content
|
||||
|
||||
> Understanding content blocks in the Agent Client Protocol
|
||||
|
||||
Content blocks represent displayable information that flows through the Agent Client Protocol. They provide a structured way to handle various types of user-facing content—whether it's text from language models, images for analysis, or embedded resources for context.
|
||||
|
||||
Content blocks appear in:
|
||||
|
||||
- User prompts sent via [`session/prompt`](./prompt-turn#1-user-message)
|
||||
- Language model output streamed through [`session/update`](./prompt-turn#3-agent-reports-output) notifications
|
||||
- Progress updates and results from [tool calls](./tool-calls)
|
||||
|
||||
## Content Types
|
||||
|
||||
The Agent Client Protocol uses the same `ContentBlock` structure as the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/specification/2025-06-18/schema#contentblock).
|
||||
|
||||
This design choice enables Agents to seamlessly forward content from MCP tool outputs without transformation.
|
||||
|
||||
### Text Content
|
||||
|
||||
Plain text messages form the foundation of most interactions.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "text",
|
||||
"text": "What's the weather like today?"
|
||||
}
|
||||
```
|
||||
|
||||
All Agents **MUST** support text content blocks when included in prompts.
|
||||
|
||||
<ParamField path="text" type="string" required>
|
||||
The text content to display
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="annotations" type="Annotations">
|
||||
Optional metadata about how the content should be used or displayed. [Learn
|
||||
more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations).
|
||||
</ParamField>
|
||||
|
||||
### Image Content <Icon icon="asterisk" size="14" />
|
||||
|
||||
Images can be included for visual context or analysis.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "image",
|
||||
"mimeType": "image/png",
|
||||
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..."
|
||||
}
|
||||
```
|
||||
|
||||
<Icon icon="asterisk" size="14" /> Requires the `image` [prompt
|
||||
capability](./initialization#prompt-capabilities) when included in prompts.
|
||||
|
||||
<ParamField path="data" type="string" required>
|
||||
Base64-encoded image data
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="mimeType" type="string" required>
|
||||
The MIME type of the image (e.g., "image/png", "image/jpeg")
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="uri" type="string">
|
||||
Optional URI reference for the image source
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="annotations" type="Annotations">
|
||||
Optional metadata about how the content should be used or displayed. [Learn
|
||||
more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations).
|
||||
</ParamField>
|
||||
|
||||
### Audio Content <Icon icon="asterisk" size="14" />
|
||||
|
||||
Audio data for transcription or analysis.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "audio",
|
||||
"mimeType": "audio/wav",
|
||||
"data": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAAB..."
|
||||
}
|
||||
```
|
||||
|
||||
<Icon icon="asterisk" size="14" /> Requires the `audio` [prompt
|
||||
capability](./initialization#prompt-capabilities) when included in prompts.
|
||||
|
||||
<ParamField path="data" type="string" required>
|
||||
Base64-encoded audio data
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="mimeType" type="string" required>
|
||||
The MIME type of the audio (e.g., "audio/wav", "audio/mp3")
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="annotations" type="Annotations">
|
||||
Optional metadata about how the content should be used or displayed. [Learn
|
||||
more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations).
|
||||
</ParamField>
|
||||
|
||||
### Embedded Resource <Icon icon="asterisk" size="14" />
|
||||
|
||||
Complete resource contents embedded directly in the message.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "resource",
|
||||
"resource": {
|
||||
"uri": "file:///home/user/script.py",
|
||||
"mimeType": "text/x-python",
|
||||
"text": "def hello():\n print('Hello, world!')"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is the preferred way to include context in prompts, such as when using @-mentions to reference files or other resources.
|
||||
|
||||
By embedding the content directly in the request, Clients can include context from sources that the Agent may not have direct access to.
|
||||
|
||||
<Icon icon="asterisk" size="14" /> Requires the `embeddedContext` [prompt
|
||||
capability](./initialization#prompt-capabilities) when included in prompts.
|
||||
|
||||
<ParamField path="resource" type="EmbeddedResourceResource" required>
|
||||
The embedded resource contents, which can be either:
|
||||
|
||||
<Expandable title="Text Resource">
|
||||
<ParamField path="uri" type="string" required>
|
||||
The URI identifying the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="text" type="string" required>
|
||||
The text content of the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="mimeType" type="string">
|
||||
Optional MIME type of the text content
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
|
||||
<Expandable title="Blob Resource">
|
||||
<ParamField path="uri" type="string" required>
|
||||
The URI identifying the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="blob" type="string" required>
|
||||
Base64-encoded binary data
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="mimeType" type="string">
|
||||
Optional MIME type of the blob
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="annotations" type="Annotations">
|
||||
Optional metadata about how the content should be used or displayed. [Learn
|
||||
more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations).
|
||||
</ParamField>
|
||||
|
||||
### Resource Link
|
||||
|
||||
References to resources that the Agent can access.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "resource_link",
|
||||
"uri": "file:///home/user/document.pdf",
|
||||
"name": "document.pdf",
|
||||
"mimeType": "application/pdf",
|
||||
"size": 1024000
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="uri" type="string" required>
|
||||
The URI of the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="name" type="string" required>
|
||||
A human-readable name for the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="mimeType" type="string">
|
||||
The MIME type of the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="title" type="string">
|
||||
Optional display title for the resource
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="description" type="string">
|
||||
Optional description of the resource contents
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="size" type="integer">
|
||||
Optional size of the resource in bytes
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="annotations" type="Annotations">
|
||||
Optional metadata about how the content should be used or displayed. [Learn
|
||||
more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations).
|
||||
</ParamField>
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
# Extensibility
|
||||
|
||||
> Adding custom data and capabilities
|
||||
|
||||
The Agent Client Protocol provides built-in extension mechanisms that allow implementations to add custom functionality while maintaining compatibility with the core protocol. These mechanisms ensure that Agents and Clients can innovate without breaking interoperability.
|
||||
|
||||
## The `_meta` Field
|
||||
|
||||
All types in the protocol include a `_meta` field with type `{ [key: string]: unknown }` that implementations can use to attach custom information. This includes requests, responses, notifications, and even nested types like content blocks, tool calls, plan entries, and capability objects.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "session/prompt",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"prompt": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Hello, world!"
|
||||
}
|
||||
],
|
||||
"_meta": {
|
||||
"traceparent": "00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01",
|
||||
"zed.dev/debugMode": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clients may propagate fields to the agent for correlation purposes, such as `requestId`. The following root-level keys in `_meta` **SHOULD** be reserved for [W3C trace context](https://www.w3.org/TR/trace-context/) to guarantee interop with existing MCP implementations and OpenTelemetry tooling:
|
||||
|
||||
- `traceparent`
|
||||
- `tracestate`
|
||||
- `baggage`
|
||||
|
||||
Implementations **MUST NOT** add any custom fields at the root of a type that's part of the specification. All possible names are reserved for future protocol versions.
|
||||
|
||||
## Extension Methods
|
||||
|
||||
The protocol reserves any method name starting with an underscore (`_`) for custom extensions. This allows implementations to add new functionality without the risk of conflicting with future protocol versions.
|
||||
|
||||
Extension methods follow standard [JSON-RPC 2.0](https://www.jsonrpc.org/specification) semantics:
|
||||
|
||||
- **[Requests](https://www.jsonrpc.org/specification#request_object)** - Include an `id` field and expect a response
|
||||
- **[Notifications](https://www.jsonrpc.org/specification#notification)** - Omit the `id` field and are one-way
|
||||
|
||||
### Custom Requests
|
||||
|
||||
In addition to the requests specified by the protocol, implementations **MAY** expose and call custom JSON-RPC requests as long as their name starts with an underscore (`_`).
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "_zed.dev/workspace/buffers",
|
||||
"params": {
|
||||
"language": "rust"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Upon receiving a custom request, implementations **MUST** respond accordingly with the provided `id`:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"buffers": [
|
||||
{ "id": 0, "path": "/home/user/project/src/main.rs" },
|
||||
{ "id": 1, "path": "/home/user/project/src/editor.rs" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the receiving end doesn't recognize the custom method name, it should respond with the standard "Method not found" error:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"error": {
|
||||
"code": -32601,
|
||||
"message": "Method not found"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To avoid such cases, extensions **SHOULD** advertise their [custom capabilities](#advertising-custom-capabilities) so that callers can check their availability first and adapt their behavior or interface accordingly.
|
||||
|
||||
### Custom Notifications
|
||||
|
||||
Custom notifications are regular JSON-RPC notifications that start with an underscore (`_`). Like all notifications, they omit the `id` field:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "_zed.dev/file_opened",
|
||||
"params": {
|
||||
"path": "/home/user/project/src/editor.rs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Unlike with custom requests, implementations **SHOULD** ignore unrecognized notifications.
|
||||
|
||||
## Advertising Custom Capabilities
|
||||
|
||||
Implementations **SHOULD** use the `_meta` field in capability objects to advertise support for extensions and their methods:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"agentCapabilities": {
|
||||
"loadSession": true,
|
||||
"_meta": {
|
||||
"zed.dev": {
|
||||
"workspace": true,
|
||||
"fileNotifications": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This allows implementations to negotiate custom features during initialization without breaking compatibility with standard Clients and Agents.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
# File System
|
||||
|
||||
> Client filesystem access methods
|
||||
|
||||
The filesystem methods allow Agents to read and write text files within the Client's environment. These methods enable Agents to access unsaved editor state and allow Clients to track file modifications made during agent execution.
|
||||
|
||||
## Checking Support
|
||||
|
||||
Before attempting to use filesystem methods, Agents **MUST** verify that the Client supports these capabilities by checking the [Client Capabilities](./initialization#client-capabilities) field in the `initialize` response:
|
||||
|
||||
```json highlight={8,9} theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"clientCapabilities": {
|
||||
"fs": {
|
||||
"readTextFile": true,
|
||||
"writeTextFile": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `readTextFile` or `writeTextFile` is `false` or not present, the Agent **MUST NOT** attempt to call the corresponding filesystem method.
|
||||
|
||||
## Reading Files
|
||||
|
||||
The `fs/read_text_file` method allows Agents to read text file contents from the Client's filesystem, including unsaved changes in the editor.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "fs/read_text_file",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"path": "/home/user/project/src/main.py",
|
||||
"line": 10,
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="sessionId" type="SessionId" required>
|
||||
The [Session ID](./session-setup#session-id) for this request
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="path" type="string" required>
|
||||
Absolute path to the file to read
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="line" type="number">
|
||||
Optional line number to start reading from (1-based)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="limit" type="number">
|
||||
Optional maximum number of lines to read
|
||||
</ParamField>
|
||||
|
||||
The Client responds with the file contents:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"result": {
|
||||
"content": "def hello_world():\n print('Hello, world!')\n"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Writing Files
|
||||
|
||||
The `fs/write_text_file` method allows Agents to write or update text files in the Client's filesystem.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "fs/write_text_file",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"path": "/home/user/project/config.json",
|
||||
"content": "{\n \"debug\": true,\n \"version\": \"1.0.0\"\n}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="sessionId" type="SessionId" required>
|
||||
The [Session ID](./session-setup#session-id) for this request
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="path" type="string" required>
|
||||
Absolute path to the file to write.
|
||||
|
||||
The Client **MUST** create the file if it doesn't exist.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="content" type="string" required>
|
||||
The text content to write to the file
|
||||
</ParamField>
|
||||
|
||||
The Client responds with an empty result on success:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
# Initialization
|
||||
|
||||
> How all Agent Client Protocol connections begin
|
||||
|
||||
The Initialization phase allows [Clients](./overview#client) and [Agents](./overview#agent) to negotiate protocol versions, capabilities, and authentication methods.
|
||||
|
||||
<br />
|
||||
|
||||
```mermaid theme={null}
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Agent
|
||||
|
||||
Note over Client, Agent: Connection established
|
||||
Client->>Agent: initialize
|
||||
Note right of Agent: Negotiate protocol<br/>version & capabilities
|
||||
Agent-->>Client: initialize response
|
||||
Note over Client,Agent: Ready for session setup
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
Before a Session can be created, Clients **MUST** initialize the connection by calling the `initialize` method with:
|
||||
|
||||
- The latest [protocol version](#protocol-version) supported
|
||||
- The [capabilities](#client-capabilities) supported
|
||||
|
||||
They **SHOULD** also provide a name and version to the Agent.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": 1,
|
||||
"clientCapabilities": {
|
||||
"fs": {
|
||||
"readTextFile": true,
|
||||
"writeTextFile": true
|
||||
},
|
||||
"terminal": true
|
||||
},
|
||||
"clientInfo": {
|
||||
"name": "my-client",
|
||||
"title": "My Client",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Agent **MUST** respond with the chosen [protocol version](#protocol-version) and the [capabilities](#agent-capabilities) it supports. It **SHOULD** also provide a name and version to the Client as well:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"agentCapabilities": {
|
||||
"loadSession": true,
|
||||
"promptCapabilities": {
|
||||
"image": true,
|
||||
"audio": true,
|
||||
"embeddedContext": true
|
||||
},
|
||||
"mcp": {
|
||||
"http": true,
|
||||
"sse": true
|
||||
}
|
||||
},
|
||||
"agentInfo": {
|
||||
"name": "my-agent",
|
||||
"title": "My Agent",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"authMethods": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Protocol version
|
||||
|
||||
The protocol versions that appear in the `initialize` requests and responses are a single integer that identifies a **MAJOR** protocol version. This version is only incremented when breaking changes are introduced.
|
||||
|
||||
Clients and Agents **MUST** agree on a protocol version and act according to its specification.
|
||||
|
||||
See [Capabilities](#capabilities) to learn how non-breaking features are introduced.
|
||||
|
||||
### Version Negotiation
|
||||
|
||||
The `initialize` request **MUST** include the latest protocol version the Client supports.
|
||||
|
||||
If the Agent supports the requested version, it **MUST** respond with the same version. Otherwise, the Agent **MUST** respond with the latest version it supports.
|
||||
|
||||
If the Client does not support the version specified by the Agent in the `initialize` response, the Client **SHOULD** close the connection and inform the user about it.
|
||||
|
||||
## Capabilities
|
||||
|
||||
Capabilities describe features supported by the Client and the Agent.
|
||||
|
||||
All capabilities included in the `initialize` request are **OPTIONAL**. Clients and Agents **SHOULD** support all possible combinations of their peer's capabilities.
|
||||
|
||||
The introduction of new capabilities is not considered a breaking change. Therefore, Clients and Agents **MUST** treat all capabilities omitted in the `initialize` request as **UNSUPPORTED**.
|
||||
|
||||
Capabilities are high-level and are not attached to a specific base protocol concept.
|
||||
|
||||
Capabilities may specify the availability of protocol methods, notifications, or a subset of their parameters. They may also signal behaviors of the Agent or Client implementation.
|
||||
|
||||
Implementations can also [advertise custom capabilities](./extensibility#advertising-custom-capabilities) using the `_meta` field to indicate support for protocol extensions.
|
||||
|
||||
### Client Capabilities
|
||||
|
||||
The Client **SHOULD** specify whether it supports the following capabilities:
|
||||
|
||||
#### File System
|
||||
|
||||
<ParamField path="readTextFile" type="boolean">
|
||||
The `fs/read_text_file` method is available.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="writeTextFile" type="boolean">
|
||||
The `fs/write_text_file` method is available.
|
||||
</ParamField>
|
||||
|
||||
<Card icon="file" horizontal href="./file-system">
|
||||
Learn more about File System methods
|
||||
</Card>
|
||||
|
||||
#### Terminal
|
||||
|
||||
<ParamField path="terminal" type="boolean">
|
||||
All `terminal/*` methods are available, allowing the Agent to execute and
|
||||
manage shell commands.
|
||||
</ParamField>
|
||||
|
||||
<Card icon="terminal" horizontal href="./terminals">
|
||||
Learn more about Terminals
|
||||
</Card>
|
||||
|
||||
### Agent Capabilities
|
||||
|
||||
The Agent **SHOULD** specify whether it supports the following capabilities:
|
||||
|
||||
<ResponseField name="loadSession" type="boolean" post={["default: false"]}>
|
||||
The [`session/load`](./session-setup#loading-sessions) method is available.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="promptCapabilities" type="PromptCapabilities Object">
|
||||
Object indicating the different types of [content](./content) that may be
|
||||
included in `session/prompt` requests.
|
||||
</ResponseField>
|
||||
|
||||
#### Prompt capabilities
|
||||
|
||||
As a baseline, all Agents **MUST** support `ContentBlock::Text` and `ContentBlock::ResourceLink` in `session/prompt` requests.
|
||||
|
||||
Optionally, they **MAY** support richer types of [content](./content) by specifying the following capabilities:
|
||||
|
||||
<ResponseField name="image" type="boolean" post={["default: false"]}>
|
||||
The prompt may include `ContentBlock::Image`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="audio" type="boolean" post={["default: false"]}>
|
||||
The prompt may include `ContentBlock::Audio`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="embeddedContext" type="boolean" post={["default: false"]}>
|
||||
The prompt may include `ContentBlock::Resource`
|
||||
</ResponseField>
|
||||
|
||||
#### MCP capabilities
|
||||
|
||||
<ResponseField name="http" type="boolean" post={["default: false"]}>
|
||||
The Agent supports connecting to MCP servers over HTTP.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="sse" type="boolean" post={["default: false"]}>
|
||||
The Agent supports connecting to MCP servers over SSE.
|
||||
|
||||
Note: This transport has been deprecated by the MCP spec.
|
||||
</ResponseField>
|
||||
|
||||
#### Session Capabilities
|
||||
|
||||
As a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`.
|
||||
|
||||
Optionally, they **MAY** support other session methods and notifications by specifying additional capabilities.
|
||||
|
||||
<Note>
|
||||
`session/load` is still handled by the top-level `load_session` capability.
|
||||
This will be unified in future versions of the protocol.
|
||||
</Note>
|
||||
|
||||
## Implementation Information
|
||||
|
||||
Both Clients and Agents **SHOULD** provide information about their implementation in the `clientInfo` and `agentInfo` fields respectively. Both take the following three fields:
|
||||
|
||||
<ParamField path="name" type="string">
|
||||
Intended for programmatic or logical use, but can be used as a display name
|
||||
fallback if title isn’t present.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="title" type="string">
|
||||
Intended for UI and end-user contexts — optimized to be human-readable and
|
||||
easily understood. If not provided, the name should be used for display.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="version" type="string">
|
||||
Version of the implementation. Can be displayed to the user or used for
|
||||
debugging or metrics purposes.
|
||||
</ParamField>
|
||||
|
||||
<Info>
|
||||
Note: in future versions of the protocol, this information will be required.
|
||||
</Info>
|
||||
|
||||
---
|
||||
|
||||
Once the connection is initialized, you're ready to [create a session](./session-setup) and begin the conversation with the Agent.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
# Agent Client Protocol
|
||||
|
||||
## Docs
|
||||
|
||||
- [Brand](https://agentclientprotocol.com/brand.md): Assets for the Agent Client Protocol brand.
|
||||
- [Code of Conduct](https://agentclientprotocol.com/community/code-of-conduct.md)
|
||||
- [Contributor Communication](https://agentclientprotocol.com/community/communication.md): Communication methods for Agent Client Protocol contributors
|
||||
- [Contributing](https://agentclientprotocol.com/community/contributing.md): How to participate in the development of ACP
|
||||
- [Governance](https://agentclientprotocol.com/community/governance.md): How the ACP project is governed
|
||||
- [Working and Interest Groups](https://agentclientprotocol.com/community/working-interest-groups.md): Learn about the two forms of collaborative groups within the Agent Client Protocol's governance structure - Working Groups and Interest Groups.
|
||||
- [Community](https://agentclientprotocol.com/libraries/community.md): Community managed libraries for the Agent Client Protocol
|
||||
- [Kotlin](https://agentclientprotocol.com/libraries/kotlin.md): Kotlin library for the Agent Client Protocol
|
||||
- [Python](https://agentclientprotocol.com/libraries/python.md): Python library for the Agent Client Protocol
|
||||
- [Rust](https://agentclientprotocol.com/libraries/rust.md): Rust library for the Agent Client Protocol
|
||||
- [TypeScript](https://agentclientprotocol.com/libraries/typescript.md): TypeScript library for the Agent Client Protocol
|
||||
- [Agents](https://agentclientprotocol.com/overview/agents.md): Agents implementing the Agent Client Protocol
|
||||
- [Architecture](https://agentclientprotocol.com/overview/architecture.md): Overview of the Agent Client Protocol architecture
|
||||
- [Clients](https://agentclientprotocol.com/overview/clients.md): Clients implementing the Agent Client Protocol
|
||||
- [Introduction](https://agentclientprotocol.com/overview/introduction.md): Get started with the Agent Client Protocol (ACP)
|
||||
- [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan.md): How Agents communicate their execution plans
|
||||
- [Content](https://agentclientprotocol.com/protocol/content.md): Understanding content blocks in the Agent Client Protocol
|
||||
- [Cancellation](https://agentclientprotocol.com/protocol/draft/cancellation.md): Mechanisms for request cancellation
|
||||
- [Schema](https://agentclientprotocol.com/protocol/draft/schema.md): Schema definitions for the Agent Client Protocol
|
||||
- [Extensibility](https://agentclientprotocol.com/protocol/extensibility.md): Adding custom data and capabilities
|
||||
- [File System](https://agentclientprotocol.com/protocol/file-system.md): Client filesystem access methods
|
||||
- [Initialization](https://agentclientprotocol.com/protocol/initialization.md): How all Agent Client Protocol connections begin
|
||||
- [Overview](https://agentclientprotocol.com/protocol/overview.md): How the Agent Client Protocol works
|
||||
- [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn.md): Understanding the core conversation flow
|
||||
- [Schema](https://agentclientprotocol.com/protocol/schema.md): Schema definitions for the Agent Client Protocol
|
||||
- [Session Modes](https://agentclientprotocol.com/protocol/session-modes.md): Switch between different agent operating modes
|
||||
- [Session Setup](https://agentclientprotocol.com/protocol/session-setup.md): Creating and loading sessions
|
||||
- [Slash Commands](https://agentclientprotocol.com/protocol/slash-commands.md): Advertise available slash commands to clients
|
||||
- [Terminals](https://agentclientprotocol.com/protocol/terminals.md): Executing and managing terminal commands
|
||||
- [Tool Calls](https://agentclientprotocol.com/protocol/tool-calls.md): How Agents report tool call execution
|
||||
- [Transports](https://agentclientprotocol.com/protocol/transports.md): Mechanisms for agents and clients to communicate with each other
|
||||
- [Requests for Dialog (RFDs)](https://agentclientprotocol.com/rfds/about.md): Our process for introducing changes to the protocol
|
||||
- [ACP Agent Registry](https://agentclientprotocol.com/rfds/acp-agent-registry.md)
|
||||
- [Agent Telemetry Export](https://agentclientprotocol.com/rfds/agent-telemetry-export.md)
|
||||
- [Introduce RFD Process](https://agentclientprotocol.com/rfds/introduce-rfd-process.md)
|
||||
- [MCP-over-ACP: MCP Transport via ACP Channels](https://agentclientprotocol.com/rfds/mcp-over-acp.md)
|
||||
- [Meta Field Propagation Conventions](https://agentclientprotocol.com/rfds/meta-propagation.md)
|
||||
- [Agent Extensions via ACP Proxies](https://agentclientprotocol.com/rfds/proxy-chains.md)
|
||||
- [Request Cancellation Mechanism](https://agentclientprotocol.com/rfds/request-cancellation.md)
|
||||
- [Session Config Options](https://agentclientprotocol.com/rfds/session-config-options.md)
|
||||
- [Forking of existing sessions](https://agentclientprotocol.com/rfds/session-fork.md)
|
||||
- [Session Info Update](https://agentclientprotocol.com/rfds/session-info-update.md)
|
||||
- [Session List](https://agentclientprotocol.com/rfds/session-list.md)
|
||||
- [Resuming of existing sessions](https://agentclientprotocol.com/rfds/session-resume.md)
|
||||
- [Session Usage and Context Status](https://agentclientprotocol.com/rfds/session-usage.md)
|
||||
- [Updates](https://agentclientprotocol.com/updates.md): Updates and announcements about the Agent Client Protocol
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
# Overview
|
||||
|
||||
> How the Agent Client Protocol works
|
||||
|
||||
The Agent Client Protocol allows [Agents](#agent) and [Clients](#client) to communicate by exposing methods that each side can call and sending notifications to inform each other of events.
|
||||
|
||||
## Communication Model
|
||||
|
||||
The protocol follows the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification with two types of messages:
|
||||
|
||||
- **Methods**: Request-response pairs that expect a result or error
|
||||
- **Notifications**: One-way messages that don't expect a response
|
||||
|
||||
## Message Flow
|
||||
|
||||
A typical flow follows this pattern:
|
||||
|
||||
<Steps>
|
||||
<Step title="Initialization Phase">
|
||||
* Client → Agent: `initialize` to establish connection
|
||||
* Client → Agent: `authenticate` if required by the Agent
|
||||
</Step>
|
||||
|
||||
<Step title="Session Setup - either:">
|
||||
* Client → Agent: `session/new` to create a new session
|
||||
* Client → Agent: `session/load` to resume an existing session if supported
|
||||
</Step>
|
||||
|
||||
<Step title="Prompt Turn">
|
||||
* Client → Agent: `session/prompt` to send user message
|
||||
* Agent → Client: `session/update` notifications for progress updates
|
||||
* Agent → Client: File operations or permission requests as needed
|
||||
* Client → Agent: `session/cancel` to interrupt processing if needed
|
||||
* Turn ends and the Agent sends the `session/prompt` response with a stop reason
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Agent
|
||||
|
||||
Agents are programs that use generative AI to autonomously modify code. They typically run as subprocesses of the Client.
|
||||
|
||||
### Baseline Methods
|
||||
|
||||
<ResponseField name="initialize" post={[<a href="./schema#initialize">Schema</a>]}>
|
||||
[Negotiate versions and exchange capabilities.](./initialization).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="authenticate" post={[<a href="./schema#authenticate">Schema</a>]}>
|
||||
Authenticate with the Agent (if required).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="session/new" post={[<a href="./schema#session%2Fnew">Schema</a>]}>
|
||||
[Create a new conversation session](./session-setup#creating-a-session).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="session/prompt" post={[<a href="./schema#session%2Fprompt">Schema</a>]}>
|
||||
[Send user prompts](./prompt-turn#1-user-message) to the Agent.
|
||||
</ResponseField>
|
||||
|
||||
### Optional Methods
|
||||
|
||||
<ResponseField name="session/load" post={[<a href="./schema#session%2Fload">Schema</a>]}>
|
||||
[Load an existing session](./session-setup#loading-sessions) (requires
|
||||
`loadSession` capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="session/set_mode" post={[<a href="./schema#session%2Fset-mode">Schema</a>]}>
|
||||
[Switch between agent operating
|
||||
modes](./session-modes#setting-the-current-mode).
|
||||
</ResponseField>
|
||||
|
||||
### Notifications
|
||||
|
||||
<ResponseField name="session/cancel" post={[<a href="./schema#session%2Fcancel">Schema</a>]}>
|
||||
[Cancel ongoing operations](./prompt-turn#cancellation) (no response
|
||||
expected).
|
||||
</ResponseField>
|
||||
|
||||
## Client
|
||||
|
||||
Clients provide the interface between users and agents. They are typically code editors (IDEs, text editors) but can also be other UIs for interacting with agents. Clients manage the environment, handle user interactions, and control access to resources.
|
||||
|
||||
### Baseline Methods
|
||||
|
||||
<ResponseField name="session/request_permission" post={[<a href="./schema#session%2Frequest_permission">Schema</a>]}>
|
||||
[Request user authorization](./tool-calls#requesting-permission) for tool
|
||||
calls.
|
||||
</ResponseField>
|
||||
|
||||
### Optional Methods
|
||||
|
||||
<ResponseField name="fs/read_text_file" post={[<a href="./schema#fs%2Fread_text_file">Schema</a>]}>
|
||||
[Read file contents](./file-system#reading-files) (requires `fs.readTextFile`
|
||||
capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="fs/write_text_file" post={[<a href="./schema#fs%2Fwrite_text_file">Schema</a>]}>
|
||||
[Write file contents](./file-system#writing-files) (requires
|
||||
`fs.writeTextFile` capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="terminal/create" post={[<a href="./schema#terminal%2Fcreate">Schema</a>]}>
|
||||
[Create a new terminal](./terminals) (requires `terminal` capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="terminal/output" post={[<a href="./schema#terminal%2Foutput">Schema</a>]}>
|
||||
Get terminal output and exit status (requires `terminal` capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="terminal/release" post={[<a href="./schema#terminal%2Frelease">Schema</a>]}>
|
||||
Release a terminal (requires `terminal` capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="terminal/wait_for_exit" post={[<a href="./schema#terminal%2Fwait_for_exit">Schema</a>]}>
|
||||
Wait for terminal command to exit (requires `terminal` capability).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="terminal/kill" post={[<a href="./schema#terminal%2Fkill">Schema</a>]}>
|
||||
Kill terminal command without releasing (requires `terminal` capability).
|
||||
</ResponseField>
|
||||
|
||||
### Notifications
|
||||
|
||||
<ResponseField name="session/update" post={[<a href="./schema#session%2Fupdate">Schema</a>]}>
|
||||
[Send session updates](./prompt-turn#3-agent-reports-output) to inform the
|
||||
Client of changes (no response expected). This includes: - [Message
|
||||
chunks](./content) (agent, user, thought) - [Tool calls and
|
||||
updates](./tool-calls) - [Plans](./agent-plan) - [Available commands
|
||||
updates](./slash-commands#advertising-commands) - [Mode
|
||||
changes](./session-modes#from-the-agent)
|
||||
</ResponseField>
|
||||
|
||||
## Argument requirements
|
||||
|
||||
- All file paths in the protocol **MUST** be absolute.
|
||||
- Line numbers are 1-based
|
||||
|
||||
## Error Handling
|
||||
|
||||
All methods follow standard JSON-RPC 2.0 [error handling](https://www.jsonrpc.org/specification#error_object):
|
||||
|
||||
- Successful responses include a `result` field
|
||||
- Errors include an `error` object with `code` and `message`
|
||||
- Notifications never receive responses (success or error)
|
||||
|
||||
## Extensibility
|
||||
|
||||
The protocol provides built-in mechanisms for adding custom functionality while maintaining compatibility:
|
||||
|
||||
- Add custom data using `_meta` fields
|
||||
- Create custom methods by prefixing their name with underscore (`_`)
|
||||
- Advertise custom capabilities during initialization
|
||||
|
||||
Learn about [protocol extensibility](./extensibility) to understand how to use these mechanisms.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Initialization](./initialization) to understand version and capability negotiation
|
||||
- Understand [Session Setup](./session-setup) for creating and loading sessions
|
||||
- Review the [Prompt Turn](./prompt-turn) lifecycle
|
||||
- Explore [Extensibility](./extensibility) to add custom features
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,321 +0,0 @@
|
|||
# Prompt Turn
|
||||
|
||||
> Understanding the core conversation flow
|
||||
|
||||
A prompt turn represents a complete interaction cycle between the [Client](./overview#client) and [Agent](./overview#agent), starting with a user message and continuing until the Agent completes its response. This may involve multiple exchanges with the language model and tool invocations.
|
||||
|
||||
Before sending prompts, Clients **MUST** first complete the [initialization](./initialization) phase and [session setup](./session-setup).
|
||||
|
||||
## The Prompt Turn Lifecycle
|
||||
|
||||
A prompt turn follows a structured flow that enables rich interactions between the user, Agent, and any connected tools.
|
||||
|
||||
<br />
|
||||
|
||||
```mermaid theme={null}
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Agent
|
||||
|
||||
Note over Agent,Client: Session ready
|
||||
|
||||
Note left of Client: User sends message
|
||||
Client->>Agent: session/prompt (user message)
|
||||
Note right of Agent: Process with LLM
|
||||
|
||||
loop Until completion
|
||||
Note right of Agent: LLM responds with<br/>content/tool calls
|
||||
Agent->>Client: session/update (plan)
|
||||
Agent->>Client: session/update (agent_message_chunk)
|
||||
|
||||
opt Tool calls requested
|
||||
Agent->>Client: session/update (tool_call)
|
||||
opt Permission required
|
||||
Agent->>Client: session/request_permission
|
||||
Note left of Client: User grants/denies
|
||||
Client-->>Agent: Permission response
|
||||
end
|
||||
Agent->>Client: session/update (tool_call status: in_progress)
|
||||
Note right of Agent: Execute tool
|
||||
Agent->>Client: session/update (tool_call status: completed)
|
||||
Note right of Agent: Send tool results<br/>back to LLM
|
||||
end
|
||||
|
||||
opt User cancelled during execution
|
||||
Note left of Client: User cancels prompt
|
||||
Client->>Agent: session/cancel
|
||||
Note right of Agent: Abort operations
|
||||
Agent-->>Client: session/prompt response (cancelled)
|
||||
end
|
||||
end
|
||||
|
||||
Agent-->>Client: session/prompt response (stopReason)
|
||||
|
||||
```
|
||||
|
||||
### 1. User Message
|
||||
|
||||
The turn begins when the Client sends a `session/prompt`:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "session/prompt",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"prompt": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Can you analyze this code for potential issues?"
|
||||
},
|
||||
{
|
||||
"type": "resource",
|
||||
"resource": {
|
||||
"uri": "file:///home/user/project/main.py",
|
||||
"mimeType": "text/x-python",
|
||||
"text": "def process_data(items):\n for item in items:\n print(item)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="sessionId" type="SessionId">
|
||||
The [ID](./session-setup#session-id) of the session to send this message to.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="prompt" type="ContentBlock[]">
|
||||
The contents of the user message, e.g. text, images, files, etc.
|
||||
|
||||
Clients **MUST** restrict types of content according to the [Prompt Capabilities](./initialization#prompt-capabilities) established during [initialization](./initialization).
|
||||
|
||||
<Card icon="comments" horizontal href="./content">
|
||||
Learn more about Content
|
||||
</Card>
|
||||
</ParamField>
|
||||
|
||||
### 2. Agent Processing
|
||||
|
||||
Upon receiving the prompt request, the Agent processes the user's message and sends it to the language model, which **MAY** respond with text content, tool calls, or both.
|
||||
|
||||
### 3. Agent Reports Output
|
||||
|
||||
The Agent reports the model's output to the Client via `session/update` notifications. This may include the Agent's plan for accomplishing the task:
|
||||
|
||||
```json expandable theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "plan",
|
||||
"entries": [
|
||||
{
|
||||
"content": "Check for syntax errors",
|
||||
"priority": "high",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"content": "Identify potential type issues",
|
||||
"priority": "medium",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"content": "Review error handling patterns",
|
||||
"priority": "medium",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"content": "Suggest improvements",
|
||||
"priority": "low",
|
||||
"status": "pending"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Card icon="lightbulb" horizontal href="./agent-plan">
|
||||
Learn more about Agent Plans
|
||||
</Card>
|
||||
|
||||
The Agent then reports text responses from the model:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "agent_message_chunk",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": "I'll analyze your code for potential issues. Let me examine it..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the model requested tool calls, these are also reported immediately:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "tool_call",
|
||||
"toolCallId": "call_001",
|
||||
"title": "Analyzing Python code",
|
||||
"kind": "other",
|
||||
"status": "pending"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Check for Completion
|
||||
|
||||
If there are no pending tool calls, the turn ends and the Agent **MUST** respond to the original `session/prompt` request with a `StopReason`:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"result": {
|
||||
"stopReason": "end_turn"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Agents **MAY** stop the turn at any point by returning the corresponding [`StopReason`](#stop-reasons).
|
||||
|
||||
### 5. Tool Invocation and Status Reporting
|
||||
|
||||
Before proceeding with execution, the Agent **MAY** request permission from the Client via the `session/request_permission` method.
|
||||
|
||||
Once permission is granted (if required), the Agent **SHOULD** invoke the tool and report a status update marking the tool as `in_progress`:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "tool_call_update",
|
||||
"toolCallId": "call_001",
|
||||
"status": "in_progress"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As the tool runs, the Agent **MAY** send additional updates, providing real-time feedback about tool execution progress.
|
||||
|
||||
While tools execute on the Agent, they **MAY** leverage Client capabilities such as the file system (`fs`) methods to access resources within the Client's environment.
|
||||
|
||||
When the tool completes, the Agent sends another update with the final status and any content:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "tool_call_update",
|
||||
"toolCallId": "call_001",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "content",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": "Analysis complete:\n- No syntax errors found\n- Consider adding type hints for better clarity\n- The function could benefit from error handling for empty lists"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Card icon="hammer" horizontal href="./tool-calls">
|
||||
Learn more about Tool Calls
|
||||
</Card>
|
||||
|
||||
### 6. Continue Conversation
|
||||
|
||||
The Agent sends the tool results back to the language model as another request.
|
||||
|
||||
The cycle returns to [step 2](#2-agent-processing), continuing until the language model completes its response without requesting additional tool calls or the turn gets stopped by the Agent or cancelled by the Client.
|
||||
|
||||
## Stop Reasons
|
||||
|
||||
When an Agent stops a turn, it must specify the corresponding `StopReason`:
|
||||
|
||||
<ResponseField name="end_turn">
|
||||
The language model finishes responding without requesting more tools
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="max_tokens">
|
||||
The maximum token limit is reached
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="max_turn_requests">
|
||||
The maximum number of model requests in a single turn is exceeded
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="refusal">The Agent refuses to continue</ResponseField>
|
||||
|
||||
<ResponseField name="cancelled">The Client cancels the turn</ResponseField>
|
||||
|
||||
## Cancellation
|
||||
|
||||
Clients **MAY** cancel an ongoing prompt turn at any time by sending a `session/cancel` notification:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/cancel",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Client **SHOULD** preemptively mark all non-finished tool calls pertaining to the current turn as `cancelled` as soon as it sends the `session/cancel` notification.
|
||||
|
||||
The Client **MUST** respond to all pending `session/request_permission` requests with the `cancelled` outcome.
|
||||
|
||||
When the Agent receives this notification, it **SHOULD** stop all language model requests and all tool call invocations as soon as possible.
|
||||
|
||||
After all ongoing operations have been successfully aborted and pending updates have been sent, the Agent **MUST** respond to the original `session/prompt` request with the `cancelled` [stop reason](#stop-reasons).
|
||||
|
||||
<Warning>
|
||||
API client libraries and tools often throw an exception when their operation is aborted, which may propagate as an error response to `session/prompt`.
|
||||
|
||||
Clients often display unrecognized errors from the Agent to the user, which would be undesirable for cancellations as they aren't considered errors.
|
||||
|
||||
Agents **MUST** catch these errors and return the semantically meaningful `cancelled` stop reason, so that Clients can reliably confirm the cancellation.
|
||||
</Warning>
|
||||
|
||||
The Agent **MAY** send `session/update` notifications with content or tool call updates after receiving the `session/cancel` notification, but it **MUST** ensure that it does so before responding to the `session/prompt` request.
|
||||
|
||||
The Client **SHOULD** still accept tool call updates received after sending `session/cancel`.
|
||||
|
||||
---
|
||||
|
||||
Once a prompt turn completes, the Client may send another `session/prompt` to continue the conversation, building on the context established in previous turns.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,170 +0,0 @@
|
|||
# Session Modes
|
||||
|
||||
> Switch between different agent operating modes
|
||||
|
||||
Agents can provide a set of modes they can operate in. Modes often affect the system prompts used, the availability of tools, and whether they request permission before running.
|
||||
|
||||
## Initial state
|
||||
|
||||
During [Session Setup](./session-setup) the Agent **MAY** return a list of modes it can operate in and the currently active mode:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"modes": {
|
||||
"currentModeId": "ask",
|
||||
"availableModes": [
|
||||
{
|
||||
"id": "ask",
|
||||
"name": "Ask",
|
||||
"description": "Request permission before making any changes"
|
||||
},
|
||||
{
|
||||
"id": "architect",
|
||||
"name": "Architect",
|
||||
"description": "Design and plan software systems without implementation"
|
||||
},
|
||||
{
|
||||
"id": "code",
|
||||
"name": "Code",
|
||||
"description": "Write and modify code with full tool access"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ResponseField name="modes" type="SessionModeState">
|
||||
The current mode state for the session
|
||||
</ResponseField>
|
||||
|
||||
### SessionModeState
|
||||
|
||||
<ResponseField name="currentModeId" type="SessionModeId" required>
|
||||
The ID of the mode that is currently active
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="availableModes" type="SessionMode[]" required>
|
||||
The set of modes that the Agent can operate in
|
||||
</ResponseField>
|
||||
|
||||
### SessionMode
|
||||
|
||||
<ResponseField name="id" type="SessionModeId" required>
|
||||
Unique identifier for this mode
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="name" type="string" required>
|
||||
Human-readable name of the mode
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="description" type="string">
|
||||
Optional description providing more details about what this mode does
|
||||
</ResponseField>
|
||||
|
||||
## Setting the current mode
|
||||
|
||||
The current mode can be changed at any point during a session, whether the Agent is idle or generating a response.
|
||||
|
||||
### From the Client
|
||||
|
||||
Typically, Clients display the available modes to the user and allow them to change the current one, which they can do by calling the [`session/set_mode`](./schema#session%2Fset-mode) method.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "session/set_mode",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"modeId": "code"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="sessionId" type="SessionId" required>
|
||||
The ID of the session to set the mode for
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="modeId" type="SessionModeId" required>
|
||||
The ID of the mode to switch to. Must be one of the modes listed in
|
||||
`availableModes`
|
||||
</ParamField>
|
||||
|
||||
### From the Agent
|
||||
|
||||
The Agent can also change its own mode and let the Client know by sending the `current_mode_update` session notification:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "current_mode_update",
|
||||
"modeId": "code"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Exiting plan modes
|
||||
|
||||
A common case where an Agent might switch modes is from within a special "exit mode" tool that can be provided to the language model during plan/architect modes. The language model can call this tool when it determines it's ready to start implementing a solution.
|
||||
|
||||
This "switch mode" tool will usually request permission before running, which it can do just like any other tool:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "session/request_permission",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"toolCall": {
|
||||
"toolCallId": "call_switch_mode_001",
|
||||
"title": "Ready for implementation",
|
||||
"kind": "switch_mode",
|
||||
"status": "pending",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "## Implementation Plan..."
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"optionId": "code",
|
||||
"name": "Yes, and auto-accept all actions",
|
||||
"kind": "allow_always"
|
||||
},
|
||||
{
|
||||
"optionId": "ask",
|
||||
"name": "Yes, and manually accept actions",
|
||||
"kind": "allow_once"
|
||||
},
|
||||
{
|
||||
"optionId": "reject",
|
||||
"name": "No, stay in architect mode",
|
||||
"kind": "reject_once"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When an option is chosen, the tool runs, setting the mode and sending the `current_mode_update` notification mentioned above.
|
||||
|
||||
<Card icon="shield-check" horizontal href="./tool-calls#requesting-permission">
|
||||
Learn more about permission requests
|
||||
</Card>
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
# Session Setup
|
||||
|
||||
> Creating and loading sessions
|
||||
|
||||
Sessions represent a specific conversation or thread between the [Client](./overview#client) and [Agent](./overview#agent). Each session maintains its own context, conversation history, and state, allowing multiple independent interactions with the same Agent.
|
||||
|
||||
Before creating a session, Clients **MUST** first complete the [initialization](./initialization) phase to establish protocol compatibility and capabilities.
|
||||
|
||||
<br />
|
||||
|
||||
```mermaid theme={null}
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Agent
|
||||
|
||||
Note over Agent,Client: Initialized
|
||||
|
||||
alt
|
||||
Client->>Agent: session/new
|
||||
Note over Agent: Create session context
|
||||
Note over Agent: Connect to MCP servers
|
||||
Agent-->>Client: session/new response (sessionId)
|
||||
else
|
||||
Client->>Agent: session/load (sessionId)
|
||||
Note over Agent: Restore session context
|
||||
Note over Agent: Connect to MCP servers
|
||||
Note over Agent,Client: Replay conversation history...
|
||||
Agent->>Client: session/update
|
||||
Agent->>Client: session/update
|
||||
Note over Agent,Client: All content streamed
|
||||
Agent-->>Client: session/load response
|
||||
end
|
||||
|
||||
Note over Client,Agent: Ready for prompts
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Creating a Session
|
||||
|
||||
Clients create a new session by calling the `session/new` method with:
|
||||
|
||||
- The [working directory](#working-directory) for the session
|
||||
- A list of [MCP servers](#mcp-servers) the Agent should connect to
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "session/new",
|
||||
"params": {
|
||||
"cwd": "/home/user/project",
|
||||
"mcpServers": [
|
||||
{
|
||||
"name": "filesystem",
|
||||
"command": "/path/to/mcp-server",
|
||||
"args": ["--stdio"],
|
||||
"env": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Agent **MUST** respond with a unique [Session ID](#session-id) that identifies this conversation:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"sessionId": "sess_abc123def456"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Loading Sessions
|
||||
|
||||
Agents that support the `loadSession` capability allow Clients to resume previous conversations. This feature enables persistence across restarts and sharing sessions between different Client instances.
|
||||
|
||||
### Checking Support
|
||||
|
||||
Before attempting to load a session, Clients **MUST** verify that the Agent supports this capability by checking the `loadSession` field in the `initialize` response:
|
||||
|
||||
```json highlight={7} theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"agentCapabilities": {
|
||||
"loadSession": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `loadSession` is `false` or not present, the Agent does not support loading sessions and Clients **MUST NOT** attempt to call `session/load`.
|
||||
|
||||
### Loading a Session
|
||||
|
||||
To load an existing session, Clients **MUST** call the `session/load` method with:
|
||||
|
||||
- The [Session ID](#session-id) to resume
|
||||
- [MCP servers](#mcp-servers) to connect to
|
||||
- The [working directory](#working-directory)
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "session/load",
|
||||
"params": {
|
||||
"sessionId": "sess_789xyz",
|
||||
"cwd": "/home/user/project",
|
||||
"mcpServers": [
|
||||
{
|
||||
"name": "filesystem",
|
||||
"command": "/path/to/mcp-server",
|
||||
"args": ["--mode", "filesystem"],
|
||||
"env": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Agent **MUST** replay the entire conversation to the Client in the form of `session/update` notifications (like `session/prompt`).
|
||||
|
||||
For example, a user message from the conversation history:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_789xyz",
|
||||
"update": {
|
||||
"sessionUpdate": "user_message_chunk",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": "What's the capital of France?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Followed by the agent's response:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_789xyz",
|
||||
"update": {
|
||||
"sessionUpdate": "agent_message_chunk",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": "The capital of France is Paris."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When **all** the conversation entries have been streamed to the Client, the Agent **MUST** respond to the original `session/load` request.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
The Client can then continue sending prompts as if the session was never interrupted.
|
||||
|
||||
## Session ID
|
||||
|
||||
The session ID returned by `session/new` is a unique identifier for the conversation context.
|
||||
|
||||
Clients use this ID to:
|
||||
|
||||
- Send prompt requests via `session/prompt`
|
||||
- Cancel ongoing operations via `session/cancel`
|
||||
- Load previous sessions via `session/load` (if the Agent supports the `loadSession` capability)
|
||||
|
||||
## Working Directory
|
||||
|
||||
The `cwd` (current working directory) parameter establishes the file system context for the session. This directory:
|
||||
|
||||
- **MUST** be an absolute path
|
||||
- **MUST** be used for the session regardless of where the Agent subprocess was spawned
|
||||
- **SHOULD** serve as a boundary for tool operations on the file system
|
||||
|
||||
## MCP Servers
|
||||
|
||||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) allows Agents to access external tools and data sources. When creating a session, Clients **MAY** include connection details for MCP servers that the Agent should connect to.
|
||||
|
||||
MCP servers can be connected to using different transports. All Agents **MUST** support the stdio transport, while HTTP and SSE transports are optional capabilities that can be checked during initialization.
|
||||
|
||||
While they are not required to by the spec, new Agents **SHOULD** support the HTTP transport to ensure compatibility with modern MCP servers.
|
||||
|
||||
### Transport Types
|
||||
|
||||
#### Stdio Transport
|
||||
|
||||
All Agents **MUST** support connecting to MCP servers via stdio (standard input/output). This is the default transport mechanism.
|
||||
|
||||
<ParamField path="name" type="string" required>
|
||||
A human-readable identifier for the server
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="command" type="string" required>
|
||||
The absolute path to the MCP server executable
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="args" type="array" required>
|
||||
Command-line arguments to pass to the server
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="env" type="EnvVariable[]">
|
||||
Environment variables to set when launching the server
|
||||
|
||||
<Expandable title="EnvVariable">
|
||||
<ParamField path="name" type="string">
|
||||
The name of the environment variable.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="value" type="string">
|
||||
The value of the environment variable.
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
|
||||
Example stdio transport configuration:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"name": "filesystem",
|
||||
"command": "/path/to/mcp-server",
|
||||
"args": ["--stdio"],
|
||||
"env": [
|
||||
{
|
||||
"name": "API_KEY",
|
||||
"value": "secret123"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### HTTP Transport
|
||||
|
||||
When the Agent supports `mcpCapabilities.http`, Clients can specify MCP servers configurations using the HTTP transport.
|
||||
|
||||
<ParamField path="type" type="string" required>
|
||||
Must be `"http"` to indicate HTTP transport
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="name" type="string" required>
|
||||
A human-readable identifier for the server
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="url" type="string" required>
|
||||
The URL of the MCP server
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="headers" type="HttpHeader[]" required>
|
||||
HTTP headers to include in requests to the server
|
||||
|
||||
<Expandable title="HttpHeader">
|
||||
<ParamField path="name" type="string">
|
||||
The name of the HTTP header.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="value" type="string">
|
||||
The value to set for the HTTP header.
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
|
||||
Example HTTP transport configuration:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "http",
|
||||
"name": "api-server",
|
||||
"url": "https://api.example.com/mcp",
|
||||
"headers": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer token123"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### SSE Transport
|
||||
|
||||
When the Agent supports `mcpCapabilities.sse`, Clients can specify MCP servers configurations using the SSE transport.
|
||||
|
||||
<Warning>This transport was deprecated by the MCP spec.</Warning>
|
||||
|
||||
<ParamField path="type" type="string" required>
|
||||
Must be `"sse"` to indicate SSE transport
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="name" type="string" required>
|
||||
A human-readable identifier for the server
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="url" type="string" required>
|
||||
The URL of the SSE endpoint
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="headers" type="HttpHeader[]" required>
|
||||
HTTP headers to include when establishing the SSE connection
|
||||
|
||||
<Expandable title="HttpHeader">
|
||||
<ParamField path="name" type="string">
|
||||
The name of the HTTP header.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="value" type="string">
|
||||
The value to set for the HTTP header.
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
|
||||
Example SSE transport configuration:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "sse",
|
||||
"name": "event-stream",
|
||||
"url": "https://events.example.com/mcp",
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-API-Key",
|
||||
"value": "apikey456"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Transport Support
|
||||
|
||||
Before using HTTP or SSE transports, Clients **MUST** verify the Agent's capabilities during initialization:
|
||||
|
||||
```json highlight={7-10} theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"agentCapabilities": {
|
||||
"mcpCapabilities": {
|
||||
"http": true,
|
||||
"sse": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `mcpCapabilities.http` is `false` or not present, the Agent does not support HTTP transport.
|
||||
If `mcpCapabilities.sse` is `false` or not present, the Agent does not support SSE transport.
|
||||
|
||||
Agents **SHOULD** connect to all MCP servers specified by the Client.
|
||||
|
||||
Clients **MAY** use this ability to provide tools directly to the underlying language model by including their own MCP server.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
# Slash Commands
|
||||
|
||||
> Advertise available slash commands to clients
|
||||
|
||||
Agents can advertise a set of slash commands that users can invoke. These commands provide quick access to specific agent capabilities and workflows. Commands are run as part of regular [prompt](./prompt-turn) requests where the Client includes the command text in the prompt.
|
||||
|
||||
## Advertising commands
|
||||
|
||||
After creating a session, the Agent **MAY** send a list of available commands via the `available_commands_update` session notification:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "available_commands_update",
|
||||
"availableCommands": [
|
||||
{
|
||||
"name": "web",
|
||||
"description": "Search the web for information",
|
||||
"input": {
|
||||
"hint": "query to search for"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "test",
|
||||
"description": "Run tests for the current project"
|
||||
},
|
||||
{
|
||||
"name": "plan",
|
||||
"description": "Create a detailed implementation plan",
|
||||
"input": {
|
||||
"hint": "description of what to plan"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ResponseField name="availableCommands" type="AvailableCommand[]">
|
||||
The list of commands available in this session
|
||||
</ResponseField>
|
||||
|
||||
### AvailableCommand
|
||||
|
||||
<ResponseField name="name" type="string" required>
|
||||
The command name (e.g., "web", "test", "plan")
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="description" type="string" required>
|
||||
Human-readable description of what the command does
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="input" type="AvailableCommandInput">
|
||||
Optional input specification for the command
|
||||
</ResponseField>
|
||||
|
||||
### AvailableCommandInput
|
||||
|
||||
Currently supports unstructured text input:
|
||||
|
||||
<ResponseField name="hint" type="string" required>
|
||||
A hint to display when the input hasn't been provided yet
|
||||
</ResponseField>
|
||||
|
||||
## Dynamic updates
|
||||
|
||||
The Agent can update the list of available commands at any time during a session by sending another `available_commands_update` notification. This allows commands to be added based on context, removed when no longer relevant, or modified with updated descriptions.
|
||||
|
||||
## Running commands
|
||||
|
||||
Commands are included as regular user messages in prompt requests:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "session/prompt",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"prompt": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "/web agent client protocol"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Agent recognizes the command prefix and processes it accordingly. Commands may be accompanied by any other user message content types (images, audio, etc.) in the same prompt array.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
# Terminals
|
||||
|
||||
> Executing and managing terminal commands
|
||||
|
||||
The terminal methods allow Agents to execute shell commands within the Client's environment. These methods enable Agents to run build processes, execute scripts, and interact with command-line tools while providing real-time output streaming and process control.
|
||||
|
||||
## Checking Support
|
||||
|
||||
Before attempting to use terminal methods, Agents **MUST** verify that the Client supports this capability by checking the [Client Capabilities](./initialization#client-capabilities) field in the `initialize` response:
|
||||
|
||||
```json highlight={7} theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"clientCapabilities": {
|
||||
"terminal": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `terminal` is `false` or not present, the Agent **MUST NOT** attempt to call any terminal methods.
|
||||
|
||||
## Executing Commands
|
||||
|
||||
The `terminal/create` method starts a command in a new terminal:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "terminal/create",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"command": "npm",
|
||||
"args": ["test", "--coverage"],
|
||||
"env": [
|
||||
{
|
||||
"name": "NODE_ENV",
|
||||
"value": "test"
|
||||
}
|
||||
],
|
||||
"cwd": "/home/user/project",
|
||||
"outputByteLimit": 1048576
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="sessionId" type="SessionId" required>
|
||||
The [Session ID](./session-setup#session-id) for this request
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="command" type="string" required>
|
||||
The command to execute
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="args" type="string[]">
|
||||
Array of command arguments
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="env" type="EnvVariable[]">
|
||||
Environment variables for the command.
|
||||
|
||||
Each variable has:
|
||||
|
||||
- `name`: The environment variable name
|
||||
- `value`: The environment variable value
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="cwd" type="string">
|
||||
Working directory for the command (absolute path)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="outputByteLimit" type="number">
|
||||
Maximum number of output bytes to retain. Once exceeded, earlier output is
|
||||
truncated to stay within this limit.
|
||||
|
||||
When the limit is exceeded, the Client truncates from the beginning of the output
|
||||
to stay within the limit.
|
||||
|
||||
The Client **MUST** ensure truncation happens at a character boundary to maintain valid
|
||||
string output, even if this means the retained output is slightly less than the
|
||||
specified limit.
|
||||
</ParamField>
|
||||
|
||||
The Client returns a Terminal ID immediately without waiting for completion:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This allows the command to run in the background while the Agent performs other operations.
|
||||
|
||||
After creating the terminal, the Agent can use the `terminal/wait_for_exit` method to wait for the command to complete.
|
||||
|
||||
<Note>
|
||||
The Agent **MUST** release the terminal using `terminal/release` when it's no
|
||||
longer needed.
|
||||
</Note>
|
||||
|
||||
## Embedding in Tool Calls
|
||||
|
||||
Terminals can be embedded directly in [tool calls](./tool-calls) to provide real-time output to users:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "tool_call",
|
||||
"toolCallId": "call_002",
|
||||
"title": "Running tests",
|
||||
"kind": "execute",
|
||||
"status": "in_progress",
|
||||
"content": [
|
||||
{
|
||||
"type": "terminal",
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When a terminal is embedded in a tool call, the Client displays live output as it's generated and continues to display it even after the terminal is released.
|
||||
|
||||
## Getting Output
|
||||
|
||||
The `terminal/output` method retrieves the current terminal output without waiting for the command to complete:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"method": "terminal/output",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Client responds with the current output and exit status (if the command has finished):
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"result": {
|
||||
"output": "Running tests...\n✓ All tests passed (42 total)\n",
|
||||
"truncated": false,
|
||||
"exitStatus": {
|
||||
"exitCode": 0,
|
||||
"signal": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ResponseField name="output" type="string" required>
|
||||
The terminal output captured so far
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="truncated" type="boolean" required>
|
||||
Whether the output was truncated due to byte limits
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="exitStatus" type="TerminalExitStatus">
|
||||
Present only if the command has exited. Contains:
|
||||
|
||||
- `exitCode`: The process exit code (may be null)
|
||||
- `signal`: The signal that terminated the process (may be null)
|
||||
</ResponseField>
|
||||
|
||||
## Waiting for Exit
|
||||
|
||||
The `terminal/wait_for_exit` method returns once the command completes:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"method": "terminal/wait_for_exit",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Client responds once the command exits:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"result": {
|
||||
"exitCode": 0,
|
||||
"signal": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ResponseField name="exitCode" type="number">
|
||||
The process exit code (may be null if terminated by signal)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="signal" type="string">
|
||||
The signal that terminated the process (may be null if exited normally)
|
||||
</ResponseField>
|
||||
|
||||
## Killing Commands
|
||||
|
||||
The `terminal/kill` method terminates a command without releasing the terminal:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"method": "terminal/kill",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After killing a command, the terminal remains valid and can be used with:
|
||||
|
||||
- `terminal/output` to get the final output
|
||||
- `terminal/wait_for_exit` to get the exit status
|
||||
|
||||
The Agent **MUST** still call `terminal/release` when it's done using it.
|
||||
|
||||
### Building a Timeout
|
||||
|
||||
Agents can implement command timeouts by combining terminal methods:
|
||||
|
||||
1. Create a terminal with `terminal/create`
|
||||
2. Start a timer for the desired timeout duration
|
||||
3. Concurrently wait for either the timer to expire or `terminal/wait_for_exit` to return
|
||||
4. If the timer expires first:
|
||||
- Call `terminal/kill` to terminate the command
|
||||
- Call `terminal/output` to retrieve any final output
|
||||
- Include the output in the response to the model
|
||||
5. Call `terminal/release` when done
|
||||
|
||||
## Releasing Terminals
|
||||
|
||||
The `terminal/release` kills the command if still running and releases all resources:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 9,
|
||||
"method": "terminal/release",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After release the terminal ID becomes invalid for all other `terminal/*` methods.
|
||||
|
||||
If the terminal was added to a tool call, the client **SHOULD** continue to display its output after release.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
# Tool Calls
|
||||
|
||||
> How Agents report tool call execution
|
||||
|
||||
Tool calls represent actions that language models request Agents to perform during a [prompt turn](./prompt-turn). When an LLM determines it needs to interact with external systems—like reading files, running code, or fetching data—it generates tool calls that the Agent executes on its behalf.
|
||||
|
||||
Agents report tool calls through [`session/update`](./prompt-turn#3-agent-reports-output) notifications, allowing Clients to display real-time progress and results to users.
|
||||
|
||||
While Agents handle the actual execution, they may leverage Client capabilities like [permission requests](#requesting-permission) or [file system access](./file-system) to provide a richer, more integrated experience.
|
||||
|
||||
## Creating
|
||||
|
||||
When the language model requests a tool invocation, the Agent **SHOULD** report it to the Client:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "tool_call",
|
||||
"toolCallId": "call_001",
|
||||
"title": "Reading configuration file",
|
||||
"kind": "read",
|
||||
"status": "pending"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="toolCallId" type="ToolCallId" required>
|
||||
A unique identifier for this tool call within the session
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="title" type="string" required>
|
||||
A human-readable title describing what the tool is doing
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="kind" type="ToolKind">
|
||||
The category of tool being invoked.
|
||||
|
||||
<Expandable title="kinds">
|
||||
* `read` - Reading files or data - `edit` - Modifying files or content -
|
||||
`delete` - Removing files or data - `move` - Moving or renaming files -
|
||||
`search` - Searching for information - `execute` - Running commands or code -
|
||||
`think` - Internal reasoning or planning - `fetch` - Retrieving external data
|
||||
* `other` - Other tool types (default)
|
||||
</Expandable>
|
||||
|
||||
Tool kinds help Clients choose appropriate icons and optimize how they display tool execution progress.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="status" type="ToolCallStatus">
|
||||
The current [execution status](#status) (defaults to `pending`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="content" type="ToolCallContent[]">
|
||||
[Content produced](#content) by the tool call
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="locations" type="ToolCallLocation[]">
|
||||
[File locations](#following-the-agent) affected by this tool call
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="rawInput" type="object">
|
||||
The raw input parameters sent to the tool
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="rawOutput" type="object">
|
||||
The raw output returned by the tool
|
||||
</ParamField>
|
||||
|
||||
## Updating
|
||||
|
||||
As tools execute, Agents send updates to report progress and results.
|
||||
|
||||
Updates use the `session/update` notification with `tool_call_update`:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "session/update",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"update": {
|
||||
"sessionUpdate": "tool_call_update",
|
||||
"toolCallId": "call_001",
|
||||
"status": "in_progress",
|
||||
"content": [
|
||||
{
|
||||
"type": "content",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": "Found 3 configuration files..."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All fields except `toolCallId` are optional in updates. Only the fields being changed need to be included.
|
||||
|
||||
## Requesting Permission
|
||||
|
||||
The Agent **MAY** request permission from the user before executing a tool call by calling the `session/request_permission` method:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "session/request_permission",
|
||||
"params": {
|
||||
"sessionId": "sess_abc123def456",
|
||||
"toolCall": {
|
||||
"toolCallId": "call_001"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"optionId": "allow-once",
|
||||
"name": "Allow once",
|
||||
"kind": "allow_once"
|
||||
},
|
||||
{
|
||||
"optionId": "reject-once",
|
||||
"name": "Reject",
|
||||
"kind": "reject_once"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="sessionId" type="SessionId" required>
|
||||
The session ID for this request
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="toolCall" type="ToolCallUpdate" required>
|
||||
The tool call update containing details about the operation
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="options" type="PermissionOption[]" required>
|
||||
Available [permission options](#permission-options) for the user to choose
|
||||
from
|
||||
</ParamField>
|
||||
|
||||
The Client responds with the user's decision:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"outcome": {
|
||||
"outcome": "selected",
|
||||
"optionId": "allow-once"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clients **MAY** automatically allow or reject permission requests according to the user settings.
|
||||
|
||||
If the current prompt turn gets [cancelled](./prompt-turn#cancellation), the Client **MUST** respond with the `"cancelled"` outcome:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"outcome": {
|
||||
"outcome": "cancelled"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<ResponseField name="outcome" type="RequestPermissionOutcome" required>
|
||||
The user's decision, either: - `cancelled` - The [prompt turn was
|
||||
cancelled](./prompt-turn#cancellation) - `selected` with an `optionId` - The
|
||||
ID of the selected permission option
|
||||
</ResponseField>
|
||||
|
||||
### Permission Options
|
||||
|
||||
Each permission option provided to the Client contains:
|
||||
|
||||
<ParamField path="optionId" type="string" required>
|
||||
Unique identifier for this option
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="name" type="string" required>
|
||||
Human-readable label to display to the user
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="kind" type="PermissionOptionKind" required>
|
||||
A hint to help Clients choose appropriate icons and UI treatment for each option.
|
||||
|
||||
- `allow_once` - Allow this operation only this time
|
||||
- `allow_always` - Allow this operation and remember the choice
|
||||
- `reject_once` - Reject this operation only this time
|
||||
- `reject_always` - Reject this operation and remember the choice
|
||||
</ParamField>
|
||||
|
||||
## Status
|
||||
|
||||
Tool calls progress through different statuses during their lifecycle:
|
||||
|
||||
<ResponseField name="pending">
|
||||
The tool call hasn't started running yet because the input is either streaming
|
||||
or awaiting approval
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="in_progress">
|
||||
The tool call is currently running
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="completed">
|
||||
The tool call completed successfully
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="failed">The tool call failed with an error</ResponseField>
|
||||
|
||||
## Content
|
||||
|
||||
Tool calls can produce different types of content:
|
||||
|
||||
### Regular Content
|
||||
|
||||
Standard [content blocks](./content) like text, images, or resources:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "content",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": "Analysis complete. Found 3 issues."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Diffs
|
||||
|
||||
File modifications shown as diffs:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "diff",
|
||||
"path": "/home/user/project/src/config.json",
|
||||
"oldText": "{\n \"debug\": false\n}",
|
||||
"newText": "{\n \"debug\": true\n}"
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="path" type="string" required>
|
||||
The absolute file path being modified
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="oldText" type="string">
|
||||
The original content (null for new files)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="newText" type="string" required>
|
||||
The new content after modification
|
||||
</ParamField>
|
||||
|
||||
### Terminals
|
||||
|
||||
Live terminal output from command execution:
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"type": "terminal",
|
||||
"terminalId": "term_xyz789"
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="terminalId" type="string" required>
|
||||
The ID of a terminal created with `terminal/create`
|
||||
</ParamField>
|
||||
|
||||
When a terminal is embedded in a tool call, the Client displays live output as it's generated and continues to display it even after the terminal is released.
|
||||
|
||||
<Card icon="terminal" horizontal href="./terminals">
|
||||
Learn more about Terminals
|
||||
</Card>
|
||||
|
||||
## Following the Agent
|
||||
|
||||
Tool calls can report file locations they're working with, enabling Clients to implement "follow-along" features that track which files the Agent is accessing or modifying in real-time.
|
||||
|
||||
```json theme={null}
|
||||
{
|
||||
"path": "/home/user/project/src/main.py",
|
||||
"line": 42
|
||||
}
|
||||
```
|
||||
|
||||
<ParamField path="path" type="string" required>
|
||||
The absolute file path being accessed or modified
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="line" type="number">
|
||||
Optional line number within the file
|
||||
</ParamField>
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
# Transports
|
||||
|
||||
> Mechanisms for agents and clients to communicate with each other
|
||||
|
||||
ACP uses JSON-RPC to encode messages. JSON-RPC messages **MUST** be UTF-8 encoded.
|
||||
|
||||
The protocol currently defines the following transport mechanisms for agent-client communication:
|
||||
|
||||
1. [stdio](#stdio), communication over standard in and standard out
|
||||
2. _[Streamable HTTP](#streamable-http) (draft proposal in progress)_
|
||||
|
||||
Agents and clients **SHOULD** support stdio whenever possible.
|
||||
|
||||
It is also possible for agents and clients to implement [custom transports](#custom-transports).
|
||||
|
||||
## stdio
|
||||
|
||||
In the **stdio** transport:
|
||||
|
||||
- The client launches the agent as a subprocess.
|
||||
- The agent reads JSON-RPC messages from its standard input (`stdin`) and sends messages to its standard output (`stdout`).
|
||||
- Messages are individual JSON-RPC requests, notifications, or responses.
|
||||
- Messages are delimited by newlines (`\n`), and **MUST NOT** contain embedded newlines.
|
||||
- The agent **MAY** write UTF-8 strings to its standard error (`stderr`) for logging purposes. Clients **MAY** capture, forward, or ignore this logging.
|
||||
- The agent **MUST NOT** write anything to its `stdout` that is not a valid ACP message.
|
||||
- The client **MUST NOT** write anything to the agent's `stdin` that is not a valid ACP message.
|
||||
|
||||
```mermaid theme={null}
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Agent Process
|
||||
|
||||
Client->>+Agent Process: Launch subprocess
|
||||
loop Message Exchange
|
||||
Client->>Agent Process: Write to stdin
|
||||
Agent Process->>Client: Write to stdout
|
||||
Agent Process--)Client: Optional logs on stderr
|
||||
end
|
||||
Client->>Agent Process: Close stdin, terminate subprocess
|
||||
deactivate Agent Process
|
||||
```
|
||||
|
||||
## _Streamable HTTP_
|
||||
|
||||
_In discussion, draft proposal in progress._
|
||||
|
||||
## Custom Transports
|
||||
|
||||
Agents and clients **MAY** implement additional custom transport mechanisms to suit their specific needs. The protocol is transport-agnostic and can be implemented over any communication channel that supports bidirectional message exchange.
|
||||
|
||||
Implementers who choose to support custom transports **MUST** ensure they preserve the JSON-RPC message format and lifecycle requirements defined by ACP. Custom transports **SHOULD** document their specific connection establishment and message exchange patterns to aid interoperability.
|
||||
|
||||
---
|
||||
|
||||
> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://agentclientprotocol.com/llms.txt
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
/**
|
||||
* ACP File System Service
|
||||
*
|
||||
* Delegates file system operations to the ACP client when supported.
|
||||
* Falls back to direct file system operations when the client doesn't
|
||||
* support the required capabilities.
|
||||
*/
|
||||
|
||||
import * as acp from "@agentclientprotocol/sdk"
|
||||
import * as fs from "node:fs/promises"
|
||||
import * as path from "node:path"
|
||||
|
||||
// =============================================================================
|
||||
// AcpFileSystemService Class
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* AcpFileSystemService provides file system operations that can be delegated
|
||||
* to the ACP client or performed locally.
|
||||
*
|
||||
* This allows the ACP client (like Zed) to handle file operations within
|
||||
* its own context, providing proper integration with the editor's file system,
|
||||
* undo stack, and other features.
|
||||
*/
|
||||
export class AcpFileSystemService {
|
||||
constructor(
|
||||
private readonly connection: acp.AgentSideConnection,
|
||||
private readonly sessionId: string,
|
||||
private readonly capabilities: acp.FileSystemCapability | undefined,
|
||||
private readonly workspacePath: string,
|
||||
) {}
|
||||
|
||||
// ===========================================================================
|
||||
// Read Operations
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Read text content from a file.
|
||||
*
|
||||
* If the ACP client supports readTextFile, delegates to the client.
|
||||
* Otherwise, reads directly from the file system.
|
||||
*/
|
||||
async readTextFile(filePath: string): Promise<string> {
|
||||
// Resolve path relative to workspace
|
||||
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(this.workspacePath, filePath)
|
||||
|
||||
// Use client capability if available
|
||||
if (this.capabilities?.readTextFile) {
|
||||
try {
|
||||
const response = await this.connection.readTextFile({
|
||||
path: absolutePath,
|
||||
sessionId: this.sessionId,
|
||||
})
|
||||
return response.content
|
||||
} catch (error) {
|
||||
// Fall back to direct read on error
|
||||
console.warn("[AcpFileSystemService] Client read failed, falling back to direct read:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// Direct file system read
|
||||
return fs.readFile(absolutePath, "utf-8")
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Write Operations
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Write text content to a file.
|
||||
*
|
||||
* If the ACP client supports writeTextFile, delegates to the client.
|
||||
* Otherwise, writes directly to the file system.
|
||||
*/
|
||||
async writeTextFile(filePath: string, content: string): Promise<void> {
|
||||
// Resolve path relative to workspace
|
||||
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(this.workspacePath, filePath)
|
||||
|
||||
// Use client capability if available
|
||||
if (this.capabilities?.writeTextFile) {
|
||||
try {
|
||||
await this.connection.writeTextFile({
|
||||
path: absolutePath,
|
||||
content,
|
||||
sessionId: this.sessionId,
|
||||
})
|
||||
return
|
||||
} catch (error) {
|
||||
// Fall back to direct write on error
|
||||
console.warn("[AcpFileSystemService] Client write failed, falling back to direct write:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
const dir = path.dirname(absolutePath)
|
||||
await fs.mkdir(dir, { recursive: true })
|
||||
|
||||
// Direct file system write
|
||||
await fs.writeFile(absolutePath, content, "utf-8")
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Capability Checks
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Check if the client supports reading files.
|
||||
*/
|
||||
canReadTextFile(): boolean {
|
||||
return this.capabilities?.readTextFile === true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the client supports writing files.
|
||||
*/
|
||||
canWriteTextFile(): boolean {
|
||||
return this.capabilities?.writeTextFile === true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any client file system capabilities are available.
|
||||
*/
|
||||
hasClientCapabilities(): boolean {
|
||||
return this.canReadTextFile() || this.canWriteTextFile()
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Factory Function
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Create an AcpFileSystemService if the client has file system capabilities.
|
||||
*/
|
||||
export function createAcpFileSystemService(
|
||||
connection: acp.AgentSideConnection,
|
||||
sessionId: string,
|
||||
clientCapabilities: acp.ClientCapabilities | undefined,
|
||||
workspacePath: string,
|
||||
): AcpFileSystemService | null {
|
||||
const fsCapabilities = clientCapabilities?.fs
|
||||
|
||||
if (!fsCapabilities) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new AcpFileSystemService(connection, sessionId, fsCapabilities, workspacePath)
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
* - RooCodeAgent: Implements the acp.Agent interface
|
||||
* - AcpSession: Wraps ExtensionHost for individual sessions
|
||||
* - Translator: Converts between internal and ACP message formats
|
||||
* - AcpFileSystemService: Delegates file operations to ACP client
|
||||
* - UpdateBuffer: Batches session updates to reduce message frequency
|
||||
* - acpLog: File-based logger for debugging (writes to ~/.roo/acp.log)
|
||||
*
|
||||
|
|
@ -18,7 +17,6 @@
|
|||
|
||||
export { RooCodeAgent, type RooCodeAgentOptions } from "./agent.js"
|
||||
export { AcpSession, type AcpSessionOptions } from "./session.js"
|
||||
export { AcpFileSystemService, createAcpFileSystemService } from "./file-system-service.js"
|
||||
export { UpdateBuffer, type UpdateBufferOptions } from "./update-buffer.js"
|
||||
export { acpLog } from "./logger.js"
|
||||
export * from "./translator.js"
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
/**
|
||||
* ACP Terminal Manager
|
||||
*
|
||||
* Manages ACP terminals for command execution. When the client supports terminals,
|
||||
* this manager handles creating, tracking, and releasing terminals according to
|
||||
* the ACP protocol specification.
|
||||
*/
|
||||
|
||||
import * as acp from "@agentclientprotocol/sdk"
|
||||
|
||||
import { acpLog } from "./logger.js"
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Information about an active terminal.
|
||||
*/
|
||||
export interface ActiveTerminal {
|
||||
/** The terminal handle from ACP SDK */
|
||||
handle: acp.TerminalHandle
|
||||
/** The command being executed */
|
||||
command: string
|
||||
/** Working directory for the command */
|
||||
cwd?: string
|
||||
/** Timestamp when the terminal was created */
|
||||
createdAt: number
|
||||
/** Associated tool call ID (for embedding in tool calls) */
|
||||
toolCallId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsed command information extracted from a Roo Code command message.
|
||||
*/
|
||||
export interface ParsedCommand {
|
||||
/** The full command string (may include shell operators) */
|
||||
fullCommand: string
|
||||
/** The executable/command name */
|
||||
executable: string
|
||||
/** Command arguments */
|
||||
args: string[]
|
||||
/** Working directory (if specified) */
|
||||
cwd?: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Terminal Manager
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Manages ACP terminals for command execution.
|
||||
*
|
||||
* This class handles the lifecycle of ACP terminals:
|
||||
* 1. Creating terminals via terminal/create
|
||||
* 2. Tracking active terminals
|
||||
* 3. Releasing terminals when done
|
||||
*
|
||||
* According to the ACP spec, terminals should be:
|
||||
* - Created with terminal/create
|
||||
* - Embedded in tool calls using { type: "terminal", terminalId }
|
||||
* - Released with terminal/release when done
|
||||
*/
|
||||
export class TerminalManager {
|
||||
/** Map of terminal IDs to active terminal info */
|
||||
private terminals: Map<string, ActiveTerminal> = new Map()
|
||||
|
||||
constructor(
|
||||
private readonly sessionId: string,
|
||||
private readonly connection: acp.AgentSideConnection,
|
||||
) {}
|
||||
|
||||
// ===========================================================================
|
||||
// Terminal Lifecycle
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Create a new terminal and execute a command.
|
||||
*
|
||||
* @param command - The command to execute
|
||||
* @param cwd - Working directory for the command
|
||||
* @param toolCallId - Optional tool call ID for embedding
|
||||
* @returns The terminal handle and ID
|
||||
*/
|
||||
async createTerminal(
|
||||
command: string,
|
||||
cwd: string,
|
||||
toolCallId?: string,
|
||||
): Promise<{ handle: acp.TerminalHandle; terminalId: string }> {
|
||||
acpLog.debug("TerminalManager", `Creating terminal for command: ${command}`)
|
||||
|
||||
const parsed = this.parseCommand(command)
|
||||
|
||||
try {
|
||||
const handle = await this.connection.createTerminal({
|
||||
sessionId: this.sessionId,
|
||||
command: parsed.executable,
|
||||
args: parsed.args,
|
||||
cwd: parsed.cwd || cwd,
|
||||
})
|
||||
|
||||
const terminalId = handle.id
|
||||
acpLog.info("TerminalManager", `Terminal created: ${terminalId}`)
|
||||
|
||||
// Track the terminal
|
||||
this.terminals.set(terminalId, {
|
||||
handle,
|
||||
command,
|
||||
cwd: parsed.cwd || cwd,
|
||||
createdAt: Date.now(),
|
||||
toolCallId,
|
||||
})
|
||||
|
||||
return { handle, terminalId }
|
||||
} catch (error) {
|
||||
acpLog.error("TerminalManager", `Failed to create terminal: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get terminal output without waiting for exit.
|
||||
*/
|
||||
async getOutput(terminalId: string): Promise<acp.TerminalOutputResponse | null> {
|
||||
const terminal = this.terminals.get(terminalId)
|
||||
if (!terminal) {
|
||||
acpLog.warn("TerminalManager", `Terminal not found: ${terminalId}`)
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return await terminal.handle.currentOutput()
|
||||
} catch (error) {
|
||||
acpLog.error("TerminalManager", `Failed to get output for ${terminalId}: ${error}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a terminal to exit and return the result.
|
||||
*/
|
||||
async waitForExit(
|
||||
terminalId: string,
|
||||
): Promise<{ exitCode: number | null; signal: string | null; output: string } | null> {
|
||||
const terminal = this.terminals.get(terminalId)
|
||||
if (!terminal) {
|
||||
acpLog.warn("TerminalManager", `Terminal not found: ${terminalId}`)
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
acpLog.debug("TerminalManager", `Waiting for exit: ${terminalId}`)
|
||||
|
||||
// Wait for the command to complete
|
||||
const exitStatus = await terminal.handle.waitForExit()
|
||||
|
||||
// Get the final output
|
||||
const outputResponse = await terminal.handle.currentOutput()
|
||||
|
||||
acpLog.info("TerminalManager", `Terminal ${terminalId} exited: code=${exitStatus.exitCode}`)
|
||||
|
||||
return {
|
||||
exitCode: exitStatus.exitCode ?? null,
|
||||
signal: exitStatus.signal ?? null,
|
||||
output: outputResponse.output,
|
||||
}
|
||||
} catch (error) {
|
||||
acpLog.error("TerminalManager", `Failed to wait for ${terminalId}: ${error}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill a running terminal command.
|
||||
*/
|
||||
async killTerminal(terminalId: string): Promise<boolean> {
|
||||
const terminal = this.terminals.get(terminalId)
|
||||
if (!terminal) {
|
||||
acpLog.warn("TerminalManager", `Terminal not found: ${terminalId}`)
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
await terminal.handle.kill()
|
||||
acpLog.info("TerminalManager", `Terminal killed: ${terminalId}`)
|
||||
return true
|
||||
} catch (error) {
|
||||
acpLog.error("TerminalManager", `Failed to kill ${terminalId}: ${error}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a terminal and free its resources.
|
||||
* This MUST be called when done with a terminal.
|
||||
*/
|
||||
async releaseTerminal(terminalId: string): Promise<boolean> {
|
||||
const terminal = this.terminals.get(terminalId)
|
||||
if (!terminal) {
|
||||
acpLog.warn("TerminalManager", `Terminal not found: ${terminalId}`)
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
await terminal.handle.release()
|
||||
this.terminals.delete(terminalId)
|
||||
acpLog.info("TerminalManager", `Terminal released: ${terminalId}`)
|
||||
return true
|
||||
} catch (error) {
|
||||
acpLog.error("TerminalManager", `Failed to release ${terminalId}: ${error}`)
|
||||
// Still remove from tracking even if release failed
|
||||
this.terminals.delete(terminalId)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all active terminals.
|
||||
*/
|
||||
async releaseAll(): Promise<void> {
|
||||
acpLog.info("TerminalManager", `Releasing ${this.terminals.size} terminals`)
|
||||
|
||||
const releasePromises = Array.from(this.terminals.keys()).map((id) => this.releaseTerminal(id))
|
||||
|
||||
await Promise.all(releasePromises)
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Query Methods
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Check if a terminal exists.
|
||||
*/
|
||||
hasTerminal(terminalId: string): boolean {
|
||||
return this.terminals.has(terminalId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about a terminal.
|
||||
*/
|
||||
getTerminalInfo(terminalId: string): ActiveTerminal | undefined {
|
||||
return this.terminals.get(terminalId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active terminal IDs.
|
||||
*/
|
||||
getActiveTerminalIds(): string[] {
|
||||
return Array.from(this.terminals.keys())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of active terminals.
|
||||
*/
|
||||
get activeCount(): number {
|
||||
return this.terminals.size
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Helpers
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Parse a command string into executable and arguments.
|
||||
*
|
||||
* This handles common shell command patterns and extracts:
|
||||
* - The executable (first word or path)
|
||||
* - Arguments
|
||||
* - Working directory changes (cd ... &&)
|
||||
*/
|
||||
parseCommand(command: string): ParsedCommand {
|
||||
// Trim and normalize whitespace
|
||||
const trimmed = command.trim()
|
||||
|
||||
// Check for cd command at the start (common pattern: cd /path && command)
|
||||
const cdMatch = trimmed.match(/^cd\s+([^\s&]+)\s*&&\s*(.+)$/i)
|
||||
if (cdMatch && cdMatch[1] && cdMatch[2]) {
|
||||
const cwd = cdMatch[1]
|
||||
const restCommand = cdMatch[2]
|
||||
const parsed = this.parseSimpleCommand(restCommand)
|
||||
return {
|
||||
...parsed,
|
||||
cwd,
|
||||
}
|
||||
}
|
||||
|
||||
return this.parseSimpleCommand(trimmed)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a simple command (no cd prefix) into parts.
|
||||
*/
|
||||
private parseSimpleCommand(command: string): ParsedCommand {
|
||||
// For shell commands with operators, we need to run through a shell
|
||||
// Check for shell operators
|
||||
const hasShellOperators = /[|&;<>]/.test(command)
|
||||
|
||||
if (hasShellOperators) {
|
||||
// Run through shell to handle operators
|
||||
const shell = process.platform === "win32" ? "cmd.exe" : "/bin/sh"
|
||||
const shellArg = process.platform === "win32" ? "/c" : "-c"
|
||||
|
||||
return {
|
||||
fullCommand: command,
|
||||
executable: shell,
|
||||
args: [shellArg, command],
|
||||
}
|
||||
}
|
||||
|
||||
// Simple command - split on whitespace
|
||||
const parts = command.split(/\s+/).filter(Boolean)
|
||||
const executable = parts[0] || command
|
||||
const args = parts.slice(1)
|
||||
|
||||
return {
|
||||
fullCommand: command,
|
||||
executable,
|
||||
args,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue