@pdhaku0/gemini-cli-agent-sdk 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.
Files changed (54) hide show
  1. package/README.md +109 -0
  2. package/client/index.d.ts +1 -0
  3. package/client/index.js +1 -0
  4. package/client/package.json +1 -0
  5. package/dist/client.d.ts +5 -0
  6. package/dist/client.js +5 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/common/types.d.ts +191 -0
  9. package/dist/common/types.js +18 -0
  10. package/dist/common/types.js.map +1 -0
  11. package/dist/core/AcpWebSocketTransport.d.ts +25 -0
  12. package/dist/core/AcpWebSocketTransport.js +222 -0
  13. package/dist/core/AcpWebSocketTransport.js.map +1 -0
  14. package/dist/core/AgentChatClient.d.ts +75 -0
  15. package/dist/core/AgentChatClient.js +679 -0
  16. package/dist/core/AgentChatClient.js.map +1 -0
  17. package/dist/core/ToolPermissionManager.d.ts +26 -0
  18. package/dist/core/ToolPermissionManager.js +88 -0
  19. package/dist/core/ToolPermissionManager.js.map +1 -0
  20. package/dist/core/diff-utils.d.ts +1 -0
  21. package/dist/core/diff-utils.js +7 -0
  22. package/dist/core/diff-utils.js.map +1 -0
  23. package/dist/core/stream-utils.d.ts +14 -0
  24. package/dist/core/stream-utils.js +57 -0
  25. package/dist/core/stream-utils.js.map +1 -0
  26. package/dist/extras/index.d.ts +1 -0
  27. package/dist/extras/index.js +2 -0
  28. package/dist/extras/index.js.map +1 -0
  29. package/dist/extras/sys-tags.d.ts +38 -0
  30. package/dist/extras/sys-tags.js +150 -0
  31. package/dist/extras/sys-tags.js.map +1 -0
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.js +2 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/server/GeminiBridge.d.ts +50 -0
  36. package/dist/server/GeminiBridge.js +500 -0
  37. package/dist/server/GeminiBridge.js.map +1 -0
  38. package/dist/server.d.ts +7 -0
  39. package/dist/server.js +7 -0
  40. package/dist/server.js.map +1 -0
  41. package/dist/ui/AgentChatStore.d.ts +16 -0
  42. package/dist/ui/AgentChatStore.js +59 -0
  43. package/dist/ui/AgentChatStore.js.map +1 -0
  44. package/docs/API.md +100 -0
  45. package/docs/EVENTS.md +100 -0
  46. package/docs/INTEGRATION.md +109 -0
  47. package/docs/SPECIFICATION.md +93 -0
  48. package/docs/TROUBLESHOOTING.md +44 -0
  49. package/docs/USAGE.md +270 -0
  50. package/docs/design.md +62 -0
  51. package/package.json +71 -0
  52. package/server/index.d.ts +1 -0
  53. package/server/index.js +1 -0
  54. package/server/package.json +1 -0
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # Agent Chat SDK
2
+
3
+ A TypeScript SDK for building chat UIs on top of the Gemini CLI Agent via ACP (Agent Chat Protocol). It provides a browser-safe client, a bridge for the CLI, and a UI-friendly event model.
4
+
5
+ ## Features
6
+
7
+ - **WebSocket transport** with reconnect + ACP framing
8
+ - **Client event model** tuned for UI rendering
9
+ - **Tool call parsing** (permissions, inputs, diff rendering)
10
+ - **Replay support** for late joiners / infinite scroll
11
+ - **Bridge process** to run Gemini CLI via stdio
12
+ - **Optional store** (`AgentChatStore`) for React/GUI state
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @pdhaku0/gemini-cli-agent-sdk
18
+ ```
19
+
20
+ ## Requirements
21
+
22
+ - Node.js 18+ (bridge and any server usage)
23
+ - Gemini CLI with `--experimental-acp`
24
+ - Browser client or Node with `ws`
25
+
26
+ ## Quick Start
27
+
28
+ ### 1) Start the bridge
29
+
30
+ ```bash
31
+ npm run start:bridge
32
+ ```
33
+
34
+ ### 2) Connect a client
35
+
36
+ ```ts
37
+ import { AgentChatClient } from '@pdhaku0/gemini-cli-agent-sdk/client';
38
+
39
+ const client = new AgentChatClient({
40
+ url: 'ws://localhost:4444',
41
+ cwd: '/path/to/project',
42
+ replay: { limit: 15 },
43
+ });
44
+
45
+ client.on('text_delta', ({ delta }) => process.stdout.write(delta));
46
+ client.on('tool_update', ({ toolCall }) => {
47
+ console.log(`[tool] ${toolCall.name} ${toolCall.status}`);
48
+ });
49
+
50
+ await client.connect();
51
+ await client.sendMessage('List files in the current directory.');
52
+ ```
53
+
54
+ ### 3) Replay older turns (infinite scroll)
55
+
56
+ ```ts
57
+ const older = await AgentChatClient.fetchReplay('ws://localhost:4444', {
58
+ before: oldestTimestampMs,
59
+ limit: 10,
60
+ });
61
+ client.prependMessages(older);
62
+ ```
63
+
64
+ ### 4) Optional SYS tags (structured capture)
65
+
66
+ Use SYS tags in assistant output and capture them on the bridge:
67
+
68
+ ```ts
69
+ import { GeminiBridge } from '@pdhaku0/gemini-cli-agent-sdk/server';
70
+ import { createSysTagTransform } from '@pdhaku0/gemini-cli-agent-sdk/extras';
71
+
72
+ const bridge = new GeminiBridge({
73
+ outgoingTransform: createSysTagTransform({ mode: 'event' }),
74
+ });
75
+ bridge.start();
76
+ ```
77
+
78
+ ### 5) Hidden initial prompt
79
+
80
+ ```ts
81
+ await client.sendMessage('System priming...', { hidden: 'turn' });
82
+ ```
83
+
84
+ ## Examples
85
+
86
+ - `examples/next-app` — full Next.js App Router UI (auth, approvals, replay, session persistence)
87
+ - `examples/cli` — minimal Node CLI (streaming + tool approvals)
88
+
89
+ Examples are **kept in the repo** but **excluded from npm** to keep the package lean.
90
+
91
+ ## Documentation
92
+
93
+ - `docs/USAGE.md` — full end-to-end guide
94
+ - `docs/API.md` — API surface
95
+ - `docs/EVENTS.md` — event model + rendering rules
96
+ - `docs/INTEGRATION.md` — Next/Node integration patterns
97
+ - `docs/TROUBLESHOOTING.md` — common pitfalls
98
+
99
+ ## Development
100
+
101
+ - Build: `npm run build`
102
+ - Bridge: `npm run start:bridge`
103
+ - CLI example: `node examples/cli/index.js`
104
+
105
+ ## Notes
106
+
107
+ - The bridge keeps **in-memory replay only**. Restarting the bridge clears replay history.
108
+ - Reusing sessions across reloads is supported by passing a saved `sessionId` to the client.
109
+ - Replay `limit` is **turns**, not messages.
@@ -0,0 +1 @@
1
+ export * from '../dist/client';
@@ -0,0 +1 @@
1
+ export * from '../dist/client.js';
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,5 @@
1
+ export { AgentChatClient } from './core/AgentChatClient.js';
2
+ export { AcpWebSocketTransport } from './core/AcpWebSocketTransport.js';
3
+ export { AgentChatStore } from './ui/AgentChatStore.js';
4
+ export * from './common/types.js';
5
+ export type { AgentChatState } from './common/types.js';
package/dist/client.js ADDED
@@ -0,0 +1,5 @@
1
+ export { AgentChatClient } from './core/AgentChatClient.js';
2
+ export { AcpWebSocketTransport } from './core/AcpWebSocketTransport.js';
3
+ export { AgentChatStore } from './ui/AgentChatStore.js';
4
+ export * from './common/types.js';
5
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,191 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Base JSON-RPC 2.0 types
4
+ */
5
+ export declare const JsonRpcIdSchema: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodNull]>;
6
+ export type JsonRpcId = z.infer<typeof JsonRpcIdSchema>;
7
+ export declare const JsonRpcMessageSchema: z.ZodObject<{
8
+ jsonrpc: z.ZodLiteral<"2.0">;
9
+ id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodNull]>>;
10
+ method: z.ZodOptional<z.ZodString>;
11
+ params: z.ZodOptional<z.ZodAny>;
12
+ result: z.ZodOptional<z.ZodAny>;
13
+ error: z.ZodOptional<z.ZodObject<{
14
+ code: z.ZodNumber;
15
+ message: z.ZodString;
16
+ data: z.ZodOptional<z.ZodAny>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ code: number;
19
+ message: string;
20
+ data?: any;
21
+ }, {
22
+ code: number;
23
+ message: string;
24
+ data?: any;
25
+ }>>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ jsonrpc: "2.0";
28
+ params?: any;
29
+ id?: string | number | null | undefined;
30
+ method?: string | undefined;
31
+ result?: any;
32
+ error?: {
33
+ code: number;
34
+ message: string;
35
+ data?: any;
36
+ } | undefined;
37
+ }, {
38
+ jsonrpc: "2.0";
39
+ params?: any;
40
+ id?: string | number | null | undefined;
41
+ method?: string | undefined;
42
+ result?: any;
43
+ error?: {
44
+ code: number;
45
+ message: string;
46
+ data?: any;
47
+ } | undefined;
48
+ }>;
49
+ export type JsonRpcMessage = z.infer<typeof JsonRpcMessageSchema>;
50
+ /**
51
+ * ACP Specific Types
52
+ */
53
+ export type ConnectionState = 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
54
+ export type HiddenMode = 'none' | 'user' | 'assistant' | 'turn';
55
+ export interface PendingApproval {
56
+ requestId: JsonRpcId;
57
+ toolCall: {
58
+ toolCallId: string;
59
+ kind: string;
60
+ title: string;
61
+ locations?: any[];
62
+ input?: string;
63
+ description?: string;
64
+ workingDir?: string;
65
+ args?: any;
66
+ };
67
+ options: Array<{
68
+ optionId: string;
69
+ kind: 'allow_once' | 'allow_always' | 'deny' | 'deny_always' | 'reject_once';
70
+ label?: string;
71
+ name?: string;
72
+ }>;
73
+ }
74
+ export interface AcpSessionNewParams {
75
+ cwd: string;
76
+ mcpServers?: any[];
77
+ model?: string;
78
+ }
79
+ export interface AcpSessionNewResult {
80
+ sessionId: string;
81
+ }
82
+ export interface AcpPromptParams {
83
+ sessionId: string;
84
+ prompt: Array<{
85
+ type: 'text';
86
+ text: string;
87
+ }>;
88
+ }
89
+ export type AcpSessionUpdateType = 'agent_thought_chunk' | 'agent_message_chunk' | 'end_of_turn' | 'tool_call' | 'tool_call_update' | 'response.completed';
90
+ export interface AcpSessionUpdateNotification {
91
+ method: 'session/update';
92
+ params: {
93
+ update: {
94
+ sessionUpdate: AcpSessionUpdateType;
95
+ content?: {
96
+ type: 'text';
97
+ text: string;
98
+ };
99
+ toolCallId?: string;
100
+ kind?: string;
101
+ title?: string;
102
+ status?: string;
103
+ locations?: any[];
104
+ stopReason?: string;
105
+ };
106
+ };
107
+ }
108
+ export interface AcpRequestPermissionNotification {
109
+ id: JsonRpcId;
110
+ method: 'session/request_permission';
111
+ params: {
112
+ toolCall: {
113
+ toolCallId: string;
114
+ kind: string;
115
+ title: string;
116
+ locations?: any[];
117
+ };
118
+ options: Array<{
119
+ optionId: string;
120
+ kind: 'allow_once' | 'deny' | 'allow_always' | 'deny_always';
121
+ label?: string;
122
+ }>;
123
+ };
124
+ }
125
+ export interface AcpAuthUrlNotification {
126
+ method: 'gemini/authUrl';
127
+ params: {
128
+ url: string;
129
+ };
130
+ }
131
+ export interface AcpSubmitAuthCodeParams {
132
+ code: string;
133
+ }
134
+ export type ChatRole = 'user' | 'assistant' | 'system' | 'tool';
135
+ export type ToolStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';
136
+ export interface ToolCall {
137
+ id: string;
138
+ name: string;
139
+ title: string;
140
+ description?: string;
141
+ workingDir?: string;
142
+ status: ToolStatus;
143
+ input?: string;
144
+ args?: any;
145
+ result?: string;
146
+ diff?: {
147
+ path?: string;
148
+ unified: string;
149
+ oldTextLength?: number;
150
+ newTextLength?: number;
151
+ };
152
+ ts: number;
153
+ }
154
+ export type MessageContentPart = {
155
+ type: 'text';
156
+ text: string;
157
+ } | {
158
+ type: 'thought';
159
+ thought: string;
160
+ } | {
161
+ type: 'tool_call';
162
+ call: ToolCall;
163
+ };
164
+ export interface AssistantMessage {
165
+ id: string;
166
+ role: 'assistant';
167
+ thought: string;
168
+ text: string;
169
+ content: MessageContentPart[];
170
+ toolCalls: ToolCall[];
171
+ stopReason?: string;
172
+ hidden?: boolean;
173
+ ts: number;
174
+ }
175
+ export interface UserMessage {
176
+ id: string;
177
+ role: 'user';
178
+ text: string;
179
+ hidden?: boolean;
180
+ ts: number;
181
+ }
182
+ export type ChatMessage = UserMessage | AssistantMessage;
183
+ export interface AgentChatState {
184
+ connectionState: ConnectionState;
185
+ isStreaming: boolean;
186
+ messages: ChatMessage[];
187
+ pendingApproval: PendingApproval | null;
188
+ authUrl: string | null;
189
+ lastStopReason: string | null;
190
+ lastError: unknown | null;
191
+ }
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Base JSON-RPC 2.0 types
4
+ */
5
+ export const JsonRpcIdSchema = z.union([z.string(), z.number(), z.null()]);
6
+ export const JsonRpcMessageSchema = z.object({
7
+ jsonrpc: z.literal('2.0'),
8
+ id: JsonRpcIdSchema.optional(),
9
+ method: z.string().optional(),
10
+ params: z.any().optional(),
11
+ result: z.any().optional(),
12
+ error: z.object({
13
+ code: z.number(),
14
+ message: z.string(),
15
+ data: z.any().optional(),
16
+ }).optional(),
17
+ });
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/common/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAG3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACzB,EAAE,EAAE,eAAe,CAAC,QAAQ,EAAE;IAC9B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC1B,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACZ,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;KAC3B,CAAC,CAAC,QAAQ,EAAE;CAChB,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { EventEmitter } from 'events';
2
+ import { JsonRpcId } from '../common/types.js';
3
+ export interface AcpWebSocketOptions {
4
+ url: string;
5
+ reconnect?: boolean;
6
+ reconnectInterval?: number;
7
+ webSocketCtor?: any;
8
+ }
9
+ export declare class AcpWebSocketTransport extends EventEmitter {
10
+ private ws;
11
+ private options;
12
+ private pendingRequests;
13
+ private nextId;
14
+ private connectionState;
15
+ constructor(options: AcpWebSocketOptions);
16
+ private resolveWebSocketConstructor;
17
+ connect(): void;
18
+ private openWebSocket;
19
+ sendRequest(method: string, params: any): Promise<any>;
20
+ sendNotification(method: string, params: any): void;
21
+ sendResponse(id: JsonRpcId, result: any): void;
22
+ private handleMessage;
23
+ dispose(): void;
24
+ private setConnectionState;
25
+ }
@@ -0,0 +1,222 @@
1
+ import { EventEmitter } from 'events';
2
+ import { JsonRpcMessageSchema } from '../common/types.js';
3
+ export class AcpWebSocketTransport extends EventEmitter {
4
+ ws = null;
5
+ options;
6
+ pendingRequests = new Map();
7
+ nextId = 1;
8
+ connectionState = 'disconnected';
9
+ constructor(options) {
10
+ super();
11
+ this.options = {
12
+ reconnect: true,
13
+ reconnectInterval: 2000,
14
+ ...options,
15
+ };
16
+ }
17
+ async resolveWebSocketConstructor() {
18
+ if (this.options.webSocketCtor)
19
+ return this.options.webSocketCtor;
20
+ // In browser, window.WebSocket is most likely to be the "real" native one.
21
+ // Bundlers like Turbopack often shim globalThis.WebSocket to 'ws' shim.
22
+ if (typeof window !== 'undefined' && typeof window.WebSocket === 'function') {
23
+ return window.WebSocket;
24
+ }
25
+ const global = globalThis;
26
+ const win = typeof window !== 'undefined' ? window : {};
27
+ // Follow-up checks
28
+ if (typeof global.WebSocket === 'function')
29
+ return global.WebSocket;
30
+ if (global.WebSocket?.WebSocket)
31
+ return global.WebSocket.WebSocket;
32
+ if (win.WebSocket?.WebSocket)
33
+ return win.WebSocket.WebSocket;
34
+ if (typeof win.MozWebSocket === 'function')
35
+ return win.MozWebSocket;
36
+ // Node fallback (dynamic import to avoid bundler issues)
37
+ if (typeof process !== 'undefined' && process.versions?.node) {
38
+ try {
39
+ const mod = await import('ws');
40
+ return mod?.WebSocket || mod;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+ connect() {
49
+ if (this.connectionState === 'connecting' || this.connectionState === 'connected') {
50
+ return;
51
+ }
52
+ this.setConnectionState('connecting');
53
+ console.log(`[ACP] Connecting to ${this.options.url}...`);
54
+ void this.openWebSocket();
55
+ }
56
+ async openWebSocket() {
57
+ const WS = await this.resolveWebSocketConstructor();
58
+ if (!WS) {
59
+ const err = new Error('WebSocket constructor not found in this environment');
60
+ console.error('[ACP]', err);
61
+ this.emit('error', err);
62
+ this.setConnectionState('disconnected');
63
+ return;
64
+ }
65
+ try {
66
+ this.ws = new WS(this.options.url);
67
+ const handleOpen = () => {
68
+ console.log(`[ACP] WebSocket Connected to ${this.options.url}`);
69
+ this.setConnectionState('connected');
70
+ this.emit('connected');
71
+ };
72
+ const handleMessage = async (eventOrData) => {
73
+ try {
74
+ // Normalize data to string (browser event or ws data)
75
+ let data = eventOrData?.data ?? eventOrData;
76
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
77
+ data = await data.text();
78
+ }
79
+ else if (typeof ArrayBuffer !== 'undefined' && (data instanceof ArrayBuffer || ArrayBuffer.isView(data))) {
80
+ data = new TextDecoder().decode(data);
81
+ }
82
+ const strData = data.toString();
83
+ if (strData.includes('"error"')) {
84
+ console.log('[ACP] Received Error Message:', strData);
85
+ }
86
+ this.handleMessage(strData);
87
+ }
88
+ catch (e) {
89
+ console.error('[ACP] Error processing message:', e);
90
+ }
91
+ };
92
+ const handleClose = (event) => {
93
+ const reason = event?.reason || 'no reason provided';
94
+ const code = event?.code;
95
+ console.log(`[ACP] WebSocket Closed (URL: ${this.options.url}, Code: ${code}, Reason: ${reason})`);
96
+ if (this.pendingRequests.size) {
97
+ const err = new Error(`WebSocket closed: ${reason} (code: ${code})`);
98
+ for (const [, pending] of this.pendingRequests) {
99
+ pending.reject(err);
100
+ }
101
+ this.pendingRequests.clear();
102
+ }
103
+ this.emit('disconnected');
104
+ if (this.options.reconnect) {
105
+ this.setConnectionState('reconnecting');
106
+ setTimeout(() => this.connect(), this.options.reconnectInterval);
107
+ }
108
+ else {
109
+ this.setConnectionState('disconnected');
110
+ }
111
+ };
112
+ const handleError = (err) => {
113
+ // Browsers often fire a generic error event with no details.
114
+ const msg = `WebSocket connection failed to ${this.options.url}. Check if the server is running and accessible.`;
115
+ console.error(`[ACP] ${msg}`, err);
116
+ this.emit('error', new Error(msg));
117
+ };
118
+ if (typeof this.ws.on === 'function') {
119
+ this.ws.on('open', handleOpen);
120
+ this.ws.on('message', handleMessage);
121
+ this.ws.on('close', handleClose);
122
+ this.ws.on('error', handleError);
123
+ }
124
+ else {
125
+ this.ws.onopen = handleOpen;
126
+ this.ws.onmessage = handleMessage;
127
+ this.ws.onclose = handleClose;
128
+ this.ws.onerror = handleError;
129
+ }
130
+ }
131
+ catch (e) {
132
+ console.error(`[ACP] Failed to initialize WebSocket for ${this.options.url}:`, e);
133
+ this.emit('error', e);
134
+ }
135
+ }
136
+ sendRequest(method, params) {
137
+ const id = this.nextId++;
138
+ const message = {
139
+ jsonrpc: '2.0',
140
+ id,
141
+ method,
142
+ params,
143
+ };
144
+ return new Promise((resolve, reject) => {
145
+ const ws = this.ws;
146
+ if (!ws || ws.readyState !== 1) { // 1 = OPEN
147
+ reject(new Error('WebSocket is not open'));
148
+ return;
149
+ }
150
+ this.pendingRequests.set(id, { resolve, reject });
151
+ ws.send(JSON.stringify(message));
152
+ });
153
+ }
154
+ sendNotification(method, params) {
155
+ const message = {
156
+ jsonrpc: '2.0',
157
+ method,
158
+ params,
159
+ };
160
+ if (this.ws?.readyState === 1) {
161
+ this.ws.send(JSON.stringify(message));
162
+ }
163
+ }
164
+ sendResponse(id, result) {
165
+ const message = {
166
+ jsonrpc: '2.0',
167
+ id,
168
+ result,
169
+ };
170
+ if (this.ws?.readyState === 1) {
171
+ this.ws.send(JSON.stringify(message));
172
+ }
173
+ }
174
+ handleMessage(data) {
175
+ try {
176
+ const raw = JSON.parse(data);
177
+ const msg = JsonRpcMessageSchema.parse(raw);
178
+ // Handle Response
179
+ if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {
180
+ const pending = this.pendingRequests.get(msg.id);
181
+ if (pending) {
182
+ this.pendingRequests.delete(msg.id);
183
+ if (msg.error) {
184
+ const errorMsg = msg.error.message || 'Unknown JSON-RPC error';
185
+ const error = new Error(errorMsg);
186
+ error.code = msg.error.code;
187
+ error.data = msg.error.data;
188
+ pending.reject(error);
189
+ }
190
+ else {
191
+ pending.resolve(msg.result);
192
+ }
193
+ }
194
+ return;
195
+ }
196
+ // Handle Request/Notification
197
+ if (msg.method) {
198
+ this.emit('notification', msg);
199
+ this.emit(`method:${msg.method}`, msg);
200
+ }
201
+ }
202
+ catch (err) {
203
+ console.error('[ACP] Failed to parse message:', err);
204
+ }
205
+ }
206
+ dispose() {
207
+ this.options.reconnect = false;
208
+ if (this.ws) {
209
+ this.ws.onclose = null; // Prevent reconnect loop
210
+ this.ws.close();
211
+ }
212
+ this.pendingRequests.clear();
213
+ this.setConnectionState('disconnected');
214
+ }
215
+ setConnectionState(state) {
216
+ if (this.connectionState === state)
217
+ return;
218
+ this.connectionState = state;
219
+ this.emit('connection_state', state);
220
+ }
221
+ }
222
+ //# sourceMappingURL=AcpWebSocketTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AcpWebSocketTransport.js","sourceRoot":"","sources":["../../src/core/AcpWebSocketTransport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { EventEmitter } from 'events';
2
+ import { ChatMessage, ConnectionState, PendingApproval, HiddenMode } from '../common/types.js';
3
+ export interface AgentChatClientOptions {
4
+ url: string;
5
+ model?: string;
6
+ cwd?: string;
7
+ diffContextLines?: number;
8
+ sessionId?: string;
9
+ replay?: {
10
+ limit?: number;
11
+ since?: number;
12
+ before?: number;
13
+ };
14
+ }
15
+ export declare class AgentChatClient extends EventEmitter {
16
+ private transport;
17
+ private sessionId;
18
+ private messages;
19
+ private authUrl;
20
+ private pendingApproval;
21
+ private options;
22
+ private connectionState;
23
+ private inTurn;
24
+ private activeAssistantId;
25
+ private lastFinalizedAssistantId;
26
+ private timeOverride;
27
+ private idCounter;
28
+ private replayNonce;
29
+ private currentTurnHidden;
30
+ constructor(options: AgentChatClientOptions);
31
+ connect(options?: {
32
+ autoSession?: boolean;
33
+ }): Promise<void>;
34
+ private initializeSession;
35
+ setSessionId(sessionId: string | null): void;
36
+ getSessionId(): string | null;
37
+ sendMessage(text: string, options?: {
38
+ hidden?: HiddenMode;
39
+ }): Promise<void>;
40
+ submitAuthCode(code: string): Promise<void>;
41
+ cancel(): Promise<void>;
42
+ approveTool(optionId: string): Promise<void>;
43
+ getMessages(options?: {
44
+ includeHidden?: boolean;
45
+ }): ChatMessage[];
46
+ prependMessages(messages: ChatMessage[]): void;
47
+ getAuthUrl(): string | null;
48
+ getPendingApproval(): PendingApproval | null;
49
+ getConnectionState(): ConnectionState;
50
+ dispose(): void;
51
+ private setupHandlers;
52
+ private handleSessionUpdate;
53
+ private updateAssistantMessageNormalized;
54
+ private handleToolCall;
55
+ private handleToolUpdate;
56
+ private getOrCreateAssistantMessage;
57
+ private handleAuthUrl;
58
+ private handlePermissionRequest;
59
+ disconnect(): void;
60
+ private shouldEmitUser;
61
+ private shouldEmitAssistant;
62
+ private shouldAutoRejectApproval;
63
+ private resolveApproval;
64
+ private parseToolTitle;
65
+ private createToolCallFromPermission;
66
+ private extractDiffData;
67
+ private normalizeDiffContextLines;
68
+ private now;
69
+ private handleReplayPrompt;
70
+ private makeId;
71
+ private buildUrlWithReplay;
72
+ static fetchReplay(baseUrl: string, replay: AgentChatClientOptions['replay'], options?: {
73
+ idleMs?: number;
74
+ }): Promise<ChatMessage[]>;
75
+ }