> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/agentclientprotocol/typescript-sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Terminal Operations

> Learn how to create and manage terminals for executing commands

## Overview

The ACP SDK allows agents to create terminals and execute commands in the client's environment. This enables agents to run build tools, tests, scripts, and other command-line operations.

<Note>
  Terminal operations are only available if the client advertises the `terminal` capability during initialization.
</Note>

## Creating Terminals

The `createTerminal` method executes a command in a new terminal:

```typescript theme={null}
async createTerminal(
  params: CreateTerminalRequest
): Promise<TerminalHandle>
```

<ParamField path="params.sessionId" type="string" required>
  The session ID creating the terminal
</ParamField>

<ParamField path="params.command" type="string" required>
  The command to execute
</ParamField>

<ParamField path="params.args" type="string[]">
  Optional command arguments
</ParamField>

<ParamField path="params.workingDirectory" type="string">
  Optional working directory for the command
</ParamField>

<ParamField path="params.env" type="Record<string, string>">
  Optional environment variables
</ParamField>

<ResponseField name="TerminalHandle" type="object" required>
  A handle to control and monitor the terminal
</ResponseField>

Returns a `TerminalHandle` that can be used to get output, wait for exit, kill the command, or release the terminal.

### Example

```typescript theme={null}
class MyAgent implements acp.Agent {
  private connection: acp.AgentSideConnection;

  async runCommand(
    sessionId: string,
    command: string,
    args: string[] = []
  ): Promise<string> {
    // Create the terminal
    const terminal = await this.connection.createTerminal({
      sessionId,
      command,
      args,
      workingDirectory: "/home/user/project",
    });

    try {
      // Wait for the command to complete
      const exitStatus = await terminal.waitForExit();

      // Get the final output
      const output = await terminal.currentOutput();

      if (exitStatus.exitCode === 0) {
        return output.output;
      } else {
        throw new Error(`Command failed with exit code ${exitStatus.exitCode}`);
      }
    } finally {
      // Always release the terminal
      await terminal.release();
    }
  }
}
```

## TerminalHandle Class

The `TerminalHandle` class provides methods to control and monitor a terminal:

```typescript theme={null}
export class TerminalHandle {
  id: string;
  
  async currentOutput(): Promise<TerminalOutputResponse>;
  async waitForExit(): Promise<WaitForTerminalExitResponse>;
  async kill(): Promise<KillTerminalResponse>;
  async release(): Promise<ReleaseTerminalResponse | void>;
  [Symbol.asyncDispose](): Promise<void>;
}
```

### currentOutput

Gets the current terminal output without waiting for the command to exit.

```typescript theme={null}
async currentOutput(): Promise<TerminalOutputResponse>
```

<ResponseField name="output" type="string" required>
  The current output from the terminal
</ResponseField>

<ResponseField name="exitCode" type="number">
  The exit code if the command has already exited
</ResponseField>

<ResponseField name="signal" type="string">
  The signal that terminated the process, if applicable
</ResponseField>

#### Example

```typescript theme={null}
const terminal = await this.connection.createTerminal({
  sessionId,
  command: "npm",
  args: ["install"],
});

// Poll for output while command is running
const pollInterval = setInterval(async () => {
  const output = await terminal.currentOutput();
  console.log("Current output:", output.output);
  
  if (output.exitCode !== undefined) {
    clearInterval(pollInterval);
  }
}, 1000);
```

### waitForExit

Waits for the terminal command to complete and returns its exit status.

```typescript theme={null}
async waitForExit(): Promise<WaitForTerminalExitResponse>
```

<ResponseField name="exitCode" type="number">
  The exit code of the process
</ResponseField>

<ResponseField name="signal" type="string">
  The signal that terminated the process, if applicable
</ResponseField>

This method blocks until the command completes, providing the exit code and/or signal that terminated the process.

#### Example

```typescript theme={null}
const terminal = await this.connection.createTerminal({
  sessionId,
  command: "npm",
  args: ["test"],
});

// Wait for tests to complete
const exitStatus = await terminal.waitForExit();

if (exitStatus.exitCode === 0) {
  console.log("Tests passed!");
} else {
  console.error(`Tests failed with exit code ${exitStatus.exitCode}`);
}

await terminal.release();
```

### kill

Kills the terminal command without releasing the terminal.

```typescript theme={null}
async kill(): Promise<KillTerminalResponse>
```

The terminal remains valid after killing, allowing you to:

* Get the final output with `currentOutput()`
* Check the exit status
* Release the terminal when done

Useful for implementing timeouts or cancellation.

#### Example

```typescript theme={null}
const terminal = await this.connection.createTerminal({
  sessionId,
  command: "long-running-command",
});

// Set a timeout
const timeout = setTimeout(async () => {
  console.log("Command timed out, killing...");
  await terminal.kill();
}, 30000); // 30 seconds

try {
  await terminal.waitForExit();
  clearTimeout(timeout);
} finally {
  // Get final output even after killing
  const output = await terminal.currentOutput();
  console.log("Final output:", output.output);
  
  await terminal.release();
}
```

### release

Releases the terminal and frees all associated resources.

```typescript theme={null}
async release(): Promise<ReleaseTerminalResponse | void>
```

If the command is still running, it will be killed. After release, the terminal ID becomes invalid and cannot be used with other terminal methods.

Tool calls that already reference this terminal will continue to display its output.

<Warning>
  Always call `release()` when done with the terminal to free resources.
</Warning>

#### Example with `await using`

The `TerminalHandle` class supports async disposal via `Symbol.asyncDispose`, allowing automatic cleanup:

```typescript theme={null}
// Automatically releases the terminal when it goes out of scope
await using terminal = await this.connection.createTerminal({
  sessionId,
  command: "npm",
  args: ["build"],
});

await terminal.waitForExit();
const output = await terminal.currentOutput();
// terminal.release() is called automatically
```

## Embedding Terminals in Tool Calls

Terminals can be embedded in tool calls by using their ID in `ToolCallContent` with type "terminal":

```typescript theme={null}
// Create a terminal
const terminal = await this.connection.createTerminal({
  sessionId,
  command: "npm",
  args: ["run", "build"],
});

// Send a tool call that references the terminal
await this.connection.sessionUpdate({
  sessionId,
  update: {
    sessionUpdate: "tool_call",
    toolCallId: "build_1",
    title: "Building project",
    kind: "bash",
    status: "pending",
    rawInput: { command: "npm run build" },
  },
});

// Wait for completion
const exitStatus = await terminal.waitForExit();
const output = await terminal.currentOutput();

// Update tool call with terminal output
await this.connection.sessionUpdate({
  sessionId,
  update: {
    sessionUpdate: "tool_call_update",
    toolCallId: "build_1",
    status: exitStatus.exitCode === 0 ? "completed" : "error",
    content: [
      {
        type: "terminal",
        terminalId: terminal.id,
      },
    ],
    rawOutput: { output: output.output, exitCode: exitStatus.exitCode },
  },
});

await terminal.release();
```

## Error Handling

Handle errors appropriately when working with terminals:

```typescript theme={null}
async runCommandSafely(
  sessionId: string,
  command: string,
  args: string[]
): Promise<{ success: boolean; output: string; exitCode?: number }> {
  let terminal: acp.TerminalHandle | null = null;

  try {
    terminal = await this.connection.createTerminal({
      sessionId,
      command,
      args,
    });

    const exitStatus = await terminal.waitForExit();
    const output = await terminal.currentOutput();

    return {
      success: exitStatus.exitCode === 0,
      output: output.output,
      exitCode: exitStatus.exitCode,
    };
  } catch (error) {
    console.error("Terminal error:", error);
    return {
      success: false,
      output: error instanceof Error ? error.message : "Unknown error",
    };
  } finally {
    if (terminal) {
      await terminal.release();
    }
  }
}
```

## Complete Example

Here's a complete example that demonstrates terminal operations:

```typescript theme={null}
import * as acp from "@agentprotocol/acp";

class BuildAgent implements acp.Agent {
  private connection: acp.AgentSideConnection;
  private capabilities: acp.ClientCapabilities | null = null;

  constructor(connection: acp.AgentSideConnection) {
    this.connection = connection;
  }

  async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResponse> {
    this.capabilities = params.clientCapabilities;
    return {
      protocolVersion: acp.PROTOCOL_VERSION,
      agentCapabilities: {},
    };
  }

  async runBuild(sessionId: string, workdir: string): Promise<void> {
    if (!this.capabilities?.terminal) {
      throw new Error("Terminal capability not available");
    }

    // Send initial message
    await this.connection.sessionUpdate({
      sessionId,
      update: {
        sessionUpdate: "agent_message_chunk",
        content: {
          type: "text",
          text: "Starting build process...",
        },
      },
    });

    // Create terminal for build
    await using terminal = await this.connection.createTerminal({
      sessionId,
      command: "npm",
      args: ["run", "build"],
      workingDirectory: workdir,
    });

    // Send tool call
    await this.connection.sessionUpdate({
      sessionId,
      update: {
        sessionUpdate: "tool_call",
        toolCallId: "build_1",
        title: "Running npm build",
        kind: "bash",
        status: "pending",
        rawInput: { command: "npm run build" },
      },
    });

    // Wait for completion
    const exitStatus = await terminal.waitForExit();
    const output = await terminal.currentOutput();

    // Update tool call with results
    await this.connection.sessionUpdate({
      sessionId,
      update: {
        sessionUpdate: "tool_call_update",
        toolCallId: "build_1",
        status: exitStatus.exitCode === 0 ? "completed" : "error",
        content: [
          {
            type: "terminal",
            terminalId: terminal.id,
          },
        ],
        rawOutput: { output: output.output, exitCode: exitStatus.exitCode },
      },
    });

    // Send final message
    const message =
      exitStatus.exitCode === 0
        ? "Build completed successfully!"
        : `Build failed with exit code ${exitStatus.exitCode}`;

    await this.connection.sessionUpdate({
      sessionId,
      update: {
        sessionUpdate: "agent_message_chunk",
        content: { type: "text", text: message },
      },
    });
  }

  // ... implement other required Agent methods
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Always Release" icon="circle-xmark">
    Always call `release()` when done to free resources, or use `await using`
  </Card>

  <Card title="Handle Timeouts" icon="clock">
    Implement timeouts using `kill()` to prevent commands from running indefinitely
  </Card>

  <Card title="Check Capabilities" icon="shield-check">
    Verify the client supports terminal operations before attempting to use them
  </Card>

  <Card title="Capture Output" icon="terminal">
    Get final output after `waitForExit()` for complete command results
  </Card>
</CardGroup>
