@gnsx/genesys.agent.client 0.4.2 → 1.0.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/dist/client.d.ts +82 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +321 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +4 -70
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -344
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +95 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +10 -0
- package/dist/protocol.js.map +1 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/uuid.d.ts +6 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +15 -0
- package/dist/uuid.js.map +1 -0
- package/package.json +9 -35
- package/src/client.ts +394 -0
- package/src/index.ts +22 -53
- package/src/protocol.ts +100 -0
- package/src/types.ts +49 -27
- package/src/uuid.ts +14 -0
- package/README.md +0 -119
- package/dist/index.cjs +0 -357
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -70
- package/src/GenesysAgentClient.ts +0 -318
package/package.json
CHANGED
|
@@ -1,52 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gnsx/genesys.agent.client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript client library for Genesys Agent Server",
|
|
3
|
+
"version": "1.0.0",
|
|
5
4
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"access": "public"
|
|
8
|
-
},
|
|
5
|
+
"description": "TypeScript client for the Genesys agent server — browser + Node.js",
|
|
9
6
|
"main": "./dist/index.js",
|
|
10
7
|
"types": "./dist/index.d.ts",
|
|
11
8
|
"exports": {
|
|
12
9
|
".": {
|
|
13
10
|
"types": "./dist/index.d.ts",
|
|
14
|
-
"import": "./dist/index.js"
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
17
14
|
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"src"
|
|
21
|
-
],
|
|
15
|
+
"files": ["dist", "src"],
|
|
22
16
|
"scripts": {
|
|
23
|
-
"build": "
|
|
17
|
+
"build": "tsc --build",
|
|
24
18
|
"clean": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true}); require('fs').rmSync('tsconfig.tsbuildinfo', {force: true})\"",
|
|
25
|
-
"lint": "eslint
|
|
26
|
-
"type-check": "tsc --noEmit",
|
|
27
|
-
"prepublishOnly": "pnpm build"
|
|
19
|
+
"lint": "eslint ."
|
|
28
20
|
},
|
|
29
|
-
"keywords": [
|
|
30
|
-
"genesys",
|
|
31
|
-
"agent",
|
|
32
|
-
"client",
|
|
33
|
-
"websocket",
|
|
34
|
-
"typescript"
|
|
35
|
-
],
|
|
36
|
-
"author": "",
|
|
37
|
-
"license": "UNLICENSED",
|
|
38
21
|
"devDependencies": {
|
|
39
|
-
"@gnsx/genesys.agent.shared": "workspace:*",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
41
|
-
"@typescript-eslint/parser": "^8.53.0",
|
|
42
|
-
"dts-bundle-generator": "^9.5.1",
|
|
43
22
|
"eslint": "^9.39.2",
|
|
44
|
-
"eslint-plugin-import": "^2.32.0",
|
|
45
|
-
"http-server": "^14.1.1",
|
|
46
|
-
"tsup": "^8.5.1",
|
|
47
23
|
"typescript": "^5.9.3"
|
|
48
24
|
},
|
|
49
|
-
"
|
|
50
|
-
"node": ">=18.0.0"
|
|
51
|
-
}
|
|
25
|
+
"license": "UNLICENSED"
|
|
52
26
|
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenesysAgentClient — browser + Node.js compatible client for the
|
|
3
|
+
* Genesys Agent v2 server.
|
|
4
|
+
*
|
|
5
|
+
* Connection flow:
|
|
6
|
+
* 1. client.connect(serverUrl)
|
|
7
|
+
* → POST /sessions — allocate workspace on server
|
|
8
|
+
* → WebSocket to wsUrl — attach to that workspace
|
|
9
|
+
*
|
|
10
|
+
* Reconnect: re-uses the stored wsUrl (no re-POST).
|
|
11
|
+
* Destroy: closes WS + DELETE /sessions/:id to clean up the workspace.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { randomUUID } from './uuid.js';
|
|
15
|
+
|
|
16
|
+
import type {
|
|
17
|
+
CreateSessionResponse,
|
|
18
|
+
SessionInfoResponse,
|
|
19
|
+
ThinkingLevel,
|
|
20
|
+
WsCommand,
|
|
21
|
+
WsServerMessage,
|
|
22
|
+
} from './protocol.js';
|
|
23
|
+
import type {
|
|
24
|
+
ClientEventMap,
|
|
25
|
+
ClientEventType,
|
|
26
|
+
ClientOptions,
|
|
27
|
+
ClientState,
|
|
28
|
+
ModelInfo,
|
|
29
|
+
WsSessionState,
|
|
30
|
+
} from './types.js';
|
|
31
|
+
|
|
32
|
+
const LOG_PREFIX = '[GenesysAgentClient]';
|
|
33
|
+
|
|
34
|
+
/** Pending promise waiting for a command response */
|
|
35
|
+
interface Pending {
|
|
36
|
+
resolve: (data: unknown) => void;
|
|
37
|
+
reject: (err: Error) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class GenesysAgentClient {
|
|
41
|
+
private ws: WebSocket | null = null;
|
|
42
|
+
private sessionId: string | null = null;
|
|
43
|
+
private wsUrl: string | null = null;
|
|
44
|
+
private serverUrl: string | null = null;
|
|
45
|
+
private state: ClientState = 'disconnected';
|
|
46
|
+
|
|
47
|
+
private reconnectAttempts = 0;
|
|
48
|
+
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
49
|
+
private intentionalClose = false;
|
|
50
|
+
|
|
51
|
+
private readonly pending = new Map<string, Pending>();
|
|
52
|
+
private readonly listeners = new Map<ClientEventType, Set<(...args: unknown[]) => void>>();
|
|
53
|
+
|
|
54
|
+
constructor(private readonly options: ClientOptions = {}) {
|
|
55
|
+
this.options.reconnectDelay ??= 1000;
|
|
56
|
+
this.options.maxReconnectAttempts ??= 5;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// =========================================================================
|
|
60
|
+
// Connection lifecycle
|
|
61
|
+
// =========================================================================
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a server workspace then open the WebSocket.
|
|
65
|
+
* @param serverUrl Base HTTP URL, e.g. "http://localhost:3000"
|
|
66
|
+
*/
|
|
67
|
+
async connect(serverUrl: string): Promise<void> {
|
|
68
|
+
if (this.state === 'connected' || this.state === 'connecting') {
|
|
69
|
+
throw new Error('Already connected or connecting');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.serverUrl = serverUrl.replace(/\/+$/, '');
|
|
73
|
+
this.intentionalClose = false;
|
|
74
|
+
this.setState('connecting');
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const response = await fetch(`${this.serverUrl}/sessions`, { method: 'POST' });
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const body = await response.json().catch(() => ({ error: 'Failed to create session' })) as { error?: string };
|
|
80
|
+
throw new Error(body.error ?? `HTTP ${response.status}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const { sessionId, wsUrl } = await response.json() as CreateSessionResponse;
|
|
84
|
+
this.sessionId = sessionId;
|
|
85
|
+
this.wsUrl = wsUrl;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
this.setState('disconnected');
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await this.openWebSocket();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Close the WebSocket and DELETE the server workspace.
|
|
96
|
+
* After this call the client is fully cleaned up and can connect() again.
|
|
97
|
+
*/
|
|
98
|
+
async destroy(): Promise<void> {
|
|
99
|
+
this.intentionalClose = true;
|
|
100
|
+
this.clearReconnectTimer();
|
|
101
|
+
|
|
102
|
+
// Reject all pending requests
|
|
103
|
+
for (const [, pending] of this.pending) {
|
|
104
|
+
pending.reject(new Error('Client destroyed'));
|
|
105
|
+
}
|
|
106
|
+
this.pending.clear();
|
|
107
|
+
|
|
108
|
+
if (this.ws) {
|
|
109
|
+
this.ws.close();
|
|
110
|
+
this.ws = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Clean up server workspace
|
|
114
|
+
if (this.sessionId && this.serverUrl) {
|
|
115
|
+
try {
|
|
116
|
+
await fetch(`${this.serverUrl}/sessions/${this.sessionId}`, { method: 'DELETE' });
|
|
117
|
+
} catch {
|
|
118
|
+
// Best-effort
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.sessionId = null;
|
|
123
|
+
this.wsUrl = null;
|
|
124
|
+
this.setState('disconnected');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Disconnect the WebSocket but keep the workspace alive on the server
|
|
129
|
+
* (allows reconnect via the same wsUrl).
|
|
130
|
+
*/
|
|
131
|
+
disconnect(): void {
|
|
132
|
+
this.intentionalClose = true;
|
|
133
|
+
this.clearReconnectTimer();
|
|
134
|
+
|
|
135
|
+
if (this.ws) {
|
|
136
|
+
this.ws.close();
|
|
137
|
+
this.ws = null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.setState('disconnected');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// =========================================================================
|
|
144
|
+
// Commands
|
|
145
|
+
// =========================================================================
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Send a prompt. Returns as soon as the server ACKs — events stream back
|
|
149
|
+
* via the 'event' listener.
|
|
150
|
+
*/
|
|
151
|
+
async prompt(message: string): Promise<void> {
|
|
152
|
+
await this.request({ type: 'prompt', message });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Abort the current agent turn. */
|
|
156
|
+
async abort(): Promise<void> {
|
|
157
|
+
await this.request({ type: 'abort' });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Get the current session state. */
|
|
161
|
+
async getState(): Promise<WsSessionState> {
|
|
162
|
+
return this.request<WsSessionState>({ type: 'get_state' });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Set the active model. */
|
|
166
|
+
async setModel(provider: string, modelId: string): Promise<void> {
|
|
167
|
+
await this.request({ type: 'set_model', provider, modelId });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Get all models available on the server. */
|
|
171
|
+
async getAvailableModels(): Promise<ModelInfo[]> {
|
|
172
|
+
const data = await this.request<{ models: ModelInfo[] }>({ type: 'get_available_models' });
|
|
173
|
+
return data.models;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Set the thinking/reasoning level. */
|
|
177
|
+
async setThinkingLevel(level: ThinkingLevel): Promise<void> {
|
|
178
|
+
await this.request({ type: 'set_thinking_level', level });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Start a new session (clears history). */
|
|
182
|
+
async newSession(): Promise<void> {
|
|
183
|
+
await this.request({ type: 'new_session' });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Compact the conversation context. */
|
|
187
|
+
async compact(customInstructions?: string): Promise<void> {
|
|
188
|
+
await this.request({ type: 'compact', customInstructions });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Get all current messages. */
|
|
192
|
+
async getMessages(): Promise<unknown[]> {
|
|
193
|
+
const data = await this.request<{ messages: unknown[] }>({ type: 'get_messages' });
|
|
194
|
+
return data.messages;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// =========================================================================
|
|
198
|
+
// HTTP helpers
|
|
199
|
+
// =========================================================================
|
|
200
|
+
|
|
201
|
+
/** Fetch workspace info from the server (no WS required). */
|
|
202
|
+
async getSessionInfo(): Promise<SessionInfoResponse> {
|
|
203
|
+
if (!this.serverUrl || !this.sessionId) {
|
|
204
|
+
throw new Error('Not connected');
|
|
205
|
+
}
|
|
206
|
+
const response = await fetch(`${this.serverUrl}/sessions/${this.sessionId}`);
|
|
207
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
208
|
+
return response.json() as Promise<SessionInfoResponse>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// =========================================================================
|
|
212
|
+
// Event listeners
|
|
213
|
+
// =========================================================================
|
|
214
|
+
|
|
215
|
+
on<E extends ClientEventType>(event: E, listener: ClientEventMap[E]): ClientEventMap[E] {
|
|
216
|
+
if (!this.listeners.has(event)) this.listeners.set(event, new Set());
|
|
217
|
+
this.listeners.get(event)!.add(listener as (...args: unknown[]) => void);
|
|
218
|
+
return listener;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
off<E extends ClientEventType>(event: E, listener: ClientEventMap[E]): void {
|
|
222
|
+
this.listeners.get(event)?.delete(listener as (...args: unknown[]) => void);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
once<E extends ClientEventType>(event: E, listener: ClientEventMap[E]): void {
|
|
226
|
+
const wrapper = ((...args: unknown[]) => {
|
|
227
|
+
this.off(event, wrapper as ClientEventMap[E]);
|
|
228
|
+
(listener as (...args: unknown[]) => void)(...args);
|
|
229
|
+
}) as ClientEventMap[E];
|
|
230
|
+
this.on(event, wrapper);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// =========================================================================
|
|
234
|
+
// State
|
|
235
|
+
// =========================================================================
|
|
236
|
+
|
|
237
|
+
getConnectionState(): ClientState { return this.state; }
|
|
238
|
+
isConnected(): boolean { return this.state === 'connected'; }
|
|
239
|
+
getSessionId(): string | null { return this.sessionId; }
|
|
240
|
+
|
|
241
|
+
// =========================================================================
|
|
242
|
+
// Internal: WebSocket
|
|
243
|
+
// =========================================================================
|
|
244
|
+
|
|
245
|
+
private openWebSocket(): Promise<void> {
|
|
246
|
+
if (!this.wsUrl) throw new Error('No wsUrl available');
|
|
247
|
+
|
|
248
|
+
return new Promise((resolve, reject) => {
|
|
249
|
+
const ws = new WebSocket(this.wsUrl!);
|
|
250
|
+
this.ws = ws;
|
|
251
|
+
|
|
252
|
+
ws.onopen = () => {
|
|
253
|
+
this.reconnectAttempts = 0;
|
|
254
|
+
this.setState('connected');
|
|
255
|
+
resolve();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
ws.onmessage = (event) => {
|
|
259
|
+
try {
|
|
260
|
+
this.handleMessage(JSON.parse(event.data as string) as WsServerMessage);
|
|
261
|
+
} catch (err) {
|
|
262
|
+
this.debug('Failed to parse message:', err);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
ws.onclose = () => {
|
|
267
|
+
this.ws = null;
|
|
268
|
+
this.setState('disconnected');
|
|
269
|
+
|
|
270
|
+
if (!this.intentionalClose && !this.options.noAutoReconnect) {
|
|
271
|
+
this.scheduleReconnect();
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
ws.onerror = (err) => {
|
|
276
|
+
this.debug('WebSocket error:', err);
|
|
277
|
+
reject(err);
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private handleMessage(msg: WsServerMessage): void {
|
|
283
|
+
// Emit raw message to 'message' listeners
|
|
284
|
+
this.emit('message', msg);
|
|
285
|
+
|
|
286
|
+
if (msg.type === 'event') {
|
|
287
|
+
// Forward pi AgentSessionEvent to 'event' listeners
|
|
288
|
+
this.emit('event', msg.event);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (msg.type === 'response') {
|
|
293
|
+
// Resolve/reject pending request promise
|
|
294
|
+
const id = msg.id;
|
|
295
|
+
if (id) {
|
|
296
|
+
const pending = this.pending.get(id);
|
|
297
|
+
if (pending) {
|
|
298
|
+
this.pending.delete(id);
|
|
299
|
+
if (msg.success) {
|
|
300
|
+
pending.resolve(msg.data);
|
|
301
|
+
} else {
|
|
302
|
+
pending.reject(new Error(msg.error));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// =========================================================================
|
|
310
|
+
// Internal: request/response correlation
|
|
311
|
+
// =========================================================================
|
|
312
|
+
|
|
313
|
+
private request<T = void>(cmd: WsCommand): Promise<T> {
|
|
314
|
+
if (!this.isConnected()) throw new Error('Not connected');
|
|
315
|
+
|
|
316
|
+
const id = randomUUID();
|
|
317
|
+
return new Promise<T>((resolve, reject) => {
|
|
318
|
+
this.pending.set(id, {
|
|
319
|
+
resolve: (data) => resolve(data as T),
|
|
320
|
+
reject,
|
|
321
|
+
});
|
|
322
|
+
this.send({ ...cmd, id });
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private send(cmd: WsCommand): void {
|
|
327
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
328
|
+
this.ws.send(JSON.stringify(cmd));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// =========================================================================
|
|
333
|
+
// Internal: reconnect
|
|
334
|
+
// =========================================================================
|
|
335
|
+
|
|
336
|
+
private scheduleReconnect(): void {
|
|
337
|
+
const max = this.options.maxReconnectAttempts!;
|
|
338
|
+
if (this.reconnectAttempts >= max) {
|
|
339
|
+
this.debug(`Giving up after ${max} reconnect attempts`);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
this.reconnectAttempts++;
|
|
344
|
+
const delay = Math.min(
|
|
345
|
+
30_000,
|
|
346
|
+
this.options.reconnectDelay! * Math.pow(1.5, this.reconnectAttempts - 1),
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
this.setState('reconnecting');
|
|
350
|
+
this.debug(`Reconnect attempt ${this.reconnectAttempts}/${max} in ${delay}ms`);
|
|
351
|
+
|
|
352
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
353
|
+
try {
|
|
354
|
+
await this.openWebSocket();
|
|
355
|
+
} catch {
|
|
356
|
+
this.scheduleReconnect();
|
|
357
|
+
}
|
|
358
|
+
}, delay);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private clearReconnectTimer(): void {
|
|
362
|
+
if (this.reconnectTimer) {
|
|
363
|
+
clearTimeout(this.reconnectTimer);
|
|
364
|
+
this.reconnectTimer = null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// =========================================================================
|
|
369
|
+
// Internal: emit / state
|
|
370
|
+
// =========================================================================
|
|
371
|
+
|
|
372
|
+
private emit<E extends ClientEventType>(event: E, ...args: Parameters<ClientEventMap[E]>): void {
|
|
373
|
+
const set = this.listeners.get(event);
|
|
374
|
+
if (!set) return;
|
|
375
|
+
for (const listener of set) {
|
|
376
|
+
try {
|
|
377
|
+
(listener as (...a: unknown[]) => void)(...(args as unknown[]));
|
|
378
|
+
} catch (err) {
|
|
379
|
+
this.debug(`Error in '${event}' listener:`, err);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private setState(state: ClientState): void {
|
|
385
|
+
if (this.state !== state) {
|
|
386
|
+
this.state = state;
|
|
387
|
+
this.emit('state_change', state);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private debug(...args: unknown[]): void {
|
|
392
|
+
if (this.options.debug) console.log(LOG_PREFIX, ...args);
|
|
393
|
+
}
|
|
394
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,56 +1,25 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from './types.js';
|
|
3
|
-
|
|
4
|
-
// Re-export shared types explicitly for proper bundling with dts-bundle-generator
|
|
5
|
-
// (inlinedLibraries doesn't properly handle subpath exports like /browser)
|
|
6
|
-
export {
|
|
7
|
-
AgentEventType,
|
|
8
|
-
ServerMessageType,
|
|
9
|
-
ClientMessageType,
|
|
10
|
-
ServerErrorCode,
|
|
11
|
-
TodoStatus,
|
|
12
|
-
TodoPriority,
|
|
13
|
-
} from '@gnsx/genesys.agent.shared/browser';
|
|
1
|
+
export { GenesysAgentClient } from './client.js';
|
|
14
2
|
|
|
15
3
|
export type {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
SessionInfoMessage,
|
|
26
|
-
SessionRestoredMessage,
|
|
27
|
-
InputMessage,
|
|
28
|
-
// Event types
|
|
29
|
-
UserMessageEvent,
|
|
30
|
-
AgentMessageEvent,
|
|
31
|
-
StreamChunkEvent,
|
|
32
|
-
ToolCallEvent,
|
|
33
|
-
ToolResultEvent,
|
|
34
|
-
ToolErrorEvent,
|
|
35
|
-
ReasoningEvent,
|
|
36
|
-
TokenUsageEvent,
|
|
37
|
-
TodoItem,
|
|
38
|
-
TodoListEvent,
|
|
39
|
-
SendDataEvent,
|
|
40
|
-
ErrorEvent,
|
|
41
|
-
MessagesClearedEvent,
|
|
42
|
-
ModelUpdatedEvent,
|
|
43
|
-
RoundFinishedEvent,
|
|
44
|
-
} from '@gnsx/genesys.agent.shared/browser';
|
|
4
|
+
ClientOptions,
|
|
5
|
+
ClientState,
|
|
6
|
+
ClientEventType,
|
|
7
|
+
ClientEventMap,
|
|
8
|
+
AgentEventListener,
|
|
9
|
+
MessageListener,
|
|
10
|
+
StateChangeListener,
|
|
11
|
+
ModelInfo,
|
|
12
|
+
} from './types.js';
|
|
45
13
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
14
|
+
export type {
|
|
15
|
+
WsCommand,
|
|
16
|
+
WsServerMessage,
|
|
17
|
+
WsEvent,
|
|
18
|
+
WsResponse,
|
|
19
|
+
WsError,
|
|
20
|
+
WsSessionState,
|
|
21
|
+
CreateSessionResponse,
|
|
22
|
+
SessionInfoResponse,
|
|
23
|
+
ThinkingLevel,
|
|
24
|
+
ImageContent,
|
|
25
|
+
} from './protocol.js';
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types shared between the Genesys Agent v2 server and this client.
|
|
3
|
+
*
|
|
4
|
+
* Kept as a standalone file (no server imports) so the client package stays
|
|
5
|
+
* browser-compatible with zero Node.js dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Keep in sync with packages/server/src/protocol.ts.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Thinking levels (mirrors @mariozechner/pi-agent-core ThinkingLevel)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
export type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Image content (mirrors @mariozechner/pi-ai ImageContent)
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export interface ImageContent {
|
|
21
|
+
type: 'image';
|
|
22
|
+
mimeType: string;
|
|
23
|
+
data: string; // base64
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Client → Server
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
export type WsCommand =
|
|
31
|
+
| { id?: string; type: 'prompt'; message: string; images?: ImageContent[]; streamingBehavior?: 'steer' | 'followUp' }
|
|
32
|
+
| { id?: string; type: 'abort' }
|
|
33
|
+
| { id?: string; type: 'new_session' }
|
|
34
|
+
| { id?: string; type: 'get_state' }
|
|
35
|
+
| { id?: string; type: 'set_model'; provider: string; modelId: string }
|
|
36
|
+
| { id?: string; type: 'get_available_models' }
|
|
37
|
+
| { id?: string; type: 'set_thinking_level'; level: ThinkingLevel }
|
|
38
|
+
| { id?: string; type: 'compact'; customInstructions?: string }
|
|
39
|
+
| { id?: string; type: 'get_messages' };
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Server → Client
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
/** A pi AgentSessionEvent forwarded verbatim */
|
|
46
|
+
export interface WsEvent {
|
|
47
|
+
type: 'event';
|
|
48
|
+
event: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Successful command response */
|
|
52
|
+
export interface WsResponse {
|
|
53
|
+
type: 'response';
|
|
54
|
+
id?: string;
|
|
55
|
+
command: string;
|
|
56
|
+
success: true;
|
|
57
|
+
data?: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Error command response */
|
|
61
|
+
export interface WsError {
|
|
62
|
+
type: 'response';
|
|
63
|
+
id?: string;
|
|
64
|
+
command: string;
|
|
65
|
+
success: false;
|
|
66
|
+
error: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type WsServerMessage = WsEvent | WsResponse | WsError;
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Session state snapshot (returned by get_state)
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
export interface WsSessionState {
|
|
76
|
+
sessionId: string;
|
|
77
|
+
sessionFile?: string;
|
|
78
|
+
model?: { provider: string; id: string };
|
|
79
|
+
thinkingLevel: ThinkingLevel;
|
|
80
|
+
isStreaming: boolean;
|
|
81
|
+
isCompacting: boolean;
|
|
82
|
+
messageCount: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// HTTP types
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
/** Response from POST /sessions */
|
|
90
|
+
export interface CreateSessionResponse {
|
|
91
|
+
sessionId: string;
|
|
92
|
+
wsUrl: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Response from GET /sessions/:id */
|
|
96
|
+
export interface SessionInfoResponse {
|
|
97
|
+
sessionId: string;
|
|
98
|
+
createdAt: number;
|
|
99
|
+
isConnected: boolean;
|
|
100
|
+
}
|