@acpjs/client 0.1.0
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/README.md +128 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +511 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @acpjs/client
|
|
2
|
+
|
|
3
|
+
Typed facade and reducer-driven client-side store for acpjs. Environment-neutral (no Node built-ins) and connected to any host purely through the Transport contract (in-process, Electron renderer, …). It replays the normalized event stream on the client side with the pure reducer from `@acpjs/protocol` into `SessionState`, exposed through a snapshot + subscribe surface. The only runtime dependency is `@acpjs/protocol`; `@acpjs/core` is consumed only through transport contract types.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @acpjs/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
ESM-only, requires `node >= 24` (also usable in browser / renderer environments).
|
|
12
|
+
|
|
13
|
+
## Minimal usage (in-process Node)
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import {
|
|
17
|
+
AcpClientError,
|
|
18
|
+
createAcpClient,
|
|
19
|
+
createInProcessTransport,
|
|
20
|
+
} from '@acpjs/client'
|
|
21
|
+
import { createAcpHost, createHostEndpoint } from '@acpjs/core'
|
|
22
|
+
|
|
23
|
+
const host = createAcpHost()
|
|
24
|
+
const transport = createInProcessTransport(createHostEndpoint(host))
|
|
25
|
+
const client = createAcpClient({ transport })
|
|
26
|
+
|
|
27
|
+
const agent = await client.agents.spawn({
|
|
28
|
+
id: 'my-agent',
|
|
29
|
+
command: 'npx',
|
|
30
|
+
args: ['some-acp-agent'],
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const session = await agent.sessions.create({ cwd: process.cwd() })
|
|
34
|
+
session.subscribe(() => render(session.getSnapshot()))
|
|
35
|
+
|
|
36
|
+
client.permissions.subscribe((requests) => {
|
|
37
|
+
for (const request of requests) {
|
|
38
|
+
// Under multi-client races, a respond rejected with acpjs/already-answered
|
|
39
|
+
// is the normal path (the pending list has already converged) — ignore it.
|
|
40
|
+
request
|
|
41
|
+
.respond({
|
|
42
|
+
outcome: 'selected',
|
|
43
|
+
optionId: request.options[0]?.optionId ?? '',
|
|
44
|
+
})
|
|
45
|
+
.catch((error) => {
|
|
46
|
+
if (
|
|
47
|
+
error instanceof AcpClientError &&
|
|
48
|
+
error.code === 'acpjs/already-answered'
|
|
49
|
+
)
|
|
50
|
+
return
|
|
51
|
+
throw error
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
await session.prompt([{ type: 'text', text: 'hello' }])
|
|
57
|
+
session.getSnapshot() // cached immutable SessionState reference
|
|
58
|
+
|
|
59
|
+
// In-process: the host lifecycle is independent of the client. client.dispose()
|
|
60
|
+
// only closes the transport; you must separately await host.dispose() or the
|
|
61
|
+
// agent child processes will leak.
|
|
62
|
+
await client.dispose()
|
|
63
|
+
await host.dispose()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Public API (closed surface, SPEC §6.4)
|
|
67
|
+
|
|
68
|
+
The export surface is exactly three values: `createAcpClient`, `createInProcessTransport`, and `AcpClientError` (pinned by an API snapshot test). There is no raw RPC send, no raw protocol notification subscription, and no store selector parameter.
|
|
69
|
+
|
|
70
|
+
- `createAcpClient({ transport })` → `AcpClient`
|
|
71
|
+
- `client.agents.spawn(definition)` → `Promise<AcpAgent>`. `definition` is an `AgentDefinition` (`id`, `command`, `args?`, `env?`, `cwd?`, `meta?`). The returned handle carries `agentId` plus `capabilities?` / `authMethods?` passed through from initialize.
|
|
72
|
+
- `client.agents.get(agentId)` → `AcpAgent | undefined`: look up a known handle by id (same reference returned by spawn / attach).
|
|
73
|
+
- `client.agents.getSnapshot()` → `readonly AcpAgent[]`: cached immutable snapshot of the handle set — a new reference is produced only when the set changes (`useSyncExternalStore` compatible).
|
|
74
|
+
- `client.agents.subscribe(() => ...)`: notification when the agent handle set changes (no immediate callback; read the initial value via `getSnapshot`).
|
|
75
|
+
- `client.agents.list()` → `Promise<readonly AgentSnapshot[]>`: a one-shot RPC query of every agent known to the host (`agentId` / `status` / `restartCount` / `reason?` / `exit?` / `capabilities?` / `authMethods?` / `authRequired?`), non-reactive.
|
|
76
|
+
- `client.agents.attach(agentId)` → `Promise<AcpAgent>`: hydrate an agent that already exists on the host into a handle (internally calls `list()` to verify existence). An unknown id rejects with `acpjs/agent-exited`; an existing handle is reused.
|
|
77
|
+
- `client.sessions.get(sessionId)` → `AcpSession | undefined`: look up a known session handle by id. create / load / resume / attach for the same sessionId share one frozen handle.
|
|
78
|
+
- `client.sessions.getSnapshot()` → `readonly AcpSession[]`: cached immutable snapshot of the session handle set.
|
|
79
|
+
- `client.sessions.subscribe(() => ...)`: notification when the session handle set changes.
|
|
80
|
+
- `client.sessions.list()` → `Promise<readonly SessionSnapshot[]>`: a one-shot RPC query of every session known to the host (`sessionId` / `status` / `agentId?` / `cwd?` / `agentDefinitionId?`), non-reactive.
|
|
81
|
+
- `client.sessions.attach(sessionId)` → `Promise<AcpSession>`: re-attach to an existing session **without an agent handle** (internally calls `list()`; if present it subscribes and rebuilds state via replay from `fromSeq: 0`). An unknown id rejects with `acpjs/session-closed`.
|
|
82
|
+
- `client.sessions.restore()` → `Promise<readonly SessionSnapshot[]>`: after a host restart, rebuild `disconnected` sessions from storage and return their snapshots.
|
|
83
|
+
- `client.permissions.getSnapshot()` → `readonly PermissionRequest[]`: cached immutable reference of the pending permission-request list — a new reference is produced only when the set changes.
|
|
84
|
+
- `client.permissions.subscribe((requests) => ...)`: notification with the latest snapshot on any add/remove of the pending set (new request, respond, other-client answer / superseded); no immediate callback (read the initial value via `getSnapshot`). Each `request` carries `requestId` / `sessionId` / `toolCall` / `options` (protocol pass-through) and `respond(outcome)`.
|
|
85
|
+
- `client.status.getSnapshot()` → `ConnectionStatusSnapshot` (`{ status: 'connecting' | 'connected' | 'closed', error? }`): cached immutable snapshot of the connection status.
|
|
86
|
+
- `client.status.subscribe(() => ...)`: notification when the connection status changes (no immediate callback; read the initial value via `getSnapshot`).
|
|
87
|
+
- `client.dispose()`: close the transport; afterwards every call rejects with `acpjs/transport-closed`.
|
|
88
|
+
- `AcpAgent`
|
|
89
|
+
- `agent.agentId`, `agent.capabilities?`, `agent.authMethods?`: readonly handle properties.
|
|
90
|
+
- `agent.getSnapshot()` → `AgentSnapshot`: cached immutable snapshot of this agent's runtime state (`status` / `restartCount` / `reason?` / `exit?` / `capabilities?` / `authMethods?` / `authRequired?`). `restartCount` is driven by live `agent-status-change` events; `authRequired` / `authMethods` reflect the waiting-for-auth dimension driven by host-level `auth-required` events (SPEC §4.11, §8.2).
|
|
91
|
+
- `agent.subscribe(() => ...)`: notification when the runtime state changes (no immediate callback; read the initial value via `getSnapshot`).
|
|
92
|
+
- `agent.sessions.create({ cwd, mcpServers? })` / `load(sessionId, { cwd, mcpServers? })` / `list({ cursor?, cwd? })` / `resume(sessionId)` / `delete(sessionId)`.
|
|
93
|
+
- `agent.authenticate(methodId)` / `agent.logout()`.
|
|
94
|
+
- `AcpSession`
|
|
95
|
+
- `session.sessionId`: readonly id.
|
|
96
|
+
- `session.getSnapshot()` → `SessionState`: cached immutable reference — a new reference is produced only when a new event arrives (`useSyncExternalStore` compatible).
|
|
97
|
+
- `session.subscribe((state) => ...)`: notification when state changes (the current value is not replayed; read the initial value via `getSnapshot`).
|
|
98
|
+
- `session.prompt(ContentBlock[])` → `Promise<PromptFinishedPayload>`: protocol content blocks passed through with no rewriting (SPEC §12). Also `cancel()`, `close()`, `setMode(modeId)`, `setConfigOption(configId, value)`.
|
|
99
|
+
- Slash commands are not a separate API: `SessionState.availableCommands` only enumerates available commands for autocompletion UI. To invoke a command, write a `/`-prefixed text block in the prompt, which may be mixed with other blocks — `session.prompt([{ type: 'text', text: '/web query' }])`. There is no `invokeCommand`-style method.
|
|
100
|
+
- Every error is an `AcpClientError` (an `ErrorObject` shape: `code` (acpjs/\*), `message`, `data?`, `retryable`). A capability-gated method whose capability was not declared rejects with `acpjs/capability-unsupported` (core semantics passed through); a second answer to a permission rejects with `acpjs/already-answered`.
|
|
101
|
+
|
|
102
|
+
## State construction (SPEC §9.2)
|
|
103
|
+
|
|
104
|
+
The only client-side state-construction path: the transport receives an `AcpEvent` → the `reduce` function from `@acpjs/protocol` replays it in `seq` order. Subscriptions carry `fromSeq`; a late subscriber / reconnection is backfilled by replay and ends up deeply equal to a full-duration subscriber (INV-2). A duplicate event whose `seq` was already applied is ignored.
|
|
105
|
+
|
|
106
|
+
## Transport
|
|
107
|
+
|
|
108
|
+
The client consumes the Transport contract from `@acpjs/protocol` (`connect` / `request` / `subscribe` / `respondInbound` / `close`, with lifecycle `connecting → connected → closed` plus an error termination path). The built-in `createInProcessTransport(endpoint)` connects to `@acpjs/core`'s `createHostEndpoint(host)` (the client only sees contract types and has zero dependency on core; SPEC §6.2). Reconnection is not a transport obligation; a new connection backfills via `fromSeq`.
|
|
109
|
+
|
|
110
|
+
## Implementation-defined decisions (SPEC §15)
|
|
111
|
+
|
|
112
|
+
- **RPC id format**: `rpc-<n>` (monotonic counter within a client instance).
|
|
113
|
+
- **RPC method names**: sourced from `@acpjs/protocol`'s `ACP_RPC_METHODS` (`agents/spawn|authenticate|logout|list`, `sessions/create|load|list|resume|delete|prompt|cancel|close|setMode|setConfigOption|getAll|restore`), the same constant table consumed by core's `createHostEndpoint` (pinned by protocol and end-to-end tests).
|
|
114
|
+
- **auth-required facade shape (create-time)**: `sessions.create` does not return a union type; instead it rejects with an `AcpClientError` (`code: 'acpjs/auth-required'`, `retryable: true`, `data.authMethods` is the protocol original). After `authenticate`, the consumer retries create. In parallel the agent's runtime snapshot becomes observable as waiting-for-auth (`authRequired: true` + `authMethods`), driven by the host-level `auth-required` event.
|
|
115
|
+
- **store subscription timing**: one store per sessionId; on first acquiring a session handle (create / load / resume / attach) it subscribes from `fromSeq: 0`. Within one client the same sessionId reuses the same store, the same frozen handle, and the same subscription.
|
|
116
|
+
- **subscribe does not call back immediately**: consistent with external-store conventions (session state, agent/session registries, connection status, and the permission list all behave this way); read the initial value via `getSnapshot` / `get`.
|
|
117
|
+
- **connection status store**: `client.status` maintains a single `ConnectionStatusSnapshot`, advancing `connecting → connected → closed` with the transport lifecycle (termination may carry an `error`); entering `closed` also clears the pending permission snapshot. No duplicate notification when neither status nor error changes.
|
|
118
|
+
- **sessions.attach semantics**: does not go through an agent handle — it uses `sessions.list()` to verify the sessionId is still known to the host; if present it creates a store and rebuilds state by replaying from `fromSeq: 0`; an unknown id rejects with `acpjs/session-closed`. Suitable for re-attaching to an existing session across windows / after a page reload.
|
|
119
|
+
- **agents/sessions list/restore are one-shot RPC queries**: `agents.list()` / `sessions.list()` / `sessions.restore()` are non-reactive request/response snapshots (for enumeration and hydration), returning wire-level `AgentSnapshot` / `SessionSnapshot` without subscribing to subsequent changes; for reactive observation use the corresponding `getSnapshot` / `subscribe`.
|
|
120
|
+
- **permission-request exit**: a request leaves the pending set (and subscribers are notified with a new snapshot) when respond succeeds, when respond is rejected with `acpjs/already-answered` (someone else already answered), or when the owning session's event stream emits `permission-request-resolved` (other-client answer / superseded). A late consumer reading `getSnapshot` only sees still-pending requests. Under multi-client races, a respond rejected with `acpjs/already-answered` is the normal path — the pending list has converged and consumers should ignore that code rather than treat it as an error. Inbound requests whose kind is not `permission` are ignored (forward compatibility).
|
|
121
|
+
- **dispose semantics**: mark closed → unsubscribe all store subscriptions → clear permission subscribers and the pending permission set → `transport.close()`; idempotent.
|
|
122
|
+
- **in-process direct-call mechanism** (named in the §15 checklist): `createInProcessTransport(endpoint)` implements the Transport contract via direct function calls — `request` / `subscribe` / `respondInbound` forward straight to `@acpjs/core`'s `EnvelopeEndpoint` (provided by `createHostEndpoint(host)`), with no JSON serialization and no transport boundary. `connect` wires up inbound requests via `endpoint.onInboundRequest` and advances the lifecycle to `connected`; inbound requests (such as permissions) are handed to the client as-is. Payloads remain structured-clone safe (INV-3, asserted by end-to-end tests). After close, `request` responds with an `acpjs/transport-closed` error, `respondInbound` rejects, and `subscribe` throws an `AcpClientError` (surfacing misuse early); a repeated `connect` throws `acpjs/config-invalid`; `close` is idempotent and unsubscribes all active subscriptions. Reconnection is not part of the contract obligation; a new connection backfills state via `fromSeq`.
|
|
123
|
+
- **listener-callback exceptions**: session-state listeners and permission listeners that throw are isolated (swallowed, without interrupting dispatch to the rest of the batch; there is no client-side diagnostic channel).
|
|
124
|
+
- **connect failure**: every facade call rejects with the lifecycle error (or `acpjs/transport-closed`).
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { AcpErrorCode, AgentCapabilities, AgentDefinition, AgentSnapshotWire, AuthMethod, ContentBlock, EnvelopeEndpoint, ErrorObject, ListSessionsResponse, McpServer, PermissionRequestCreatedPayload, PromptFinishedPayload, RequestPermissionOutcome, SessionConfigOption, SessionConfigValue, SessionSnapshotWire, SessionState, Transport, TransportConnectionStatus } from "@acpjs/protocol";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type AgentSnapshot = AgentSnapshotWire;
|
|
5
|
+
type SessionSnapshot = SessionSnapshotWire;
|
|
6
|
+
interface AcpSession {
|
|
7
|
+
readonly sessionId: string;
|
|
8
|
+
getSnapshot: () => SessionState;
|
|
9
|
+
subscribe: (listener: (state: SessionState) => void) => () => void;
|
|
10
|
+
prompt: (blocks: ContentBlock[]) => Promise<PromptFinishedPayload>;
|
|
11
|
+
cancel: () => Promise<void>;
|
|
12
|
+
close: () => Promise<void>;
|
|
13
|
+
setMode: (modeId: string) => Promise<void>;
|
|
14
|
+
setConfigOption: (configId: string, value: SessionConfigValue) => Promise<SessionConfigOption[]>;
|
|
15
|
+
}
|
|
16
|
+
interface SessionCreateParams {
|
|
17
|
+
cwd: string;
|
|
18
|
+
mcpServers?: McpServer[];
|
|
19
|
+
}
|
|
20
|
+
interface SessionListParams {
|
|
21
|
+
cursor?: string;
|
|
22
|
+
cwd?: string;
|
|
23
|
+
}
|
|
24
|
+
interface AcpAgentSessions {
|
|
25
|
+
create: (params: SessionCreateParams) => Promise<AcpSession>;
|
|
26
|
+
load: (sessionId: string, params: SessionCreateParams) => Promise<AcpSession>;
|
|
27
|
+
list: (params?: SessionListParams) => Promise<ListSessionsResponse>;
|
|
28
|
+
resume: (sessionId: string) => Promise<AcpSession>;
|
|
29
|
+
delete: (sessionId: string) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
interface AcpAgent {
|
|
32
|
+
readonly agentId: string;
|
|
33
|
+
readonly capabilities?: AgentCapabilities;
|
|
34
|
+
readonly authMethods?: AuthMethod[];
|
|
35
|
+
getSnapshot: () => AgentSnapshot;
|
|
36
|
+
subscribe: (listener: ChangeListener) => () => void;
|
|
37
|
+
readonly sessions: AcpAgentSessions;
|
|
38
|
+
authenticate: (methodId: string) => Promise<void>;
|
|
39
|
+
logout: () => Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
interface PermissionRequest {
|
|
42
|
+
readonly requestId: string;
|
|
43
|
+
readonly sessionId: string;
|
|
44
|
+
readonly toolCall: PermissionRequestCreatedPayload['toolCall'];
|
|
45
|
+
readonly options: PermissionRequestCreatedPayload['options'];
|
|
46
|
+
respond: (outcome: RequestPermissionOutcome) => Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
type PermissionListener = (requests: readonly PermissionRequest[]) => void;
|
|
49
|
+
type ChangeListener = () => void;
|
|
50
|
+
interface ConnectionStatusSnapshot {
|
|
51
|
+
status: TransportConnectionStatus;
|
|
52
|
+
error?: ErrorObject;
|
|
53
|
+
}
|
|
54
|
+
interface AcpClient {
|
|
55
|
+
readonly agents: {
|
|
56
|
+
spawn: (definition: AgentDefinition) => Promise<AcpAgent>;
|
|
57
|
+
get: (agentId: string) => AcpAgent | undefined;
|
|
58
|
+
getSnapshot: () => readonly AcpAgent[];
|
|
59
|
+
subscribe: (listener: ChangeListener) => () => void;
|
|
60
|
+
list: () => Promise<readonly AgentSnapshot[]>;
|
|
61
|
+
attach: (agentId: string) => Promise<AcpAgent>;
|
|
62
|
+
};
|
|
63
|
+
readonly sessions: {
|
|
64
|
+
get: (sessionId: string) => AcpSession | undefined;
|
|
65
|
+
getSnapshot: () => readonly AcpSession[];
|
|
66
|
+
subscribe: (listener: ChangeListener) => () => void;
|
|
67
|
+
list: () => Promise<readonly SessionSnapshot[]>;
|
|
68
|
+
attach: (sessionId: string) => Promise<AcpSession>;
|
|
69
|
+
restore: () => Promise<readonly SessionSnapshot[]>;
|
|
70
|
+
};
|
|
71
|
+
readonly permissions: {
|
|
72
|
+
getSnapshot: () => readonly PermissionRequest[];
|
|
73
|
+
subscribe: (listener: PermissionListener) => () => void;
|
|
74
|
+
};
|
|
75
|
+
readonly status: {
|
|
76
|
+
getSnapshot: () => ConnectionStatusSnapshot;
|
|
77
|
+
subscribe: (listener: ChangeListener) => () => void;
|
|
78
|
+
};
|
|
79
|
+
dispose: () => Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
interface CreateAcpClientOptions {
|
|
82
|
+
transport: Transport;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/client.d.ts
|
|
86
|
+
declare function createAcpClient(options: CreateAcpClientOptions): AcpClient;
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/errors.d.ts
|
|
89
|
+
declare class AcpClientError extends Error {
|
|
90
|
+
code: AcpErrorCode;
|
|
91
|
+
data: unknown;
|
|
92
|
+
retryable: boolean;
|
|
93
|
+
constructor(error: ErrorObject);
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/in-process.d.ts
|
|
97
|
+
declare function createInProcessTransport(endpoint: EnvelopeEndpoint): Transport;
|
|
98
|
+
//#endregion
|
|
99
|
+
export { type AcpAgent, type AcpAgentSessions, type AcpClient, AcpClientError, type AcpSession, type AgentDefinition, type AgentSnapshot, type ChangeListener, type ConnectionStatusSnapshot, type CreateAcpClientOptions, type PermissionListener, type PermissionRequest, type SessionConfigValue, type SessionCreateParams, type SessionListParams, type SessionSnapshot, createAcpClient, createInProcessTransport };
|
|
100
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/client.ts","../src/errors.ts","../src/in-process.ts"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { ACP_ERROR_CODES, ACP_RPC_METHODS, createInitialSessionState, isAcpErrorCode, reduce } from "@acpjs/protocol";
|
|
2
|
+
//#region src/errors.ts
|
|
3
|
+
var AcpClientError = class extends Error {
|
|
4
|
+
code;
|
|
5
|
+
data;
|
|
6
|
+
retryable;
|
|
7
|
+
constructor(error) {
|
|
8
|
+
super(error.message);
|
|
9
|
+
this.name = "AcpClientError";
|
|
10
|
+
this.code = error.code;
|
|
11
|
+
this.data = error.data;
|
|
12
|
+
this.retryable = error.retryable;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function transportClosedError() {
|
|
16
|
+
return {
|
|
17
|
+
code: ACP_ERROR_CODES.transportClosed,
|
|
18
|
+
message: "transport is closed",
|
|
19
|
+
retryable: true
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function asErrorObject(value) {
|
|
23
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
24
|
+
const candidate = value;
|
|
25
|
+
if (typeof candidate.code !== "string" || !isAcpErrorCode(candidate.code)) return;
|
|
26
|
+
return {
|
|
27
|
+
code: candidate.code,
|
|
28
|
+
message: typeof candidate.message === "string" ? candidate.message : "",
|
|
29
|
+
...candidate.data === void 0 ? {} : { data: candidate.data },
|
|
30
|
+
retryable: candidate.retryable === true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function toClientError(value) {
|
|
34
|
+
if (value instanceof AcpClientError) return value;
|
|
35
|
+
const errorObject = asErrorObject(value);
|
|
36
|
+
if (errorObject) return new AcpClientError(errorObject);
|
|
37
|
+
const message = value instanceof Error ? value.message : typeof value === "string" ? value : "unknown error";
|
|
38
|
+
return new AcpClientError({
|
|
39
|
+
code: ACP_ERROR_CODES.agentError,
|
|
40
|
+
message,
|
|
41
|
+
retryable: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/internal.ts
|
|
46
|
+
function notifyChange(listeners) {
|
|
47
|
+
for (const listener of listeners) try {
|
|
48
|
+
listener();
|
|
49
|
+
} catch {}
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/agent-handle.ts
|
|
53
|
+
function sameAuthMethods(current, next) {
|
|
54
|
+
if (current === void 0) return next.length === 0;
|
|
55
|
+
if (current.length !== next.length) return false;
|
|
56
|
+
return current.every((method, index) => method.id === next[index]?.id);
|
|
57
|
+
}
|
|
58
|
+
function sameRuntimeState(current, payload) {
|
|
59
|
+
return current.status === payload.status && current.restartCount === payload.restartCount && current.reason === payload.reason && current.exit?.code === payload.exit?.code && current.exit?.signal === payload.exit?.signal;
|
|
60
|
+
}
|
|
61
|
+
function createAgentHandle(call, openSession, onStatusChanged, snapshot) {
|
|
62
|
+
const agentId = snapshot.agentId;
|
|
63
|
+
let current = snapshot;
|
|
64
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
65
|
+
return {
|
|
66
|
+
agent: Object.freeze({
|
|
67
|
+
agentId,
|
|
68
|
+
...snapshot.capabilities === void 0 ? {} : { capabilities: snapshot.capabilities },
|
|
69
|
+
...snapshot.authMethods === void 0 ? {} : { authMethods: snapshot.authMethods },
|
|
70
|
+
getSnapshot: () => current,
|
|
71
|
+
subscribe(listener) {
|
|
72
|
+
listeners.add(listener);
|
|
73
|
+
return () => listeners.delete(listener);
|
|
74
|
+
},
|
|
75
|
+
sessions: Object.freeze({
|
|
76
|
+
async create(params) {
|
|
77
|
+
const result = await call(ACP_RPC_METHODS.createSession, {
|
|
78
|
+
agentId,
|
|
79
|
+
cwd: params.cwd,
|
|
80
|
+
...params.mcpServers === void 0 ? {} : { mcpServers: params.mcpServers }
|
|
81
|
+
});
|
|
82
|
+
if (result.status === "auth-required") throw new AcpClientError({
|
|
83
|
+
code: "acpjs/auth-required",
|
|
84
|
+
message: "agent requires authentication",
|
|
85
|
+
data: { authMethods: result.authMethods },
|
|
86
|
+
retryable: true
|
|
87
|
+
});
|
|
88
|
+
return openSession(result.sessionId);
|
|
89
|
+
},
|
|
90
|
+
async load(sessionId, params) {
|
|
91
|
+
await call(ACP_RPC_METHODS.loadSession, {
|
|
92
|
+
agentId,
|
|
93
|
+
sessionId,
|
|
94
|
+
cwd: params.cwd,
|
|
95
|
+
...params.mcpServers === void 0 ? {} : { mcpServers: params.mcpServers }
|
|
96
|
+
});
|
|
97
|
+
return openSession(sessionId);
|
|
98
|
+
},
|
|
99
|
+
async list(params = {}) {
|
|
100
|
+
return await call(ACP_RPC_METHODS.listSessions, {
|
|
101
|
+
agentId,
|
|
102
|
+
...params.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
103
|
+
...params.cwd === void 0 ? {} : { cwd: params.cwd }
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
async resume(sessionId) {
|
|
107
|
+
await call(ACP_RPC_METHODS.resumeSession, { sessionId });
|
|
108
|
+
return openSession(sessionId);
|
|
109
|
+
},
|
|
110
|
+
async delete(sessionId) {
|
|
111
|
+
await call(ACP_RPC_METHODS.deleteSession, { sessionId });
|
|
112
|
+
}
|
|
113
|
+
}),
|
|
114
|
+
async authenticate(methodId) {
|
|
115
|
+
await call(ACP_RPC_METHODS.authenticate, {
|
|
116
|
+
agentId,
|
|
117
|
+
methodId
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
async logout() {
|
|
121
|
+
await call(ACP_RPC_METHODS.logout, { agentId });
|
|
122
|
+
}
|
|
123
|
+
}),
|
|
124
|
+
applyStatus(payload) {
|
|
125
|
+
if (sameRuntimeState(current, payload)) return;
|
|
126
|
+
current = {
|
|
127
|
+
agentId,
|
|
128
|
+
status: payload.status,
|
|
129
|
+
restartCount: payload.restartCount,
|
|
130
|
+
...payload.reason === void 0 ? {} : { reason: payload.reason },
|
|
131
|
+
...payload.exit === void 0 ? {} : { exit: payload.exit },
|
|
132
|
+
...current.capabilities === void 0 ? {} : { capabilities: current.capabilities },
|
|
133
|
+
...current.authMethods === void 0 ? {} : { authMethods: current.authMethods },
|
|
134
|
+
...current.authRequired === void 0 ? {} : { authRequired: current.authRequired }
|
|
135
|
+
};
|
|
136
|
+
notifyChange(listeners);
|
|
137
|
+
onStatusChanged();
|
|
138
|
+
},
|
|
139
|
+
applyAuthRequired(payload) {
|
|
140
|
+
if (current.authRequired === true && sameAuthMethods(current.authMethods, payload.authMethods)) return;
|
|
141
|
+
current = {
|
|
142
|
+
...current,
|
|
143
|
+
authRequired: true,
|
|
144
|
+
authMethods: payload.authMethods
|
|
145
|
+
};
|
|
146
|
+
notifyChange(listeners);
|
|
147
|
+
onStatusChanged();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/permission-registry.ts
|
|
153
|
+
function createPermissionRegistry() {
|
|
154
|
+
const pending = /* @__PURE__ */ new Map();
|
|
155
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
156
|
+
let snapshot = Object.freeze([]);
|
|
157
|
+
function publish() {
|
|
158
|
+
snapshot = Object.freeze([...pending.values()]);
|
|
159
|
+
for (const listener of listeners) try {
|
|
160
|
+
listener(snapshot);
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
getSnapshot: () => snapshot,
|
|
165
|
+
subscribe(listener) {
|
|
166
|
+
listeners.add(listener);
|
|
167
|
+
return () => listeners.delete(listener);
|
|
168
|
+
},
|
|
169
|
+
add(request) {
|
|
170
|
+
pending.set(request.requestId, request);
|
|
171
|
+
publish();
|
|
172
|
+
},
|
|
173
|
+
prune(requestId) {
|
|
174
|
+
if (pending.delete(requestId)) publish();
|
|
175
|
+
},
|
|
176
|
+
reset() {
|
|
177
|
+
if (pending.size === 0) return;
|
|
178
|
+
pending.clear();
|
|
179
|
+
publish();
|
|
180
|
+
},
|
|
181
|
+
clear() {
|
|
182
|
+
pending.clear();
|
|
183
|
+
publish();
|
|
184
|
+
listeners.clear();
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/session-handle.ts
|
|
190
|
+
function createSessionHandle(call, store) {
|
|
191
|
+
const sessionId = store.sessionId;
|
|
192
|
+
return Object.freeze({
|
|
193
|
+
sessionId,
|
|
194
|
+
getSnapshot: () => store.getSnapshot(),
|
|
195
|
+
subscribe: (listener) => store.subscribe(listener),
|
|
196
|
+
async prompt(blocks) {
|
|
197
|
+
return await call(ACP_RPC_METHODS.prompt, {
|
|
198
|
+
sessionId,
|
|
199
|
+
prompt: blocks
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
async cancel() {
|
|
203
|
+
await call(ACP_RPC_METHODS.cancel, { sessionId });
|
|
204
|
+
},
|
|
205
|
+
async close() {
|
|
206
|
+
await call(ACP_RPC_METHODS.closeSession, { sessionId });
|
|
207
|
+
},
|
|
208
|
+
async setMode(modeId) {
|
|
209
|
+
await call(ACP_RPC_METHODS.setMode, {
|
|
210
|
+
sessionId,
|
|
211
|
+
modeId
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
async setConfigOption(configId, value) {
|
|
215
|
+
return await call(ACP_RPC_METHODS.setConfigOption, {
|
|
216
|
+
sessionId,
|
|
217
|
+
configId,
|
|
218
|
+
value
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/store.ts
|
|
225
|
+
function createSessionStore(sessionId) {
|
|
226
|
+
let state = createInitialSessionState(sessionId);
|
|
227
|
+
let lastSeq = 0;
|
|
228
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
229
|
+
return {
|
|
230
|
+
sessionId,
|
|
231
|
+
getSnapshot: () => state,
|
|
232
|
+
subscribe(listener) {
|
|
233
|
+
listeners.add(listener);
|
|
234
|
+
return () => listeners.delete(listener);
|
|
235
|
+
},
|
|
236
|
+
apply(event) {
|
|
237
|
+
if (!("sessionId" in event) || event.sessionId !== sessionId) return;
|
|
238
|
+
if (event.seq <= lastSeq) return;
|
|
239
|
+
lastSeq = event.seq;
|
|
240
|
+
const next = reduce(state, event);
|
|
241
|
+
if (next === state) return;
|
|
242
|
+
state = next;
|
|
243
|
+
for (const listener of listeners) try {
|
|
244
|
+
listener(state);
|
|
245
|
+
} catch {}
|
|
246
|
+
},
|
|
247
|
+
lastSeq: () => lastSeq
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/client.ts
|
|
252
|
+
function createAcpClient(options) {
|
|
253
|
+
const transport = options.transport;
|
|
254
|
+
let closedError;
|
|
255
|
+
const stores = /* @__PURE__ */ new Map();
|
|
256
|
+
const storeUnsubscribers = /* @__PURE__ */ new Set();
|
|
257
|
+
const agents = /* @__PURE__ */ new Map();
|
|
258
|
+
const agentListeners = /* @__PURE__ */ new Set();
|
|
259
|
+
const agentUpdaters = /* @__PURE__ */ new Map();
|
|
260
|
+
let agentsSnapshot = Object.freeze([]);
|
|
261
|
+
function publishAgents() {
|
|
262
|
+
agentsSnapshot = Object.freeze([...agents.values()]);
|
|
263
|
+
notifyChange(agentListeners);
|
|
264
|
+
}
|
|
265
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
266
|
+
const sessionListeners = /* @__PURE__ */ new Set();
|
|
267
|
+
let sessionsSnapshot = Object.freeze([]);
|
|
268
|
+
function publishSessions() {
|
|
269
|
+
sessionsSnapshot = Object.freeze([...sessions.values()]);
|
|
270
|
+
notifyChange(sessionListeners);
|
|
271
|
+
}
|
|
272
|
+
const permissions = createPermissionRegistry();
|
|
273
|
+
let statusSnapshot = Object.freeze({ status: "connecting" });
|
|
274
|
+
const statusListeners = /* @__PURE__ */ new Set();
|
|
275
|
+
function ensureOpen() {
|
|
276
|
+
if (closedError) throw new AcpClientError(closedError);
|
|
277
|
+
}
|
|
278
|
+
const handlers = {
|
|
279
|
+
onInboundRequest(request) {
|
|
280
|
+
if (request.kind !== "permission") return;
|
|
281
|
+
const payload = request.payload;
|
|
282
|
+
const permission = Object.freeze({
|
|
283
|
+
requestId: payload.requestId,
|
|
284
|
+
sessionId: payload.sessionId,
|
|
285
|
+
toolCall: payload.toolCall,
|
|
286
|
+
options: payload.options,
|
|
287
|
+
respond: (outcome) => respondPermission(request.id, payload.requestId, outcome)
|
|
288
|
+
});
|
|
289
|
+
permissions.add(permission);
|
|
290
|
+
},
|
|
291
|
+
onLifecycle(event) {
|
|
292
|
+
if (event.status === "closed") {
|
|
293
|
+
closedError = event.error ?? transportClosedError();
|
|
294
|
+
permissions.reset();
|
|
295
|
+
}
|
|
296
|
+
if (statusSnapshot.status === event.status && statusSnapshot.error === event.error) return;
|
|
297
|
+
statusSnapshot = Object.freeze({
|
|
298
|
+
status: event.status,
|
|
299
|
+
...event.error === void 0 ? {} : { error: event.error }
|
|
300
|
+
});
|
|
301
|
+
notifyChange(statusListeners);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
function onHostEvent(event) {
|
|
305
|
+
if (event.type === "agent-status-change") agentUpdaters.get(event.agentId)?.applyStatus(event.payload);
|
|
306
|
+
else if (event.type === "auth-required") agentUpdaters.get(event.agentId)?.applyAuthRequired(event.payload);
|
|
307
|
+
else if (event.type === "session-created" || event.type === "session-closed") notifyChange(sessionListeners);
|
|
308
|
+
}
|
|
309
|
+
const connected = transport.connect(handlers);
|
|
310
|
+
connected.then(() => {
|
|
311
|
+
if (closedError) return;
|
|
312
|
+
storeUnsubscribers.add(transport.subscribe({ fromSeq: 0 }, onHostEvent));
|
|
313
|
+
}).catch(() => {});
|
|
314
|
+
async function respondPermission(inboundId, requestId, outcome) {
|
|
315
|
+
ensureOpen();
|
|
316
|
+
try {
|
|
317
|
+
await connected;
|
|
318
|
+
await transport.respondInbound({
|
|
319
|
+
id: inboundId,
|
|
320
|
+
result: outcome
|
|
321
|
+
});
|
|
322
|
+
} catch (error) {
|
|
323
|
+
const clientError = toClientError(error);
|
|
324
|
+
if (clientError.code === ACP_ERROR_CODES.alreadyAnswered) permissions.prune(requestId);
|
|
325
|
+
throw clientError;
|
|
326
|
+
}
|
|
327
|
+
permissions.prune(requestId);
|
|
328
|
+
}
|
|
329
|
+
let rpcCounter = 0;
|
|
330
|
+
async function call(method, params) {
|
|
331
|
+
ensureOpen();
|
|
332
|
+
try {
|
|
333
|
+
await connected;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
throw toClientError(error);
|
|
336
|
+
}
|
|
337
|
+
ensureOpen();
|
|
338
|
+
rpcCounter += 1;
|
|
339
|
+
let response;
|
|
340
|
+
try {
|
|
341
|
+
response = await transport.request({
|
|
342
|
+
id: `rpc-${rpcCounter}`,
|
|
343
|
+
method,
|
|
344
|
+
params
|
|
345
|
+
});
|
|
346
|
+
} catch (error) {
|
|
347
|
+
throw toClientError(error);
|
|
348
|
+
}
|
|
349
|
+
if (!response.ok) throw new AcpClientError(response.error);
|
|
350
|
+
return response.result;
|
|
351
|
+
}
|
|
352
|
+
function attachStore(sessionId) {
|
|
353
|
+
const existing = stores.get(sessionId);
|
|
354
|
+
if (existing) return existing;
|
|
355
|
+
const store = createSessionStore(sessionId);
|
|
356
|
+
stores.set(sessionId, store);
|
|
357
|
+
const unsubscribe = transport.subscribe({
|
|
358
|
+
sessionId,
|
|
359
|
+
fromSeq: store.lastSeq()
|
|
360
|
+
}, (event) => {
|
|
361
|
+
if (event.type === "permission-request-resolved") permissions.prune(event.payload.requestId);
|
|
362
|
+
store.apply(event);
|
|
363
|
+
});
|
|
364
|
+
storeUnsubscribers.add(unsubscribe);
|
|
365
|
+
return store;
|
|
366
|
+
}
|
|
367
|
+
function openSession(sessionId) {
|
|
368
|
+
const existing = sessions.get(sessionId);
|
|
369
|
+
if (existing) return existing;
|
|
370
|
+
const session = createSessionHandle(call, attachStore(sessionId));
|
|
371
|
+
sessions.set(sessionId, session);
|
|
372
|
+
publishSessions();
|
|
373
|
+
return session;
|
|
374
|
+
}
|
|
375
|
+
async function listAgents() {
|
|
376
|
+
return await call(ACP_RPC_METHODS.listAgents, {});
|
|
377
|
+
}
|
|
378
|
+
async function listAllSessions() {
|
|
379
|
+
return await call(ACP_RPC_METHODS.getAllSessions, {});
|
|
380
|
+
}
|
|
381
|
+
function registerAgent(snapshot) {
|
|
382
|
+
const { agent, applyStatus, applyAuthRequired } = createAgentHandle(call, openSession, publishAgents, snapshot);
|
|
383
|
+
agentUpdaters.set(agent.agentId, {
|
|
384
|
+
applyStatus,
|
|
385
|
+
applyAuthRequired
|
|
386
|
+
});
|
|
387
|
+
agents.set(agent.agentId, agent);
|
|
388
|
+
publishAgents();
|
|
389
|
+
return agent;
|
|
390
|
+
}
|
|
391
|
+
return Object.freeze({
|
|
392
|
+
agents: Object.freeze({
|
|
393
|
+
async spawn(definition) {
|
|
394
|
+
return registerAgent(await call(ACP_RPC_METHODS.spawnAgent, { definition }));
|
|
395
|
+
},
|
|
396
|
+
get: (agentId) => agents.get(agentId),
|
|
397
|
+
getSnapshot: () => agentsSnapshot,
|
|
398
|
+
subscribe(listener) {
|
|
399
|
+
agentListeners.add(listener);
|
|
400
|
+
return () => agentListeners.delete(listener);
|
|
401
|
+
},
|
|
402
|
+
list: listAgents,
|
|
403
|
+
async attach(agentId) {
|
|
404
|
+
const snapshot = (await listAgents()).find((candidate) => candidate.agentId === agentId);
|
|
405
|
+
if (!snapshot) throw new AcpClientError({
|
|
406
|
+
code: ACP_ERROR_CODES.agentExited,
|
|
407
|
+
message: `agent ${agentId} is not known to the host`,
|
|
408
|
+
retryable: false
|
|
409
|
+
});
|
|
410
|
+
return agents.get(agentId) ?? registerAgent(snapshot);
|
|
411
|
+
}
|
|
412
|
+
}),
|
|
413
|
+
sessions: Object.freeze({
|
|
414
|
+
get: (sessionId) => sessions.get(sessionId),
|
|
415
|
+
getSnapshot: () => sessionsSnapshot,
|
|
416
|
+
subscribe(listener) {
|
|
417
|
+
sessionListeners.add(listener);
|
|
418
|
+
return () => sessionListeners.delete(listener);
|
|
419
|
+
},
|
|
420
|
+
list: listAllSessions,
|
|
421
|
+
async attach(sessionId) {
|
|
422
|
+
if (!(await listAllSessions()).some((candidate) => candidate.sessionId === sessionId)) throw new AcpClientError({
|
|
423
|
+
code: ACP_ERROR_CODES.sessionClosed,
|
|
424
|
+
message: `session ${sessionId} is not known to the host`,
|
|
425
|
+
retryable: false
|
|
426
|
+
});
|
|
427
|
+
return openSession(sessionId);
|
|
428
|
+
},
|
|
429
|
+
async restore() {
|
|
430
|
+
return await call(ACP_RPC_METHODS.restoreSessions, {});
|
|
431
|
+
}
|
|
432
|
+
}),
|
|
433
|
+
permissions: Object.freeze({
|
|
434
|
+
getSnapshot: permissions.getSnapshot,
|
|
435
|
+
subscribe: permissions.subscribe
|
|
436
|
+
}),
|
|
437
|
+
status: Object.freeze({
|
|
438
|
+
getSnapshot: () => statusSnapshot,
|
|
439
|
+
subscribe(listener) {
|
|
440
|
+
statusListeners.add(listener);
|
|
441
|
+
return () => statusListeners.delete(listener);
|
|
442
|
+
}
|
|
443
|
+
}),
|
|
444
|
+
async dispose() {
|
|
445
|
+
if (closedError) return;
|
|
446
|
+
closedError = transportClosedError();
|
|
447
|
+
for (const unsubscribe of storeUnsubscribers) unsubscribe();
|
|
448
|
+
storeUnsubscribers.clear();
|
|
449
|
+
permissions.clear();
|
|
450
|
+
await transport.close();
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/in-process.ts
|
|
456
|
+
function createInProcessTransport(endpoint) {
|
|
457
|
+
let status = "connecting";
|
|
458
|
+
let handlers;
|
|
459
|
+
const unsubscribers = /* @__PURE__ */ new Set();
|
|
460
|
+
let detachInbound;
|
|
461
|
+
return {
|
|
462
|
+
async connect(next) {
|
|
463
|
+
if (status === "closed") throw new AcpClientError(transportClosedError());
|
|
464
|
+
if (handlers) throw new AcpClientError({
|
|
465
|
+
code: ACP_ERROR_CODES.configInvalid,
|
|
466
|
+
message: "transport already connected",
|
|
467
|
+
retryable: false
|
|
468
|
+
});
|
|
469
|
+
handlers = next;
|
|
470
|
+
next.onLifecycle({ status: "connecting" });
|
|
471
|
+
detachInbound = endpoint.onInboundRequest((request) => {
|
|
472
|
+
next.onInboundRequest(request);
|
|
473
|
+
});
|
|
474
|
+
status = "connected";
|
|
475
|
+
next.onLifecycle({ status: "connected" });
|
|
476
|
+
},
|
|
477
|
+
async request(request) {
|
|
478
|
+
if (status !== "connected") return {
|
|
479
|
+
id: request.id,
|
|
480
|
+
ok: false,
|
|
481
|
+
error: transportClosedError()
|
|
482
|
+
};
|
|
483
|
+
return endpoint.request(request);
|
|
484
|
+
},
|
|
485
|
+
subscribe(params, onEvent) {
|
|
486
|
+
if (status !== "connected") throw new AcpClientError(transportClosedError());
|
|
487
|
+
const unsubscribe = endpoint.subscribe(params, onEvent);
|
|
488
|
+
unsubscribers.add(unsubscribe);
|
|
489
|
+
return () => {
|
|
490
|
+
unsubscribers.delete(unsubscribe);
|
|
491
|
+
unsubscribe();
|
|
492
|
+
};
|
|
493
|
+
},
|
|
494
|
+
async respondInbound(response) {
|
|
495
|
+
if (status !== "connected") throw new AcpClientError(transportClosedError());
|
|
496
|
+
await endpoint.respondInbound(response);
|
|
497
|
+
},
|
|
498
|
+
async close() {
|
|
499
|
+
if (status === "closed") return;
|
|
500
|
+
status = "closed";
|
|
501
|
+
detachInbound?.();
|
|
502
|
+
for (const unsubscribe of unsubscribers) unsubscribe();
|
|
503
|
+
unsubscribers.clear();
|
|
504
|
+
handlers?.onLifecycle({ status: "closed" });
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
export { AcpClientError, createAcpClient, createInProcessTransport };
|
|
510
|
+
|
|
511
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/internal.ts","../src/agent-handle.ts","../src/permission-registry.ts","../src/session-handle.ts","../src/store.ts","../src/client.ts","../src/in-process.ts"],"sourcesContent":["import {\n ACP_ERROR_CODES,\n isAcpErrorCode,\n type AcpErrorCode,\n type ErrorObject,\n} from '@acpjs/protocol'\n\nexport class AcpClientError extends Error {\n code: AcpErrorCode\n data: unknown\n retryable: boolean\n\n constructor(error: ErrorObject) {\n super(error.message)\n this.name = 'AcpClientError'\n this.code = error.code\n this.data = error.data\n this.retryable = error.retryable\n }\n}\n\nexport function transportClosedError(): ErrorObject {\n return {\n code: ACP_ERROR_CODES.transportClosed,\n message: 'transport is closed',\n retryable: true,\n }\n}\n\nfunction asErrorObject(value: unknown): ErrorObject | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const candidate = value as {\n code?: unknown\n message?: unknown\n data?: unknown\n retryable?: unknown\n }\n if (typeof candidate.code !== 'string' || !isAcpErrorCode(candidate.code)) {\n return undefined\n }\n return {\n code: candidate.code,\n message: typeof candidate.message === 'string' ? candidate.message : '',\n ...(candidate.data === undefined ? {} : { data: candidate.data }),\n retryable: candidate.retryable === true,\n }\n}\n\nexport function toClientError(value: unknown): AcpClientError {\n if (value instanceof AcpClientError) return value\n const errorObject = asErrorObject(value)\n if (errorObject) return new AcpClientError(errorObject)\n const message =\n value instanceof Error\n ? value.message\n : typeof value === 'string'\n ? value\n : 'unknown error'\n return new AcpClientError({\n code: ACP_ERROR_CODES.agentError,\n message,\n retryable: false,\n })\n}\n","import type { ChangeListener } from './types.ts'\n\nexport type RpcCall = (\n method: string,\n params: Record<string, unknown>,\n) => Promise<unknown>\n\nexport function notifyChange(listeners: ReadonlySet<ChangeListener>): void {\n for (const listener of listeners) {\n try {\n listener()\n } catch {}\n }\n}\n","import {\n ACP_RPC_METHODS,\n type AgentSnapshotWire,\n type AgentStatusChangePayload,\n type AuthRequiredPayload,\n type CreateSessionResult,\n type ListSessionsResponse,\n} from '@acpjs/protocol'\n\nimport { AcpClientError } from './errors.ts'\nimport { notifyChange, type RpcCall } from './internal.ts'\n\nimport type {\n AcpAgent,\n AcpSession,\n ChangeListener,\n SessionCreateParams,\n SessionListParams,\n} from './types.ts'\n\nexport interface AgentHandle {\n agent: AcpAgent\n applyStatus: (payload: AgentStatusChangePayload) => void\n applyAuthRequired: (payload: AuthRequiredPayload) => void\n}\n\nfunction sameAuthMethods(\n current: AgentSnapshotWire['authMethods'],\n next: AuthRequiredPayload['authMethods'],\n): boolean {\n if (current === undefined) return next.length === 0\n if (current.length !== next.length) return false\n return current.every((method, index) => method.id === next[index]?.id)\n}\n\nfunction sameRuntimeState(\n current: AgentSnapshotWire,\n payload: AgentStatusChangePayload,\n): boolean {\n return (\n current.status === payload.status &&\n current.restartCount === payload.restartCount &&\n current.reason === payload.reason &&\n current.exit?.code === payload.exit?.code &&\n current.exit?.signal === payload.exit?.signal\n )\n}\n\nexport function createAgentHandle(\n call: RpcCall,\n openSession: (sessionId: string) => AcpSession,\n onStatusChanged: () => void,\n snapshot: AgentSnapshotWire,\n): AgentHandle {\n const agentId = snapshot.agentId\n let current = snapshot\n const listeners = new Set<ChangeListener>()\n const agent: AcpAgent = Object.freeze({\n agentId,\n ...(snapshot.capabilities === undefined\n ? {}\n : { capabilities: snapshot.capabilities }),\n ...(snapshot.authMethods === undefined\n ? {}\n : { authMethods: snapshot.authMethods }),\n getSnapshot: () => current,\n subscribe(listener: ChangeListener): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n sessions: Object.freeze({\n async create(params: SessionCreateParams): Promise<AcpSession> {\n const result = (await call(ACP_RPC_METHODS.createSession, {\n agentId,\n cwd: params.cwd,\n ...(params.mcpServers === undefined\n ? {}\n : { mcpServers: params.mcpServers }),\n })) as CreateSessionResult\n if (result.status === 'auth-required') {\n throw new AcpClientError({\n code: 'acpjs/auth-required',\n message: 'agent requires authentication',\n data: { authMethods: result.authMethods },\n retryable: true,\n })\n }\n return openSession(result.sessionId)\n },\n async load(\n sessionId: string,\n params: SessionCreateParams,\n ): Promise<AcpSession> {\n await call(ACP_RPC_METHODS.loadSession, {\n agentId,\n sessionId,\n cwd: params.cwd,\n ...(params.mcpServers === undefined\n ? {}\n : { mcpServers: params.mcpServers }),\n })\n return openSession(sessionId)\n },\n async list(\n params: SessionListParams = {},\n ): Promise<ListSessionsResponse> {\n return (await call(ACP_RPC_METHODS.listSessions, {\n agentId,\n ...(params.cursor === undefined ? {} : { cursor: params.cursor }),\n ...(params.cwd === undefined ? {} : { cwd: params.cwd }),\n })) as ListSessionsResponse\n },\n async resume(sessionId: string): Promise<AcpSession> {\n await call(ACP_RPC_METHODS.resumeSession, { sessionId })\n return openSession(sessionId)\n },\n async delete(sessionId: string): Promise<void> {\n await call(ACP_RPC_METHODS.deleteSession, { sessionId })\n },\n }),\n async authenticate(methodId: string): Promise<void> {\n await call(ACP_RPC_METHODS.authenticate, { agentId, methodId })\n },\n async logout(): Promise<void> {\n await call(ACP_RPC_METHODS.logout, { agentId })\n },\n })\n return {\n agent,\n applyStatus(payload: AgentStatusChangePayload): void {\n if (sameRuntimeState(current, payload)) return\n current = {\n agentId,\n status: payload.status,\n restartCount: payload.restartCount,\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n ...(payload.exit === undefined ? {} : { exit: payload.exit }),\n ...(current.capabilities === undefined\n ? {}\n : { capabilities: current.capabilities }),\n ...(current.authMethods === undefined\n ? {}\n : { authMethods: current.authMethods }),\n ...(current.authRequired === undefined\n ? {}\n : { authRequired: current.authRequired }),\n }\n notifyChange(listeners)\n onStatusChanged()\n },\n applyAuthRequired(payload: AuthRequiredPayload): void {\n if (\n current.authRequired === true &&\n sameAuthMethods(current.authMethods, payload.authMethods)\n ) {\n return\n }\n current = {\n ...current,\n authRequired: true,\n authMethods: payload.authMethods,\n }\n notifyChange(listeners)\n onStatusChanged()\n },\n }\n}\n","import type { PermissionListener, PermissionRequest } from './types.ts'\n\nexport interface PermissionRegistry {\n getSnapshot: () => readonly PermissionRequest[]\n subscribe: (listener: PermissionListener) => () => void\n add: (request: PermissionRequest) => void\n prune: (requestId: string) => void\n reset: () => void\n clear: () => void\n}\n\nexport function createPermissionRegistry(): PermissionRegistry {\n const pending = new Map<string, PermissionRequest>()\n const listeners = new Set<PermissionListener>()\n let snapshot: readonly PermissionRequest[] = Object.freeze([])\n\n function publish(): void {\n snapshot = Object.freeze([...pending.values()])\n for (const listener of listeners) {\n try {\n listener(snapshot)\n } catch {}\n }\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n add(request) {\n pending.set(request.requestId, request)\n publish()\n },\n prune(requestId) {\n if (pending.delete(requestId)) publish()\n },\n reset() {\n if (pending.size === 0) return\n pending.clear()\n publish()\n },\n clear() {\n pending.clear()\n publish()\n listeners.clear()\n },\n }\n}\n","import {\n ACP_RPC_METHODS,\n type ContentBlock,\n type PromptFinishedPayload,\n type SessionConfigOption,\n} from '@acpjs/protocol'\n\nimport type { RpcCall } from './internal.ts'\nimport type { SessionStore, StateListener } from './store.ts'\nimport type { AcpSession, SessionConfigValue } from './types.ts'\n\nexport function createSessionHandle(\n call: RpcCall,\n store: SessionStore,\n): AcpSession {\n const sessionId = store.sessionId\n return Object.freeze({\n sessionId,\n getSnapshot: () => store.getSnapshot(),\n subscribe: (listener: StateListener) => store.subscribe(listener),\n async prompt(blocks: ContentBlock[]): Promise<PromptFinishedPayload> {\n return (await call(ACP_RPC_METHODS.prompt, {\n sessionId,\n prompt: blocks,\n })) as PromptFinishedPayload\n },\n async cancel(): Promise<void> {\n await call(ACP_RPC_METHODS.cancel, { sessionId })\n },\n async close(): Promise<void> {\n await call(ACP_RPC_METHODS.closeSession, { sessionId })\n },\n async setMode(modeId: string): Promise<void> {\n await call(ACP_RPC_METHODS.setMode, { sessionId, modeId })\n },\n async setConfigOption(\n configId: string,\n value: SessionConfigValue,\n ): Promise<SessionConfigOption[]> {\n return (await call(ACP_RPC_METHODS.setConfigOption, {\n sessionId,\n configId,\n value,\n })) as SessionConfigOption[]\n },\n })\n}\n","import {\n createInitialSessionState,\n reduce,\n type AcpEvent,\n type SessionState,\n} from '@acpjs/protocol'\n\nexport type StateListener = (state: SessionState) => void\n\nexport interface SessionStore {\n readonly sessionId: string\n getSnapshot: () => SessionState\n subscribe: (listener: StateListener) => () => void\n apply: (event: AcpEvent) => void\n lastSeq: () => number\n}\n\nexport function createSessionStore(sessionId: string): SessionStore {\n let state = createInitialSessionState(sessionId)\n let lastSeq = 0\n const listeners = new Set<StateListener>()\n return {\n sessionId,\n getSnapshot: () => state,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n apply(event) {\n if (!('sessionId' in event) || event.sessionId !== sessionId) return\n if (event.seq <= lastSeq) return\n lastSeq = event.seq\n const next = reduce(state, event)\n if (next === state) return\n state = next\n for (const listener of listeners) {\n try {\n listener(state)\n } catch {}\n }\n },\n lastSeq: () => lastSeq,\n }\n}\n","import {\n ACP_ERROR_CODES,\n ACP_RPC_METHODS,\n type AcpEvent,\n type AgentSnapshotWire,\n type AgentStatusChangePayload,\n type AuthRequiredPayload,\n type ErrorObject,\n type PermissionRequestCreatedPayload,\n type RequestPermissionOutcome,\n type SessionSnapshotWire,\n type TransportHandlers,\n} from '@acpjs/protocol'\n\nimport { createAgentHandle } from './agent-handle.ts'\nimport {\n AcpClientError,\n toClientError,\n transportClosedError,\n} from './errors.ts'\nimport { notifyChange } from './internal.ts'\nimport { createPermissionRegistry } from './permission-registry.ts'\nimport { createSessionHandle } from './session-handle.ts'\nimport { createSessionStore, type SessionStore } from './store.ts'\n\nimport type {\n AcpAgent,\n AcpClient,\n AcpSession,\n AgentDefinition,\n ChangeListener,\n ConnectionStatusSnapshot,\n CreateAcpClientOptions,\n PermissionRequest,\n} from './types.ts'\n\nexport function createAcpClient(options: CreateAcpClientOptions): AcpClient {\n const transport = options.transport\n let closedError: ErrorObject | undefined\n const stores = new Map<string, SessionStore>()\n const storeUnsubscribers = new Set<() => void>()\n const agents = new Map<string, AcpAgent>()\n const agentListeners = new Set<ChangeListener>()\n const agentUpdaters = new Map<\n string,\n {\n applyStatus: (payload: AgentStatusChangePayload) => void\n applyAuthRequired: (payload: AuthRequiredPayload) => void\n }\n >()\n let agentsSnapshot: readonly AcpAgent[] = Object.freeze([])\n\n function publishAgents(): void {\n agentsSnapshot = Object.freeze([...agents.values()])\n notifyChange(agentListeners)\n }\n\n const sessions = new Map<string, AcpSession>()\n const sessionListeners = new Set<ChangeListener>()\n let sessionsSnapshot: readonly AcpSession[] = Object.freeze([])\n\n function publishSessions(): void {\n sessionsSnapshot = Object.freeze([...sessions.values()])\n notifyChange(sessionListeners)\n }\n\n const permissions = createPermissionRegistry()\n\n let statusSnapshot: ConnectionStatusSnapshot = Object.freeze({\n status: 'connecting' as const,\n })\n const statusListeners = new Set<ChangeListener>()\n\n function ensureOpen(): void {\n if (closedError) throw new AcpClientError(closedError)\n }\n\n const handlers: TransportHandlers = {\n onInboundRequest(request) {\n if (request.kind !== 'permission') return\n const payload = request.payload as PermissionRequestCreatedPayload & {\n sessionId: string\n }\n const permission: PermissionRequest = Object.freeze({\n requestId: payload.requestId,\n sessionId: payload.sessionId,\n toolCall: payload.toolCall,\n options: payload.options,\n respond: (outcome: RequestPermissionOutcome) =>\n respondPermission(request.id, payload.requestId, outcome),\n })\n permissions.add(permission)\n },\n onLifecycle(event) {\n if (event.status === 'closed') {\n closedError = event.error ?? transportClosedError()\n permissions.reset()\n }\n if (\n statusSnapshot.status === event.status &&\n statusSnapshot.error === event.error\n ) {\n return\n }\n statusSnapshot = Object.freeze({\n status: event.status,\n ...(event.error === undefined ? {} : { error: event.error }),\n })\n notifyChange(statusListeners)\n },\n }\n\n function onHostEvent(event: AcpEvent): void {\n if (event.type === 'agent-status-change') {\n agentUpdaters.get(event.agentId)?.applyStatus(event.payload)\n } else if (event.type === 'auth-required') {\n agentUpdaters.get(event.agentId)?.applyAuthRequired(event.payload)\n } else if (\n event.type === 'session-created' ||\n event.type === 'session-closed'\n ) {\n notifyChange(sessionListeners)\n }\n }\n\n const connected = transport.connect(handlers)\n connected\n .then(() => {\n if (closedError) return\n storeUnsubscribers.add(transport.subscribe({ fromSeq: 0 }, onHostEvent))\n })\n .catch(() => {})\n\n async function respondPermission(\n inboundId: string,\n requestId: string,\n outcome: RequestPermissionOutcome,\n ): Promise<void> {\n ensureOpen()\n try {\n await connected\n await transport.respondInbound({ id: inboundId, result: outcome })\n } catch (error) {\n const clientError = toClientError(error)\n if (clientError.code === ACP_ERROR_CODES.alreadyAnswered) {\n permissions.prune(requestId)\n }\n throw clientError\n }\n permissions.prune(requestId)\n }\n\n let rpcCounter = 0\n async function call(\n method: string,\n params: Record<string, unknown>,\n ): Promise<unknown> {\n ensureOpen()\n try {\n await connected\n } catch (error) {\n throw toClientError(error)\n }\n ensureOpen()\n rpcCounter += 1\n let response\n try {\n response = await transport.request({\n id: `rpc-${rpcCounter}`,\n method,\n params,\n })\n } catch (error) {\n throw toClientError(error)\n }\n if (!response.ok) throw new AcpClientError(response.error)\n return response.result\n }\n\n function attachStore(sessionId: string): SessionStore {\n const existing = stores.get(sessionId)\n if (existing) return existing\n const store = createSessionStore(sessionId)\n stores.set(sessionId, store)\n const unsubscribe = transport.subscribe(\n { sessionId, fromSeq: store.lastSeq() },\n (event) => {\n if (event.type === 'permission-request-resolved') {\n permissions.prune(event.payload.requestId)\n }\n store.apply(event)\n },\n )\n storeUnsubscribers.add(unsubscribe)\n return store\n }\n\n function openSession(sessionId: string): AcpSession {\n const existing = sessions.get(sessionId)\n if (existing) return existing\n const session = createSessionHandle(call, attachStore(sessionId))\n sessions.set(sessionId, session)\n publishSessions()\n return session\n }\n\n async function listAgents(): Promise<readonly AgentSnapshotWire[]> {\n return (await call(ACP_RPC_METHODS.listAgents, {})) as AgentSnapshotWire[]\n }\n\n async function listAllSessions(): Promise<readonly SessionSnapshotWire[]> {\n return (await call(\n ACP_RPC_METHODS.getAllSessions,\n {},\n )) as SessionSnapshotWire[]\n }\n\n function registerAgent(snapshot: AgentSnapshotWire): AcpAgent {\n const { agent, applyStatus, applyAuthRequired } = createAgentHandle(\n call,\n openSession,\n publishAgents,\n snapshot,\n )\n agentUpdaters.set(agent.agentId, { applyStatus, applyAuthRequired })\n agents.set(agent.agentId, agent)\n publishAgents()\n return agent\n }\n\n return Object.freeze({\n agents: Object.freeze({\n async spawn(definition: AgentDefinition): Promise<AcpAgent> {\n const snapshot = (await call(ACP_RPC_METHODS.spawnAgent, {\n definition,\n })) as AgentSnapshotWire\n return registerAgent(snapshot)\n },\n get: (agentId: string) => agents.get(agentId),\n getSnapshot: () => agentsSnapshot,\n subscribe(listener: ChangeListener): () => void {\n agentListeners.add(listener)\n return () => agentListeners.delete(listener)\n },\n list: listAgents,\n async attach(agentId: string): Promise<AcpAgent> {\n const snapshots = await listAgents()\n const snapshot = snapshots.find(\n (candidate) => candidate.agentId === agentId,\n )\n if (!snapshot) {\n throw new AcpClientError({\n code: ACP_ERROR_CODES.agentExited,\n message: `agent ${agentId} is not known to the host`,\n retryable: false,\n })\n }\n return agents.get(agentId) ?? registerAgent(snapshot)\n },\n }),\n sessions: Object.freeze({\n get: (sessionId: string) => sessions.get(sessionId),\n getSnapshot: () => sessionsSnapshot,\n subscribe(listener: ChangeListener): () => void {\n sessionListeners.add(listener)\n return () => sessionListeners.delete(listener)\n },\n list: listAllSessions,\n async attach(sessionId: string): Promise<AcpSession> {\n const snapshots = await listAllSessions()\n const known = snapshots.some(\n (candidate) => candidate.sessionId === sessionId,\n )\n if (!known) {\n throw new AcpClientError({\n code: ACP_ERROR_CODES.sessionClosed,\n message: `session ${sessionId} is not known to the host`,\n retryable: false,\n })\n }\n return openSession(sessionId)\n },\n async restore(): Promise<readonly SessionSnapshotWire[]> {\n return (await call(\n ACP_RPC_METHODS.restoreSessions,\n {},\n )) as SessionSnapshotWire[]\n },\n }),\n permissions: Object.freeze({\n getSnapshot: permissions.getSnapshot,\n subscribe: permissions.subscribe,\n }),\n status: Object.freeze({\n getSnapshot: () => statusSnapshot,\n subscribe(listener: ChangeListener): () => void {\n statusListeners.add(listener)\n return () => statusListeners.delete(listener)\n },\n }),\n async dispose(): Promise<void> {\n if (closedError) return\n closedError = transportClosedError()\n for (const unsubscribe of storeUnsubscribers) unsubscribe()\n storeUnsubscribers.clear()\n permissions.clear()\n await transport.close()\n },\n })\n}\n","import {\n ACP_ERROR_CODES,\n type EnvelopeEndpoint,\n type Transport,\n type TransportConnectionStatus,\n type TransportHandlers,\n} from '@acpjs/protocol'\n\nimport { AcpClientError, transportClosedError } from './errors.ts'\n\nexport function createInProcessTransport(\n endpoint: EnvelopeEndpoint,\n): Transport {\n let status: TransportConnectionStatus = 'connecting'\n let handlers: TransportHandlers | undefined\n const unsubscribers = new Set<() => void>()\n let detachInbound: (() => void) | undefined\n\n return {\n async connect(next: TransportHandlers): Promise<void> {\n if (status === 'closed') {\n throw new AcpClientError(transportClosedError())\n }\n if (handlers) {\n throw new AcpClientError({\n code: ACP_ERROR_CODES.configInvalid,\n message: 'transport already connected',\n retryable: false,\n })\n }\n handlers = next\n next.onLifecycle({ status: 'connecting' })\n detachInbound = endpoint.onInboundRequest((request) => {\n next.onInboundRequest(request)\n })\n status = 'connected'\n next.onLifecycle({ status: 'connected' })\n },\n async request(request) {\n if (status !== 'connected') {\n return { id: request.id, ok: false, error: transportClosedError() }\n }\n return endpoint.request(request)\n },\n subscribe(params, onEvent) {\n if (status !== 'connected') {\n throw new AcpClientError(transportClosedError())\n }\n const unsubscribe = endpoint.subscribe(params, onEvent)\n unsubscribers.add(unsubscribe)\n return () => {\n unsubscribers.delete(unsubscribe)\n unsubscribe()\n }\n },\n async respondInbound(response) {\n if (status !== 'connected') {\n throw new AcpClientError(transportClosedError())\n }\n await endpoint.respondInbound(response)\n },\n async close() {\n if (status === 'closed') return\n status = 'closed'\n detachInbound?.()\n for (const unsubscribe of unsubscribers) unsubscribe()\n unsubscribers.clear()\n handlers?.onLifecycle({ status: 'closed' })\n },\n }\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@acpjs/client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "acpjs typed facade, reducer-driven snapshot+subscribe store, and in-process transport.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@acpjs/protocol": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^25.9.2",
|
|
24
|
+
"@acpjs/core": "0.1.0",
|
|
25
|
+
"@acpjs/fixture-agent": "0.0.0"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=24"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"clean": "rm -rf dist .turbo"
|
|
35
|
+
}
|
|
36
|
+
}
|