acp-factory 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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +341 -0
- package/dist/agent-handle.d.ts +55 -0
- package/dist/agent-handle.d.ts.map +1 -0
- package/dist/agent-handle.js +154 -0
- package/dist/agent-handle.js.map +1 -0
- package/dist/client-handler.d.ts +99 -0
- package/dist/client-handler.d.ts.map +1 -0
- package/dist/client-handler.js +311 -0
- package/dist/client-handler.js.map +1 -0
- package/dist/factory.d.ts +28 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +48 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/claude-code.d.ts +9 -0
- package/dist/providers/claude-code.d.ts.map +1 -0
- package/dist/providers/claude-code.js +12 -0
- package/dist/providers/claude-code.js.map +1 -0
- package/dist/session.d.ts +125 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +221 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +154 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2024-XX-XX
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Initial release
|
|
15
|
+
- `AgentFactory` for registering and spawning agents
|
|
16
|
+
- `AgentHandle` for managing agent processes
|
|
17
|
+
- `Session` for interacting with agent sessions
|
|
18
|
+
- Pre-registered `claude-code` agent
|
|
19
|
+
- Streaming responses via async iterators
|
|
20
|
+
- Permission handling modes: `auto-approve`, `auto-deny`, `callback`, `interactive`
|
|
21
|
+
- Interactive permission requests as session updates
|
|
22
|
+
- `session.interruptWith()` for interrupting and redirecting agents
|
|
23
|
+
- `session.fork()` for creating independent session copies (experimental)
|
|
24
|
+
- `agentHandle.forkSession()` for forking by session ID (experimental)
|
|
25
|
+
- `session.addContext()` stub for future mid-execution messaging
|
|
26
|
+
- Custom file read/write handlers
|
|
27
|
+
- Terminal operation handlers
|
|
28
|
+
- MCP server support
|
|
29
|
+
- Full TypeScript type exports
|
|
30
|
+
|
|
31
|
+
### Dependencies
|
|
32
|
+
|
|
33
|
+
- `@agentclientprotocol/sdk` ^0.10.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sudocode AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# acp-factory
|
|
2
|
+
|
|
3
|
+
A TypeScript library for spawning and managing AI agents through the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install acp-factory
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- Node.js 18+
|
|
14
|
+
- [Claude Code](https://claude.ai/claude-code) installed and authenticated (run `claude` once to set up)
|
|
15
|
+
- Or set `ANTHROPIC_API_KEY` environment variable
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { AgentFactory } from "acp-factory";
|
|
21
|
+
|
|
22
|
+
// Spawn a Claude Code agent
|
|
23
|
+
const agent = await AgentFactory.spawn("claude-code", {
|
|
24
|
+
permissionMode: "auto-approve",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Create a session
|
|
28
|
+
const session = await agent.createSession(process.cwd());
|
|
29
|
+
|
|
30
|
+
// Send a prompt and stream responses
|
|
31
|
+
for await (const update of session.prompt("What files are in this directory?")) {
|
|
32
|
+
if (update.sessionUpdate === "agent_message_chunk") {
|
|
33
|
+
if (update.content.type === "text") {
|
|
34
|
+
process.stdout.write(update.content.text);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Clean up
|
|
40
|
+
await agent.close();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API Reference
|
|
44
|
+
|
|
45
|
+
### AgentFactory
|
|
46
|
+
|
|
47
|
+
Static class for managing agent types and spawning agents.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// List available agents
|
|
51
|
+
AgentFactory.listAgents(); // ["claude-code"]
|
|
52
|
+
|
|
53
|
+
// Register a custom agent
|
|
54
|
+
AgentFactory.register("my-agent", {
|
|
55
|
+
command: "npx",
|
|
56
|
+
args: ["my-agent-acp"],
|
|
57
|
+
env: { MY_VAR: "value" },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Spawn an agent
|
|
61
|
+
const agent = await AgentFactory.spawn("claude-code", options);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### AgentHandle
|
|
65
|
+
|
|
66
|
+
Represents a running agent process.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Agent capabilities
|
|
70
|
+
agent.capabilities; // { loadSession: true, ... }
|
|
71
|
+
|
|
72
|
+
// Create a new session
|
|
73
|
+
const session = await agent.createSession("/path/to/cwd", {
|
|
74
|
+
mode: "code", // Optional: initial mode
|
|
75
|
+
mcpServers: [], // Optional: MCP servers to connect
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Load an existing session
|
|
79
|
+
const session = await agent.loadSession(sessionId, "/path/to/cwd");
|
|
80
|
+
|
|
81
|
+
// Fork an existing session (experimental)
|
|
82
|
+
const forkedSession = await agent.forkSession(sessionId);
|
|
83
|
+
|
|
84
|
+
// Close the agent
|
|
85
|
+
await agent.close();
|
|
86
|
+
|
|
87
|
+
// Check if running
|
|
88
|
+
agent.isRunning(); // true/false
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Session
|
|
92
|
+
|
|
93
|
+
High-level interface for interacting with an agent session.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Session properties
|
|
97
|
+
session.id; // Session ID
|
|
98
|
+
session.modes; // Available modes ["code", "ask", ...]
|
|
99
|
+
session.models; // Available models ["claude-sonnet-4-...", ...]
|
|
100
|
+
|
|
101
|
+
// Send a prompt (returns async iterable)
|
|
102
|
+
for await (const update of session.prompt("Hello!")) {
|
|
103
|
+
// Handle updates
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Cancel the current prompt
|
|
107
|
+
await session.cancel();
|
|
108
|
+
|
|
109
|
+
// Set the session mode
|
|
110
|
+
await session.setMode("ask");
|
|
111
|
+
|
|
112
|
+
// Interrupt and redirect with new context
|
|
113
|
+
for await (const update of session.interruptWith("Focus on tests only")) {
|
|
114
|
+
// Handle updates from new prompt
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Fork the session (experimental)
|
|
118
|
+
const forkedSession = await session.fork();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Session Updates
|
|
122
|
+
|
|
123
|
+
The `prompt()` method yields `SessionUpdate` objects:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
for await (const update of session.prompt("Hello")) {
|
|
127
|
+
switch (update.sessionUpdate) {
|
|
128
|
+
case "agent_message_chunk":
|
|
129
|
+
// Text or image content from the agent
|
|
130
|
+
if (update.content.type === "text") {
|
|
131
|
+
process.stdout.write(update.content.text);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case "tool_call":
|
|
136
|
+
// Agent is calling a tool
|
|
137
|
+
console.log(`Tool: ${update.title}`);
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case "tool_call_update":
|
|
141
|
+
// Tool call status changed
|
|
142
|
+
if (update.status === "completed") {
|
|
143
|
+
console.log("Tool completed");
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case "agent_thought_chunk":
|
|
148
|
+
// Agent's thinking (if enabled)
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Permission Modes
|
|
155
|
+
|
|
156
|
+
Control how permission requests are handled:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Auto-approve all requests (default)
|
|
160
|
+
await AgentFactory.spawn("claude-code", {
|
|
161
|
+
permissionMode: "auto-approve",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Auto-deny all requests
|
|
165
|
+
await AgentFactory.spawn("claude-code", {
|
|
166
|
+
permissionMode: "auto-deny",
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Use a callback handler
|
|
170
|
+
await AgentFactory.spawn("claude-code", {
|
|
171
|
+
permissionMode: "callback",
|
|
172
|
+
onPermissionRequest: async (request) => {
|
|
173
|
+
// Return permission response
|
|
174
|
+
return {
|
|
175
|
+
outcome: { outcome: "selected", optionId: "allow" },
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Interactive mode - permissions emitted as session updates
|
|
181
|
+
await AgentFactory.spawn("claude-code", {
|
|
182
|
+
permissionMode: "interactive",
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Interactive Permissions
|
|
187
|
+
|
|
188
|
+
With `permissionMode: "interactive"`, permission requests appear as session updates:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { type ExtendedSessionUpdate } from "acp-factory";
|
|
192
|
+
|
|
193
|
+
for await (const update of session.prompt("Write a file")) {
|
|
194
|
+
if (update.sessionUpdate === "permission_request") {
|
|
195
|
+
// Show options to user
|
|
196
|
+
console.log(`Permission needed: ${update.toolCall.title}`);
|
|
197
|
+
update.options.forEach((opt, i) => {
|
|
198
|
+
console.log(`${i + 1}. ${opt.name}`);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Respond to the request
|
|
202
|
+
session.respondToPermission(update.requestId, update.options[0].optionId);
|
|
203
|
+
// Or cancel it
|
|
204
|
+
// session.cancelPermission(update.requestId);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Interrupting and Redirecting
|
|
210
|
+
|
|
211
|
+
Interrupt an agent mid-execution and redirect with new context:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const promptIterator = session.prompt("Analyze this codebase")[Symbol.asyncIterator]();
|
|
215
|
+
|
|
216
|
+
while (true) {
|
|
217
|
+
const { done, value } = await promptIterator.next();
|
|
218
|
+
if (done) break;
|
|
219
|
+
|
|
220
|
+
handleUpdate(value);
|
|
221
|
+
|
|
222
|
+
if (userWantsToRedirect) {
|
|
223
|
+
// Interrupt and provide new direction
|
|
224
|
+
for await (const update of session.interruptWith("Focus only on the tests")) {
|
|
225
|
+
handleUpdate(update);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Forking Sessions
|
|
233
|
+
|
|
234
|
+
Fork a session to create an independent copy with shared history:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// Original session continues normally
|
|
238
|
+
const session = await agent.createSession(process.cwd());
|
|
239
|
+
await consumePrompt(session.prompt("Analyze the codebase"));
|
|
240
|
+
|
|
241
|
+
// Fork to generate summary without affecting original
|
|
242
|
+
const summarySession = await session.fork();
|
|
243
|
+
for await (const update of summarySession.prompt("Summarize our conversation")) {
|
|
244
|
+
// This doesn't affect the original session
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Original session can continue independently
|
|
248
|
+
for await (const update of session.prompt("Now implement the feature")) {
|
|
249
|
+
// Continues from original context
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Custom File and Terminal Handlers
|
|
254
|
+
|
|
255
|
+
Override default file operations or provide terminal support:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const agent = await AgentFactory.spawn("claude-code", {
|
|
259
|
+
// Custom file handling
|
|
260
|
+
onFileRead: async (path) => {
|
|
261
|
+
return await myCustomRead(path);
|
|
262
|
+
},
|
|
263
|
+
onFileWrite: async (path, content) => {
|
|
264
|
+
await myCustomWrite(path, content);
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// Terminal support (all handlers required)
|
|
268
|
+
onTerminalCreate: async (params) => {
|
|
269
|
+
const id = createTerminal(params.command, params.args);
|
|
270
|
+
return { terminalId: id };
|
|
271
|
+
},
|
|
272
|
+
onTerminalOutput: async (terminalId) => {
|
|
273
|
+
return getTerminalOutput(terminalId);
|
|
274
|
+
},
|
|
275
|
+
onTerminalKill: async (terminalId) => {
|
|
276
|
+
killTerminal(terminalId);
|
|
277
|
+
},
|
|
278
|
+
onTerminalRelease: async (terminalId) => {
|
|
279
|
+
releaseTerminal(terminalId);
|
|
280
|
+
},
|
|
281
|
+
onTerminalWaitForExit: async (terminalId) => {
|
|
282
|
+
return await waitForExit(terminalId);
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Examples
|
|
288
|
+
|
|
289
|
+
See the [examples](./examples) directory:
|
|
290
|
+
|
|
291
|
+
- [`basic-usage.ts`](./examples/basic-usage.ts) - Simple prompt and response
|
|
292
|
+
- [`interactive-permissions.ts`](./examples/interactive-permissions.ts) - Handle permissions in UI
|
|
293
|
+
- [`interrupt-context.ts`](./examples/interrupt-context.ts) - Interrupt and redirect agent
|
|
294
|
+
|
|
295
|
+
Run examples with:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npx tsx examples/basic-usage.ts
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Type Exports
|
|
302
|
+
|
|
303
|
+
The library exports all necessary types:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
import {
|
|
307
|
+
// Core classes
|
|
308
|
+
AgentFactory,
|
|
309
|
+
AgentHandle,
|
|
310
|
+
Session,
|
|
311
|
+
|
|
312
|
+
// Configuration types
|
|
313
|
+
type AgentConfig,
|
|
314
|
+
type SpawnOptions,
|
|
315
|
+
type SessionOptions,
|
|
316
|
+
type PermissionMode,
|
|
317
|
+
|
|
318
|
+
// Session update types
|
|
319
|
+
type SessionUpdate,
|
|
320
|
+
type ExtendedSessionUpdate,
|
|
321
|
+
type PermissionRequestUpdate,
|
|
322
|
+
|
|
323
|
+
// Content types
|
|
324
|
+
type ContentBlock,
|
|
325
|
+
type TextContent,
|
|
326
|
+
type ImageContent,
|
|
327
|
+
|
|
328
|
+
// Tool types
|
|
329
|
+
type ToolCall,
|
|
330
|
+
type ToolCallUpdate,
|
|
331
|
+
|
|
332
|
+
// Permission types
|
|
333
|
+
type PermissionOption,
|
|
334
|
+
type RequestPermissionRequest,
|
|
335
|
+
type RequestPermissionResponse,
|
|
336
|
+
} from "acp-factory";
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
MIT
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentHandle - Represents a running agent with an ACP connection
|
|
3
|
+
*/
|
|
4
|
+
import * as acp from "@agentclientprotocol/sdk";
|
|
5
|
+
import type { AgentConfig, SpawnOptions, SessionOptions } from "./types.js";
|
|
6
|
+
import type { AgentCapabilities } from "@agentclientprotocol/sdk";
|
|
7
|
+
import { Session } from "./session.js";
|
|
8
|
+
/**
|
|
9
|
+
* Handle to a running agent process with ACP connection
|
|
10
|
+
*/
|
|
11
|
+
export declare class AgentHandle {
|
|
12
|
+
private readonly process;
|
|
13
|
+
private readonly connection;
|
|
14
|
+
private readonly clientHandler;
|
|
15
|
+
readonly capabilities: AgentCapabilities;
|
|
16
|
+
private constructor();
|
|
17
|
+
/**
|
|
18
|
+
* Create and initialize an agent handle
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
static create(config: AgentConfig, options: SpawnOptions): Promise<AgentHandle>;
|
|
22
|
+
/**
|
|
23
|
+
* Create a new session with the agent
|
|
24
|
+
*/
|
|
25
|
+
createSession(cwd: string, options?: SessionOptions): Promise<Session>;
|
|
26
|
+
/**
|
|
27
|
+
* Load an existing session by ID
|
|
28
|
+
*/
|
|
29
|
+
loadSession(sessionId: string, cwd: string, mcpServers?: Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
uri: string;
|
|
32
|
+
}>): Promise<Session>;
|
|
33
|
+
/**
|
|
34
|
+
* Fork an existing session to create a new independent session
|
|
35
|
+
*
|
|
36
|
+
* The forked session inherits the conversation history from the original,
|
|
37
|
+
* allowing operations without affecting the original session's state.
|
|
38
|
+
*
|
|
39
|
+
* @experimental This relies on the unstable session/fork ACP capability
|
|
40
|
+
*/
|
|
41
|
+
forkSession(sessionId: string): Promise<Session>;
|
|
42
|
+
/**
|
|
43
|
+
* Close the agent connection and terminate the process
|
|
44
|
+
*/
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Get the underlying connection (for advanced use)
|
|
48
|
+
*/
|
|
49
|
+
getConnection(): acp.ClientSideConnection;
|
|
50
|
+
/**
|
|
51
|
+
* Check if the agent process is still running
|
|
52
|
+
*/
|
|
53
|
+
isRunning(): boolean;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=agent-handle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-handle.d.ts","sourceRoot":"","sources":["../src/agent-handle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;GAEG;AACH,qBAAa,WAAW;IAIpB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IALhC,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IAEzC,OAAO;IASP;;;OAGG;WACU,MAAM,CACjB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,WAAW,CAAC;IA+EvB;;OAEG;IACG,aAAa,CACjB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC;IAuBnB;;OAEG;IACG,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAM,GACpD,OAAO,CAAC,OAAO,CAAC;IAoBnB;;;;;;;OAOG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBtD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACH,aAAa,IAAI,GAAG,CAAC,oBAAoB;IAIzC;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentHandle - Represents a running agent with an ACP connection
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { Writable, Readable } from "node:stream";
|
|
6
|
+
import * as acp from "@agentclientprotocol/sdk";
|
|
7
|
+
import { Session } from "./session.js";
|
|
8
|
+
import { ACPClientHandler } from "./client-handler.js";
|
|
9
|
+
/**
|
|
10
|
+
* Handle to a running agent process with ACP connection
|
|
11
|
+
*/
|
|
12
|
+
export class AgentHandle {
|
|
13
|
+
process;
|
|
14
|
+
connection;
|
|
15
|
+
clientHandler;
|
|
16
|
+
capabilities;
|
|
17
|
+
constructor(process, connection, clientHandler, capabilities) {
|
|
18
|
+
this.process = process;
|
|
19
|
+
this.connection = connection;
|
|
20
|
+
this.clientHandler = clientHandler;
|
|
21
|
+
this.capabilities = capabilities;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create and initialize an agent handle
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
static async create(config, options) {
|
|
28
|
+
// 1. Spawn subprocess
|
|
29
|
+
const env = {
|
|
30
|
+
...process.env,
|
|
31
|
+
...config.env,
|
|
32
|
+
...options.env,
|
|
33
|
+
};
|
|
34
|
+
const agentProcess = spawn(config.command, config.args, {
|
|
35
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
36
|
+
env,
|
|
37
|
+
});
|
|
38
|
+
// Handle process errors
|
|
39
|
+
agentProcess.on("error", (err) => {
|
|
40
|
+
console.error(`Agent process error: ${err.message}`);
|
|
41
|
+
});
|
|
42
|
+
// 2. Set up NDJSON streams
|
|
43
|
+
if (!agentProcess.stdin || !agentProcess.stdout) {
|
|
44
|
+
agentProcess.kill();
|
|
45
|
+
throw new Error("Failed to get agent process stdio streams");
|
|
46
|
+
}
|
|
47
|
+
const input = Writable.toWeb(agentProcess.stdin);
|
|
48
|
+
const output = Readable.toWeb(agentProcess.stdout);
|
|
49
|
+
const stream = acp.ndJsonStream(input, output);
|
|
50
|
+
// 3. Create client handler and connection
|
|
51
|
+
const clientHandler = new ACPClientHandler({
|
|
52
|
+
onPermissionRequest: options.onPermissionRequest,
|
|
53
|
+
onFileRead: options.onFileRead,
|
|
54
|
+
onFileWrite: options.onFileWrite,
|
|
55
|
+
onTerminalCreate: options.onTerminalCreate,
|
|
56
|
+
onTerminalOutput: options.onTerminalOutput,
|
|
57
|
+
onTerminalKill: options.onTerminalKill,
|
|
58
|
+
onTerminalRelease: options.onTerminalRelease,
|
|
59
|
+
onTerminalWaitForExit: options.onTerminalWaitForExit,
|
|
60
|
+
}, options.permissionMode ?? "auto-approve");
|
|
61
|
+
const connection = new acp.ClientSideConnection(() => clientHandler, stream);
|
|
62
|
+
// 4. Initialize connection
|
|
63
|
+
try {
|
|
64
|
+
const initResult = await connection.initialize({
|
|
65
|
+
protocolVersion: acp.PROTOCOL_VERSION,
|
|
66
|
+
clientCapabilities: {
|
|
67
|
+
fs: {
|
|
68
|
+
readTextFile: true,
|
|
69
|
+
writeTextFile: true,
|
|
70
|
+
},
|
|
71
|
+
terminal: !!(options.onTerminalCreate &&
|
|
72
|
+
options.onTerminalOutput &&
|
|
73
|
+
options.onTerminalKill &&
|
|
74
|
+
options.onTerminalRelease &&
|
|
75
|
+
options.onTerminalWaitForExit),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return new AgentHandle(agentProcess, connection, clientHandler, initResult.agentCapabilities ?? {});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
agentProcess.kill();
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a new session with the agent
|
|
87
|
+
*/
|
|
88
|
+
async createSession(cwd, options = {}) {
|
|
89
|
+
const result = await this.connection.newSession({
|
|
90
|
+
cwd,
|
|
91
|
+
mcpServers: options.mcpServers ?? [],
|
|
92
|
+
});
|
|
93
|
+
// Set mode if specified
|
|
94
|
+
if (options.mode && this.connection.setSessionMode) {
|
|
95
|
+
await this.connection.setSessionMode({
|
|
96
|
+
sessionId: result.sessionId,
|
|
97
|
+
modeId: options.mode,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return new Session(result.sessionId, this.connection, this.clientHandler, result.modes?.availableModes?.map((m) => m.id) ?? [], result.models?.availableModels?.map((m) => m.modelId) ?? []);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Load an existing session by ID
|
|
104
|
+
*/
|
|
105
|
+
async loadSession(sessionId, cwd, mcpServers = []) {
|
|
106
|
+
if (!this.capabilities.loadSession) {
|
|
107
|
+
throw new Error("Agent does not support loading sessions");
|
|
108
|
+
}
|
|
109
|
+
const result = await this.connection.loadSession({
|
|
110
|
+
sessionId,
|
|
111
|
+
cwd,
|
|
112
|
+
mcpServers,
|
|
113
|
+
});
|
|
114
|
+
return new Session(sessionId, this.connection, this.clientHandler, result.modes?.availableModes?.map((m) => m.id) ?? [], result.models?.availableModels?.map((m) => m.modelId) ?? []);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Fork an existing session to create a new independent session
|
|
118
|
+
*
|
|
119
|
+
* The forked session inherits the conversation history from the original,
|
|
120
|
+
* allowing operations without affecting the original session's state.
|
|
121
|
+
*
|
|
122
|
+
* @experimental This relies on the unstable session/fork ACP capability
|
|
123
|
+
*/
|
|
124
|
+
async forkSession(sessionId) {
|
|
125
|
+
if (!this.capabilities.sessionCapabilities?.fork) {
|
|
126
|
+
throw new Error("Agent does not support forking sessions");
|
|
127
|
+
}
|
|
128
|
+
const result = await this.connection.forkSession({
|
|
129
|
+
sessionId,
|
|
130
|
+
});
|
|
131
|
+
return new Session(result.sessionId, this.connection, this.clientHandler, result.modes?.availableModes?.map((m) => m.id) ?? [], result.models?.availableModels?.map((m) => m.modelId) ?? []);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Close the agent connection and terminate the process
|
|
135
|
+
*/
|
|
136
|
+
async close() {
|
|
137
|
+
this.process.kill();
|
|
138
|
+
// Wait for the connection to close
|
|
139
|
+
await this.connection.closed;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get the underlying connection (for advanced use)
|
|
143
|
+
*/
|
|
144
|
+
getConnection() {
|
|
145
|
+
return this.connection;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Check if the agent process is still running
|
|
149
|
+
*/
|
|
150
|
+
isRunning() {
|
|
151
|
+
return !this.process.killed && this.process.exitCode === null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=agent-handle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-handle.js","sourceRoot":"","sources":["../src/agent-handle.ts"],"names":[],"mappings":"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"}
|