@plaited/acp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025 Plaited
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # @plaited/acp
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@plaited/acp.svg)](https://www.npmjs.com/package/@plaited/acp)
4
+ [![CI](https://github.com/plaited/acp-harness/actions/workflows/ci.yml/badge.svg)](https://github.com/plaited/acp-harness/actions/workflows/ci.yml)
5
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
6
+
7
+ Unified ACP client and evaluation harness for TypeScript/Bun projects. Connect to ACP-compatible agents programmatically, capture full trajectories, and pipe to downstream analysis tools.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ bun add @plaited/acp
13
+ ```
14
+
15
+ **Prerequisite:** Install an ACP adapter:
16
+
17
+ ```bash
18
+ npm install -g @zed-industries/claude-code-acp
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```typescript
24
+ import { createACPClient, createPrompt, summarizeResponse } from '@plaited/acp'
25
+
26
+ const client = createACPClient({
27
+ command: ['claude-code-acp'],
28
+ cwd: '/path/to/project',
29
+ })
30
+
31
+ await client.connect()
32
+ const session = await client.createSession()
33
+
34
+ const { updates } = await client.promptSync(
35
+ session.id,
36
+ createPrompt('Create a function that validates email addresses')
37
+ )
38
+
39
+ const summary = summarizeResponse(updates)
40
+ console.log(summary.text, summary.completedToolCalls)
41
+
42
+ await client.disconnect()
43
+ ```
44
+
45
+ ## Recommended: Use the Bundled Skill
46
+
47
+ This package includes a comprehensive **acp-harness skill** designed for AI-assisted evaluation development. The skill provides:
48
+
49
+ - Complete API reference for `createACPClient` and helpers
50
+ - Harness CLI usage with all options and examples
51
+ - Output format schemas (summary and judge formats)
52
+ - LLM-as-judge evaluation templates
53
+ - Downstream integration patterns (Braintrust, jq, custom scorers)
54
+ - Docker execution guidance
55
+
56
+ ### Install the Skill
57
+
58
+ ```bash
59
+ # For Claude Code, Cursor, OpenCode, Amp, Goose, or Factory
60
+ curl -sSL https://raw.githubusercontent.com/plaited/acp-harness/main/scripts/install-acp.sh | bash
61
+ ```
62
+
63
+ Once installed, the skill auto-activates when working on evaluation tasks. Ask your AI agent to help you:
64
+
65
+ - Set up evaluation prompts
66
+ - Configure the harness CLI
67
+ - Design scoring pipelines
68
+ - Integrate with Braintrust or custom analysis tools
69
+
70
+ The skill contains everything needed to build agent evaluations - use it as your primary reference.
71
+
72
+ ## Development
73
+
74
+ ```bash
75
+ bun install # Install dependencies
76
+ bun run check # Type check + lint + format
77
+ bun test # Run unit tests
78
+ bun run check:write # Auto-fix issues
79
+ ```
80
+
81
+ ## Requirements
82
+
83
+ - **Runtime:** Bun >= 1.2.9
84
+ - **ACP Adapter:** `@zed-industries/claude-code-acp` or compatible
85
+ - **API Key:** `ANTHROPIC_API_KEY` environment variable
86
+
87
+ ## License
88
+
89
+ ISC © [Plaited Labs](https://github.com/plaited)
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@plaited/acp",
3
+ "version": "0.0.1",
4
+ "description": "ACP client and evaluation harness for TypeScript/Bun projects",
5
+ "license": "ISC",
6
+ "engines": {
7
+ "bun": ">= v1.2.9"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/plaited/acp-harness.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/plaited/acp-harness/issues"
15
+ },
16
+ "homepage": "https://github.com/plaited/acp-harness/tree/main#readme",
17
+ "type": "module",
18
+ "main": "./src/acp.ts",
19
+ "exports": {
20
+ ".": "./src/acp.ts",
21
+ "./*.ts": "./src/*.ts"
22
+ },
23
+ "files": [
24
+ "./src/**",
25
+ "!./src/**/tests/*",
26
+ "!./src/**/*.spec.ts",
27
+ "!./src/**/*.docker.ts"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "check": "bun run check:biome && bun run check:types && bun run check:package",
34
+ "check:biome": "biome check",
35
+ "check:package": "format-package --check",
36
+ "check:types": "tsc --noEmit",
37
+ "check:write": "biome check --write && format-package --write",
38
+ "prepare": "git rev-parse --git-dir > /dev/null 2>&1 && git config core.hooksPath .hooks || true",
39
+ "test": "bun test",
40
+ "test:docker": "docker compose -f docker-compose.test.yml run --rm acp-test"
41
+ },
42
+ "lint-staged": {
43
+ "*.{js,cjs,jsx,tsx,ts}": [
44
+ "bunx biome check --write --files-ignore-unknown"
45
+ ],
46
+ "package.json": [
47
+ "format-package -w"
48
+ ]
49
+ },
50
+ "dependencies": {
51
+ "@agentclientprotocol/sdk": "^0.13.0",
52
+ "zod": "^4.3.5"
53
+ },
54
+ "peerDependencies": {
55
+ "bun": ">=1.2.9"
56
+ },
57
+ "devDependencies": {
58
+ "@biomejs/biome": "2.3.11",
59
+ "@types/bun": "1.3.6",
60
+ "@zed-industries/claude-code-acp": "0.13.0",
61
+ "format-package": "7.0.0",
62
+ "lint-staged": "16.2.7",
63
+ "typescript": "5.9.3"
64
+ }
65
+ }
@@ -0,0 +1,503 @@
1
+ /**
2
+ * Headless ACP client for programmatic agent interaction.
3
+ *
4
+ * @remarks
5
+ * This client enables automated evaluation of ACP-compatible agents like
6
+ * Claude Code, Droid, Gemini CLI, and others. It provides:
7
+ *
8
+ * - **Subprocess management**: Spawn and control agent processes
9
+ * - **Session handling**: Create and manage conversation sessions
10
+ * - **Streaming prompts**: AsyncGenerator for real-time updates
11
+ * - **Sync prompts**: Simple request/response for basic evals
12
+ * - **Auto-permissions**: Automatically approves all permissions for headless use
13
+ *
14
+ * Designed for testing and evaluation, not for user-facing applications.
15
+ */
16
+
17
+ import type {
18
+ AgentCapabilities,
19
+ CancelNotification,
20
+ ClientCapabilities,
21
+ ContentBlock,
22
+ Implementation,
23
+ InitializeRequest,
24
+ InitializeResponse,
25
+ McpServer,
26
+ PromptRequest,
27
+ PromptResponse,
28
+ RequestPermissionRequest,
29
+ RequestPermissionResponse,
30
+ SessionNotification,
31
+ } from '@agentclientprotocol/sdk'
32
+ import { version } from '../package.json' with { type: 'json' }
33
+ import { ACP_METHODS, ACP_PROTOCOL_VERSION, DEFAULT_ACP_CLIENT_NAME } from './acp.constants.ts'
34
+ import { RequestPermissionRequestSchema, SessionNotificationSchema } from './acp.schemas.ts'
35
+ import type { Session } from './acp.types.ts'
36
+ import { createACPTransport } from './acp-transport.ts'
37
+ // ============================================================================
38
+ // Types
39
+ // ============================================================================
40
+
41
+ /** Configuration for the ACP client */
42
+ export type ACPClientConfig = {
43
+ /** Command to spawn agent (e.g., ['claude', 'code'] or ['droid']) */
44
+ command: string[]
45
+ /** Working directory for agent process */
46
+ cwd?: string
47
+ /** Environment variables for agent process */
48
+ env?: Record<string, string>
49
+ /** Client info for initialization */
50
+ clientInfo?: Implementation
51
+ /** Client capabilities to advertise */
52
+ capabilities?: ClientCapabilities
53
+ /** Timeout for operations in milliseconds (default: 30000) */
54
+ timeout?: number
55
+ /**
56
+ * Polling interval for streaming updates in milliseconds (default: 50).
57
+ * Lower values provide more responsive updates but increase CPU usage.
58
+ * Consider increasing for testing to reduce timing-related flakiness.
59
+ */
60
+ pollingInterval?: number
61
+ /**
62
+ * Permission handler for agent requests.
63
+ * Default: auto-approve all permissions (headless mode)
64
+ */
65
+ onPermissionRequest?: (params: RequestPermissionRequest) => Promise<RequestPermissionResponse>
66
+ }
67
+
68
+ /** Session update emitted during prompt streaming */
69
+ export type SessionUpdate = {
70
+ type: 'update'
71
+ params: SessionNotification
72
+ }
73
+
74
+ /** Prompt completion emitted when prompt finishes */
75
+ export type PromptComplete = {
76
+ type: 'complete'
77
+ result: PromptResponse
78
+ }
79
+
80
+ /** Events emitted during prompt streaming */
81
+ export type PromptEvent = SessionUpdate | PromptComplete
82
+
83
+ /** Error thrown by ACP client operations */
84
+ export class ACPClientError extends Error {
85
+ constructor(
86
+ message: string,
87
+ public readonly code?: string,
88
+ ) {
89
+ super(message)
90
+ this.name = 'ACPClientError'
91
+ }
92
+ }
93
+
94
+ // ============================================================================
95
+ // Client Implementation
96
+ // ============================================================================
97
+
98
+ /**
99
+ * Creates a headless ACP client for agent evaluation.
100
+ *
101
+ * @param config - Client configuration including command, cwd, and permission handling
102
+ * @returns Client object with lifecycle, session, and prompt methods
103
+ *
104
+ * @remarks
105
+ * The client manages:
106
+ * - Agent subprocess lifecycle (connect/disconnect)
107
+ * - Protocol initialization and capability negotiation
108
+ * - Session creation and management
109
+ * - Prompt streaming with real-time updates
110
+ * - Automatic permission approval for headless evaluation
111
+ *
112
+ * See module-level documentation in `src/acp.ts` for usage guidance.
113
+ * See client tests for usage patterns.
114
+ */
115
+ export const createACPClient = (config: ACPClientConfig) => {
116
+ const {
117
+ command,
118
+ cwd,
119
+ env,
120
+ clientInfo = { name: DEFAULT_ACP_CLIENT_NAME, version },
121
+ capabilities = {},
122
+ timeout = 30000,
123
+ pollingInterval = 50,
124
+ onPermissionRequest,
125
+ } = config
126
+
127
+ let transport: ReturnType<typeof createACPTransport> | undefined
128
+ let agentCapabilities: AgentCapabilities | undefined
129
+ let initializeResult: InitializeResponse | undefined
130
+
131
+ // Track active prompt sessions for update routing
132
+ const activePrompts = new Map<
133
+ string,
134
+ {
135
+ updates: SessionNotification[]
136
+ resolve: (result: PromptResponse) => void
137
+ reject: (error: Error) => void
138
+ }
139
+ >()
140
+
141
+ // --------------------------------------------------------------------------
142
+ // Permission Handling
143
+ // --------------------------------------------------------------------------
144
+
145
+ /**
146
+ * Default permission handler: auto-approve all requests.
147
+ * For headless evaluation in trusted environments.
148
+ *
149
+ * @remarks
150
+ * Validates params with Zod before processing.
151
+ * Prioritizes `allow_always` for faster headless evaluation with fewer
152
+ * permission round-trips. Cancels if validation fails or no allow option
153
+ * is available.
154
+ */
155
+ const autoApprovePermission = async (params: RequestPermissionRequest): Promise<RequestPermissionResponse> => {
156
+ const result = RequestPermissionRequestSchema.safeParse(params)
157
+ if (!result.success) {
158
+ return { outcome: { outcome: 'cancelled' } }
159
+ }
160
+
161
+ const { options } = result.data
162
+
163
+ // Priority: allow_always (fewer round-trips) > allow_once
164
+ const allowAlways = options.find((opt) => opt.kind === 'allow_always')
165
+ if (allowAlways) {
166
+ return { outcome: { outcome: 'selected', optionId: allowAlways.optionId } }
167
+ }
168
+
169
+ const allowOnce = options.find((opt) => opt.kind === 'allow_once')
170
+ if (allowOnce) {
171
+ return { outcome: { outcome: 'selected', optionId: allowOnce.optionId } }
172
+ }
173
+
174
+ // No allow option available - cancel
175
+ return { outcome: { outcome: 'cancelled' } }
176
+ }
177
+
178
+ const handlePermissionRequest = onPermissionRequest ?? autoApprovePermission
179
+
180
+ // --------------------------------------------------------------------------
181
+ // Transport Callbacks
182
+ // --------------------------------------------------------------------------
183
+
184
+ const onNotification = (method: string, params: unknown) => {
185
+ if (method === ACP_METHODS.UPDATE) {
186
+ const updateParams = SessionNotificationSchema.parse(params)
187
+ const activePrompt = activePrompts.get(updateParams.sessionId)
188
+ if (activePrompt) {
189
+ activePrompt.updates.push(updateParams)
190
+ }
191
+ }
192
+ }
193
+
194
+ const onRequest = async (method: string, params: unknown): Promise<unknown> => {
195
+ if (method === ACP_METHODS.REQUEST_PERMISSION) {
196
+ return handlePermissionRequest(RequestPermissionRequestSchema.parse(params))
197
+ }
198
+
199
+ throw new ACPClientError(`Unknown request method: ${method}`)
200
+ }
201
+
202
+ // --------------------------------------------------------------------------
203
+ // Lifecycle Methods
204
+ // --------------------------------------------------------------------------
205
+
206
+ /**
207
+ * Connects to the agent by spawning the subprocess and initializing the protocol.
208
+ *
209
+ * @returns Initialize result with agent capabilities
210
+ * @throws {ACPClientError} If already connected or connection fails
211
+ */
212
+ const connect = async (): Promise<InitializeResponse> => {
213
+ if (transport?.isConnected()) {
214
+ throw new ACPClientError('Already connected')
215
+ }
216
+
217
+ transport = createACPTransport({
218
+ command,
219
+ cwd,
220
+ env,
221
+ timeout,
222
+ onNotification,
223
+ onRequest,
224
+ onError: (error) => {
225
+ console.error('[ACP Client Error]:', error.message)
226
+ },
227
+ onClose: (code) => {
228
+ // Reject all active prompts on unexpected close
229
+ for (const [sessionId, prompt] of activePrompts) {
230
+ prompt.reject(new ACPClientError(`Agent process exited with code ${code}`))
231
+ activePrompts.delete(sessionId)
232
+ }
233
+ },
234
+ })
235
+
236
+ await transport.start()
237
+
238
+ // Initialize protocol
239
+ const initParams: InitializeRequest = {
240
+ protocolVersion: ACP_PROTOCOL_VERSION,
241
+ clientInfo,
242
+ clientCapabilities: capabilities,
243
+ }
244
+
245
+ initializeResult = await transport.request<InitializeResponse>(ACP_METHODS.INITIALIZE, initParams)
246
+
247
+ agentCapabilities = initializeResult?.agentCapabilities
248
+
249
+ return initializeResult
250
+ }
251
+
252
+ /**
253
+ * Disconnects from the agent, closing the subprocess.
254
+ *
255
+ * @param graceful - If true, sends shutdown request first (default: true)
256
+ */
257
+ const disconnect = async (graceful = true): Promise<void> => {
258
+ if (!transport) return
259
+
260
+ // Cancel all active prompts
261
+ for (const [sessionId, prompt] of activePrompts) {
262
+ prompt.reject(new ACPClientError('Client disconnected'))
263
+ activePrompts.delete(sessionId)
264
+ }
265
+
266
+ await transport.close(graceful)
267
+ transport = undefined
268
+ agentCapabilities = undefined
269
+ initializeResult = undefined
270
+ }
271
+
272
+ // --------------------------------------------------------------------------
273
+ // Session Methods
274
+ // --------------------------------------------------------------------------
275
+
276
+ /**
277
+ * Creates a new conversation session.
278
+ *
279
+ * @param params - Session parameters with working directory and optional MCP servers
280
+ * @returns The created session
281
+ * @throws {ACPClientError} If not connected
282
+ */
283
+ const createSession = async (params: { cwd: string; mcpServers?: McpServer[] }): Promise<Session> => {
284
+ if (!transport?.isConnected()) {
285
+ throw new ACPClientError('Not connected')
286
+ }
287
+
288
+ const response = await transport.request<{ sessionId: string }>(ACP_METHODS.CREATE_SESSION, {
289
+ cwd: params.cwd,
290
+ mcpServers: params.mcpServers ?? [],
291
+ })
292
+ return { id: response.sessionId }
293
+ }
294
+
295
+ /**
296
+ * Sets the model for a session.
297
+ *
298
+ * @experimental This is an unstable ACP feature and may change.
299
+ * @param sessionId - The session ID to set the model for
300
+ * @param modelId - The model ID (e.g., 'claude-3-5-haiku-20241022', 'claude-sonnet-4-20250514')
301
+ * @throws {ACPClientError} If not connected
302
+ */
303
+ const setModel = async (sessionId: string, modelId: string): Promise<void> => {
304
+ if (!transport?.isConnected()) {
305
+ throw new ACPClientError('Not connected')
306
+ }
307
+
308
+ await transport.request(ACP_METHODS.SET_MODEL, { sessionId, modelId })
309
+ }
310
+
311
+ // --------------------------------------------------------------------------
312
+ // Prompt Methods
313
+ // --------------------------------------------------------------------------
314
+
315
+ /**
316
+ * Sends a prompt and streams updates as they arrive.
317
+ *
318
+ * @param sessionId - The session ID to send the prompt to
319
+ * @param content - Content blocks for the prompt
320
+ * @yields Session updates and final completion
321
+ * @throws {ACPClientError} If not connected
322
+ *
323
+ * @remarks
324
+ * Use this for evaluation scenarios where you need access to
325
+ * intermediate updates (tool calls, plan changes, etc).
326
+ */
327
+ async function* prompt(sessionId: string, content: ContentBlock[]): AsyncGenerator<PromptEvent> {
328
+ if (!transport?.isConnected()) {
329
+ throw new ACPClientError('Not connected')
330
+ }
331
+
332
+ const { promise, resolve, reject } = Promise.withResolvers<PromptResponse>()
333
+ const updates: SessionNotification[] = []
334
+ const promptState = {
335
+ updates,
336
+ resolve,
337
+ reject,
338
+ }
339
+
340
+ activePrompts.set(sessionId, promptState)
341
+
342
+ // Send prompt request
343
+ const promptParams: PromptRequest = {
344
+ sessionId,
345
+ prompt: content,
346
+ }
347
+
348
+ // Start the prompt request (don't await - we'll poll for updates)
349
+ const promptPromise = transport
350
+ .request<PromptResponse>(ACP_METHODS.PROMPT, promptParams)
351
+ .then(resolve)
352
+ .catch(reject)
353
+
354
+ try {
355
+ // Poll for updates until prompt completes
356
+ let lastYieldedIndex = 0
357
+
358
+ while (true) {
359
+ // Yield any new updates
360
+ while (lastYieldedIndex < promptState.updates.length) {
361
+ const update = promptState.updates[lastYieldedIndex]
362
+ if (update) {
363
+ yield { type: 'update', params: update }
364
+ }
365
+ lastYieldedIndex++
366
+ }
367
+
368
+ // Check if prompt completed
369
+ const raceResult = await Promise.race([
370
+ promise.then((result) => ({ done: true as const, result })),
371
+ new Promise<{ done: false }>((res) => setTimeout(() => res({ done: false }), pollingInterval)),
372
+ ])
373
+
374
+ if (raceResult.done) {
375
+ // Yield any remaining updates
376
+ while (lastYieldedIndex < promptState.updates.length) {
377
+ const update = promptState.updates[lastYieldedIndex]
378
+ if (update) {
379
+ yield { type: 'update', params: update }
380
+ }
381
+ lastYieldedIndex++
382
+ }
383
+
384
+ // Yield completion
385
+ yield {
386
+ type: 'complete',
387
+ result: raceResult.result,
388
+ }
389
+ break
390
+ }
391
+ }
392
+
393
+ await promptPromise
394
+ } finally {
395
+ activePrompts.delete(sessionId)
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Sends a prompt and waits for the final result.
401
+ *
402
+ * @param sessionId - The session ID to send the prompt to
403
+ * @param content - Content blocks for the prompt
404
+ * @returns The prompt result with all accumulated updates
405
+ * @throws {ACPClientError} If not connected
406
+ *
407
+ * @remarks
408
+ * Use this for simple evaluation scenarios where you only need
409
+ * the final result. All intermediate updates are collected but
410
+ * returned together at the end.
411
+ */
412
+ const promptSync = async (
413
+ sessionId: string,
414
+ content: ContentBlock[],
415
+ ): Promise<{
416
+ result: PromptResponse
417
+ updates: SessionNotification[]
418
+ }> => {
419
+ const updates: SessionNotification[] = []
420
+ let result: PromptResponse | undefined
421
+
422
+ for await (const event of prompt(sessionId, content)) {
423
+ if (event.type === 'update') {
424
+ updates.push(event.params)
425
+ } else if (event.type === 'complete') {
426
+ result = event.result
427
+ }
428
+ }
429
+
430
+ if (!result) {
431
+ throw new ACPClientError('Prompt completed without result')
432
+ }
433
+
434
+ return { result, updates }
435
+ }
436
+
437
+ /**
438
+ * Cancels an ongoing prompt.
439
+ *
440
+ * @param sessionId - The session ID to cancel
441
+ * @throws {ACPClientError} If not connected
442
+ */
443
+ const cancelPrompt = async (sessionId: string): Promise<void> => {
444
+ if (!transport?.isConnected()) {
445
+ throw new ACPClientError('Not connected')
446
+ }
447
+
448
+ const cancelParams: CancelNotification = { sessionId }
449
+ await transport.notify(ACP_METHODS.CANCEL, cancelParams)
450
+ }
451
+
452
+ // --------------------------------------------------------------------------
453
+ // State Methods
454
+ // --------------------------------------------------------------------------
455
+
456
+ /**
457
+ * Gets the agent capabilities negotiated during initialization.
458
+ *
459
+ * @returns Agent capabilities or undefined if not connected
460
+ */
461
+ const getCapabilities = (): AgentCapabilities | undefined => {
462
+ return agentCapabilities
463
+ }
464
+
465
+ /**
466
+ * Gets the full initialization result.
467
+ *
468
+ * @returns Initialize result or undefined if not connected
469
+ */
470
+ const getInitializeResult = (): InitializeResponse | undefined => {
471
+ return initializeResult
472
+ }
473
+
474
+ /**
475
+ * Checks if the client is connected to an agent.
476
+ */
477
+ const isConnected = (): boolean => {
478
+ return transport?.isConnected() ?? false
479
+ }
480
+
481
+ return {
482
+ // Lifecycle
483
+ connect,
484
+ disconnect,
485
+
486
+ // Sessions
487
+ createSession,
488
+ setModel,
489
+
490
+ // Prompts
491
+ prompt,
492
+ promptSync,
493
+ cancelPrompt,
494
+
495
+ // State
496
+ getCapabilities,
497
+ getInitializeResult,
498
+ isConnected,
499
+ }
500
+ }
501
+
502
+ /** Client instance type */
503
+ export type ACPClient = ReturnType<typeof createACPClient>