agentxjs 1.9.0 → 1.9.2-dev
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/build.ts +26 -0
- package/package.json +8 -27
- package/src/RemoteClient.ts +221 -0
- package/src/index.ts +118 -0
- package/src/presentation/Presentation.ts +189 -0
- package/src/presentation/index.ts +32 -0
- package/src/presentation/reducer.ts +317 -0
- package/src/presentation/types.ts +111 -0
- package/src/types.ts +337 -0
- package/tsconfig.json +11 -0
- package/README.md +0 -393
- package/dist/browser.js +0 -275
- package/dist/browser.js.map +0 -12
- package/dist/index.d.ts +0 -58
- package/dist/index.js +0 -481
- package/dist/index.js.map +0 -13
package/build.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build script for agentxjs
|
|
3
|
+
*
|
|
4
|
+
* Generates dist/ with JS and .d.ts files
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { $ } from "bun";
|
|
8
|
+
|
|
9
|
+
console.log("Building agentxjs...");
|
|
10
|
+
|
|
11
|
+
// Clean dist
|
|
12
|
+
await $`rm -rf dist`;
|
|
13
|
+
|
|
14
|
+
// Generate .d.ts using tsc
|
|
15
|
+
await $`tsc --emitDeclarationOnly --outDir dist`;
|
|
16
|
+
|
|
17
|
+
// Build with bun
|
|
18
|
+
await Bun.build({
|
|
19
|
+
entrypoints: ["./src/index.ts"],
|
|
20
|
+
outdir: "./dist",
|
|
21
|
+
format: "esm",
|
|
22
|
+
target: "node",
|
|
23
|
+
sourcemap: "external",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
console.log("Build complete!");
|
package/package.json
CHANGED
|
@@ -1,49 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentxjs",
|
|
3
|
-
"version": "1.9.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.9.2-dev",
|
|
4
|
+
"description": "AgentX Client SDK - Connect to AgentX servers",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"engines": {
|
|
7
|
-
"node": ">=22.0.0"
|
|
8
|
-
},
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/Deepractice/AgentX.git"
|
|
12
|
-
},
|
|
13
|
-
"homepage": "https://github.com/Deepractice/AgentX",
|
|
14
6
|
"type": "module",
|
|
15
7
|
"main": "./dist/index.js",
|
|
16
8
|
"types": "./dist/index.d.ts",
|
|
17
9
|
"exports": {
|
|
18
10
|
".": {
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"types": "./dist/index.d.ts",
|
|
22
|
-
"default": "./dist/index.js"
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"default": "./src/index.ts"
|
|
23
13
|
}
|
|
24
14
|
},
|
|
25
|
-
"files": [
|
|
26
|
-
"dist"
|
|
27
|
-
],
|
|
28
15
|
"scripts": {
|
|
29
16
|
"build": "bun run build.ts",
|
|
30
|
-
"clean": "rm -rf dist test-results",
|
|
31
|
-
"lint": "eslint .",
|
|
32
17
|
"typecheck": "tsc --noEmit",
|
|
33
|
-
"test": "bun test"
|
|
34
|
-
"test:watch": "bun test --watch"
|
|
18
|
+
"test": "bun test"
|
|
35
19
|
},
|
|
36
20
|
"dependencies": {
|
|
37
|
-
"@agentxjs/
|
|
38
|
-
"@agentxjs/network": "^1.9.0",
|
|
39
|
-
"@agentxjs/types": "^1.9.0",
|
|
21
|
+
"@agentxjs/core": "workspace:*",
|
|
40
22
|
"reconnecting-websocket": "^4.4.0",
|
|
41
23
|
"ws": "^8.18.0"
|
|
42
24
|
},
|
|
43
25
|
"devDependencies": {
|
|
44
|
-
"@
|
|
45
|
-
"
|
|
46
|
-
"@agentxjs/runtime": "^1.9.0"
|
|
26
|
+
"@types/ws": "^8.5.10",
|
|
27
|
+
"typescript": "^5.3.3"
|
|
47
28
|
},
|
|
48
29
|
"publishConfig": {
|
|
49
30
|
"access": "public"
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemoteClient - AgentX client for remote server
|
|
3
|
+
*
|
|
4
|
+
* Uses RpcClient from @agentxjs/core/network for JSON-RPC communication.
|
|
5
|
+
* This class focuses on business logic, not protocol details.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { BusEvent, EventBus, BusEventHandler, Unsubscribe } from "@agentxjs/core/event";
|
|
9
|
+
import { EventBusImpl } from "@agentxjs/core/event";
|
|
10
|
+
import { RpcClient, type RpcMethod } from "@agentxjs/core/network";
|
|
11
|
+
import { createLogger } from "commonxjs/logger";
|
|
12
|
+
import type {
|
|
13
|
+
AgentX,
|
|
14
|
+
AgentXConfig,
|
|
15
|
+
AgentCreateResponse,
|
|
16
|
+
AgentGetResponse,
|
|
17
|
+
AgentListResponse,
|
|
18
|
+
ImageCreateResponse,
|
|
19
|
+
ImageGetResponse,
|
|
20
|
+
ImageListResponse,
|
|
21
|
+
ContainerCreateResponse,
|
|
22
|
+
ContainerGetResponse,
|
|
23
|
+
ContainerListResponse,
|
|
24
|
+
MessageSendResponse,
|
|
25
|
+
BaseResponse,
|
|
26
|
+
} from "./types";
|
|
27
|
+
import { Presentation, type PresentationOptions } from "./presentation";
|
|
28
|
+
|
|
29
|
+
const logger = createLogger("agentx/RemoteClient");
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* RemoteClient implementation using JSON-RPC 2.0
|
|
33
|
+
*/
|
|
34
|
+
export class RemoteClient implements AgentX {
|
|
35
|
+
private readonly config: AgentXConfig;
|
|
36
|
+
private readonly eventBus: EventBus;
|
|
37
|
+
private readonly rpcClient: RpcClient;
|
|
38
|
+
|
|
39
|
+
constructor(config: AgentXConfig) {
|
|
40
|
+
this.config = config;
|
|
41
|
+
this.eventBus = new EventBusImpl();
|
|
42
|
+
|
|
43
|
+
// Create RPC client
|
|
44
|
+
this.rpcClient = new RpcClient({
|
|
45
|
+
url: config.serverUrl,
|
|
46
|
+
timeout: config.timeout ?? 30000,
|
|
47
|
+
autoReconnect: config.autoReconnect ?? true,
|
|
48
|
+
headers: config.headers as Record<string, string> | undefined,
|
|
49
|
+
debug: false,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Forward stream events to internal event bus
|
|
53
|
+
this.rpcClient.onStreamEvent((topic, event) => {
|
|
54
|
+
logger.debug("Received stream event", { topic, type: event.type });
|
|
55
|
+
this.eventBus.emit(event as BusEvent);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ==================== Properties ====================
|
|
60
|
+
|
|
61
|
+
get connected(): boolean {
|
|
62
|
+
return this.rpcClient.connected;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get events(): EventBus {
|
|
66
|
+
return this.eventBus;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ==================== Connection ====================
|
|
70
|
+
|
|
71
|
+
async connect(): Promise<void> {
|
|
72
|
+
await this.rpcClient.connect();
|
|
73
|
+
logger.info("Connected to server", { url: this.config.serverUrl });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async disconnect(): Promise<void> {
|
|
77
|
+
this.rpcClient.disconnect();
|
|
78
|
+
logger.info("Disconnected from server");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async dispose(): Promise<void> {
|
|
82
|
+
this.rpcClient.dispose();
|
|
83
|
+
this.eventBus.destroy();
|
|
84
|
+
logger.info("RemoteClient disposed");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ==================== Container Operations ====================
|
|
88
|
+
|
|
89
|
+
async createContainer(containerId: string): Promise<ContainerCreateResponse> {
|
|
90
|
+
const result = await this.rpcClient.call<ContainerCreateResponse>("container.create", {
|
|
91
|
+
containerId,
|
|
92
|
+
});
|
|
93
|
+
return { ...result, requestId: "" };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getContainer(containerId: string): Promise<ContainerGetResponse> {
|
|
97
|
+
const result = await this.rpcClient.call<ContainerGetResponse>("container.get", {
|
|
98
|
+
containerId,
|
|
99
|
+
});
|
|
100
|
+
return { ...result, requestId: "" };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async listContainers(): Promise<ContainerListResponse> {
|
|
104
|
+
const result = await this.rpcClient.call<ContainerListResponse>("container.list", {});
|
|
105
|
+
return { ...result, requestId: "" };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ==================== Image Operations ====================
|
|
109
|
+
|
|
110
|
+
async createImage(params: {
|
|
111
|
+
containerId: string;
|
|
112
|
+
name?: string;
|
|
113
|
+
description?: string;
|
|
114
|
+
systemPrompt?: string;
|
|
115
|
+
mcpServers?: Record<string, unknown>;
|
|
116
|
+
}): Promise<ImageCreateResponse> {
|
|
117
|
+
const result = await this.rpcClient.call<ImageCreateResponse>("image.create", params);
|
|
118
|
+
|
|
119
|
+
// Auto subscribe to session events
|
|
120
|
+
if (result.__subscriptions) {
|
|
121
|
+
for (const sessionId of result.__subscriptions) {
|
|
122
|
+
this.subscribe(sessionId);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { ...result, requestId: "" };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async getImage(imageId: string): Promise<ImageGetResponse> {
|
|
130
|
+
const result = await this.rpcClient.call<ImageGetResponse>("image.get", { imageId });
|
|
131
|
+
|
|
132
|
+
// Auto subscribe
|
|
133
|
+
if (result.__subscriptions) {
|
|
134
|
+
for (const sessionId of result.__subscriptions) {
|
|
135
|
+
this.subscribe(sessionId);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { ...result, requestId: "" };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async listImages(containerId?: string): Promise<ImageListResponse> {
|
|
143
|
+
const result = await this.rpcClient.call<ImageListResponse>("image.list", { containerId });
|
|
144
|
+
|
|
145
|
+
// Auto subscribe
|
|
146
|
+
if (result.__subscriptions) {
|
|
147
|
+
for (const sessionId of result.__subscriptions) {
|
|
148
|
+
this.subscribe(sessionId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { ...result, requestId: "" };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async deleteImage(imageId: string): Promise<BaseResponse> {
|
|
156
|
+
const result = await this.rpcClient.call<BaseResponse>("image.delete", { imageId });
|
|
157
|
+
return { ...result, requestId: "" };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ==================== Agent Operations ====================
|
|
161
|
+
|
|
162
|
+
async createAgent(params: { imageId: string; agentId?: string }): Promise<AgentCreateResponse> {
|
|
163
|
+
// Agent creation via image.run RPC
|
|
164
|
+
const result = await this.rpcClient.call<AgentCreateResponse>("image.run" as RpcMethod, {
|
|
165
|
+
imageId: params.imageId,
|
|
166
|
+
agentId: params.agentId,
|
|
167
|
+
});
|
|
168
|
+
return { ...result, requestId: "" };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async getAgent(agentId: string): Promise<AgentGetResponse> {
|
|
172
|
+
const result = await this.rpcClient.call<AgentGetResponse>("agent.get", { agentId });
|
|
173
|
+
return { ...result, requestId: "" };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async listAgents(containerId?: string): Promise<AgentListResponse> {
|
|
177
|
+
const result = await this.rpcClient.call<AgentListResponse>("agent.list", { containerId });
|
|
178
|
+
return { ...result, requestId: "" };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async destroyAgent(agentId: string): Promise<BaseResponse> {
|
|
182
|
+
const result = await this.rpcClient.call<BaseResponse>("agent.destroy", { agentId });
|
|
183
|
+
return { ...result, requestId: "" };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ==================== Message Operations ====================
|
|
187
|
+
|
|
188
|
+
async sendMessage(agentId: string, content: string | unknown[]): Promise<MessageSendResponse> {
|
|
189
|
+
const result = await this.rpcClient.call<MessageSendResponse>("message.send", {
|
|
190
|
+
agentId,
|
|
191
|
+
content,
|
|
192
|
+
});
|
|
193
|
+
return { ...result, requestId: "" };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async interrupt(agentId: string): Promise<BaseResponse> {
|
|
197
|
+
const result = await this.rpcClient.call<BaseResponse>("agent.interrupt", { agentId });
|
|
198
|
+
return { ...result, requestId: "" };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ==================== Event Subscription ====================
|
|
202
|
+
|
|
203
|
+
on<T extends string>(type: T, handler: BusEventHandler<BusEvent & { type: T }>): Unsubscribe {
|
|
204
|
+
return this.eventBus.on(type, handler);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
onAny(handler: BusEventHandler): Unsubscribe {
|
|
208
|
+
return this.eventBus.onAny(handler);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
subscribe(sessionId: string): void {
|
|
212
|
+
this.rpcClient.subscribe(sessionId);
|
|
213
|
+
logger.debug("Subscribed to session", { sessionId });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ==================== Presentation ====================
|
|
217
|
+
|
|
218
|
+
presentation(agentId: string, options?: PresentationOptions): Presentation {
|
|
219
|
+
return new Presentation(this, agentId, options);
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentxjs - AgentX Client SDK
|
|
3
|
+
*
|
|
4
|
+
* Connect to AgentX servers from Node.js and browsers.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { createAgentX } from "agentxjs";
|
|
9
|
+
*
|
|
10
|
+
* // Connect to server
|
|
11
|
+
* const agentx = await createAgentX({
|
|
12
|
+
* serverUrl: "ws://localhost:5200",
|
|
13
|
+
* headers: { Authorization: "Bearer sk-xxx" },
|
|
14
|
+
* context: { userId: "123", tenantId: "abc" },
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Create container and image
|
|
18
|
+
* await agentx.createContainer("my-container");
|
|
19
|
+
* const { record: image } = await agentx.createImage({
|
|
20
|
+
* containerId: "my-container",
|
|
21
|
+
* name: "Assistant",
|
|
22
|
+
* systemPrompt: "You are a helpful assistant",
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Create agent and send message
|
|
26
|
+
* const { agentId } = await agentx.createAgent({ imageId: image.imageId });
|
|
27
|
+
*
|
|
28
|
+
* // Subscribe to events
|
|
29
|
+
* agentx.on("text_delta", (event) => {
|
|
30
|
+
* process.stdout.write(event.data.text);
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* agentx.on("assistant_message", (event) => {
|
|
34
|
+
* console.log("Complete:", event.data.content);
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* // Send message
|
|
38
|
+
* await agentx.sendMessage(agentId, "Hello!");
|
|
39
|
+
*
|
|
40
|
+
* // Cleanup
|
|
41
|
+
* await agentx.dispose();
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example Dynamic headers and context
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const agentx = await createAgentX({
|
|
47
|
+
* serverUrl: "ws://localhost:5200",
|
|
48
|
+
* headers: () => ({ Authorization: `Bearer ${getToken()}` }),
|
|
49
|
+
* context: async () => ({
|
|
50
|
+
* userId: await getUserId(),
|
|
51
|
+
* permissions: await getPermissions(),
|
|
52
|
+
* }),
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
import { RemoteClient } from "./RemoteClient";
|
|
58
|
+
import type { AgentX, AgentXConfig } from "./types";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create an AgentX client
|
|
62
|
+
*
|
|
63
|
+
* @param config - Client configuration
|
|
64
|
+
* @returns Connected AgentX client
|
|
65
|
+
*/
|
|
66
|
+
export async function createAgentX(config: AgentXConfig): Promise<AgentX> {
|
|
67
|
+
const client = new RemoteClient(config);
|
|
68
|
+
await client.connect();
|
|
69
|
+
return client;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Re-export types
|
|
73
|
+
export type {
|
|
74
|
+
AgentX,
|
|
75
|
+
AgentXConfig,
|
|
76
|
+
MaybeAsync,
|
|
77
|
+
AgentInfo,
|
|
78
|
+
ImageRecord,
|
|
79
|
+
ContainerInfo,
|
|
80
|
+
AgentCreateResponse,
|
|
81
|
+
AgentGetResponse,
|
|
82
|
+
AgentListResponse,
|
|
83
|
+
ImageCreateResponse,
|
|
84
|
+
ImageGetResponse,
|
|
85
|
+
ImageListResponse,
|
|
86
|
+
ContainerCreateResponse,
|
|
87
|
+
ContainerGetResponse,
|
|
88
|
+
ContainerListResponse,
|
|
89
|
+
MessageSendResponse,
|
|
90
|
+
BaseResponse,
|
|
91
|
+
} from "./types";
|
|
92
|
+
|
|
93
|
+
// Re-export RemoteClient for advanced use
|
|
94
|
+
export { RemoteClient } from "./RemoteClient";
|
|
95
|
+
|
|
96
|
+
// Re-export Presentation types and classes
|
|
97
|
+
export type {
|
|
98
|
+
Block,
|
|
99
|
+
TextBlock,
|
|
100
|
+
ToolBlock,
|
|
101
|
+
ImageBlock,
|
|
102
|
+
Conversation,
|
|
103
|
+
UserConversation,
|
|
104
|
+
AssistantConversation,
|
|
105
|
+
ErrorConversation,
|
|
106
|
+
PresentationState,
|
|
107
|
+
PresentationOptions,
|
|
108
|
+
PresentationUpdateHandler,
|
|
109
|
+
PresentationErrorHandler,
|
|
110
|
+
} from "./presentation";
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
Presentation,
|
|
114
|
+
presentationReducer,
|
|
115
|
+
addUserConversation,
|
|
116
|
+
createInitialState,
|
|
117
|
+
initialPresentationState,
|
|
118
|
+
} from "./presentation";
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation Class
|
|
3
|
+
*
|
|
4
|
+
* High-level API for UI integration.
|
|
5
|
+
* Wraps AgentX client and provides presentation state management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AgentX } from "../types";
|
|
9
|
+
import type { Unsubscribe, BusEvent } from "@agentxjs/core/event";
|
|
10
|
+
import type { PresentationState } from "./types";
|
|
11
|
+
import {
|
|
12
|
+
presentationReducer,
|
|
13
|
+
addUserConversation,
|
|
14
|
+
createInitialState,
|
|
15
|
+
} from "./reducer";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Presentation update handler
|
|
19
|
+
*/
|
|
20
|
+
export type PresentationUpdateHandler = (state: PresentationState) => void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Presentation error handler
|
|
24
|
+
*/
|
|
25
|
+
export type PresentationErrorHandler = (error: Error) => void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Presentation options
|
|
29
|
+
*/
|
|
30
|
+
export interface PresentationOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Called on every state update
|
|
33
|
+
*/
|
|
34
|
+
onUpdate?: PresentationUpdateHandler;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Called on errors
|
|
38
|
+
*/
|
|
39
|
+
onError?: PresentationErrorHandler;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Presentation - UI-friendly wrapper for AgentX
|
|
44
|
+
*/
|
|
45
|
+
export class Presentation {
|
|
46
|
+
private agentx: AgentX;
|
|
47
|
+
private agentId: string;
|
|
48
|
+
private state: PresentationState;
|
|
49
|
+
private updateHandlers: Set<PresentationUpdateHandler> = new Set();
|
|
50
|
+
private errorHandlers: Set<PresentationErrorHandler> = new Set();
|
|
51
|
+
private eventUnsubscribe: Unsubscribe | null = null;
|
|
52
|
+
|
|
53
|
+
constructor(agentx: AgentX, agentId: string, options?: PresentationOptions) {
|
|
54
|
+
this.agentx = agentx;
|
|
55
|
+
this.agentId = agentId;
|
|
56
|
+
this.state = createInitialState();
|
|
57
|
+
|
|
58
|
+
// Register initial handlers
|
|
59
|
+
if (options?.onUpdate) {
|
|
60
|
+
this.updateHandlers.add(options.onUpdate);
|
|
61
|
+
}
|
|
62
|
+
if (options?.onError) {
|
|
63
|
+
this.errorHandlers.add(options.onError);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Subscribe to all events
|
|
67
|
+
this.subscribeToEvents();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get current state
|
|
72
|
+
*/
|
|
73
|
+
getState(): PresentationState {
|
|
74
|
+
return this.state;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Subscribe to state updates
|
|
79
|
+
*/
|
|
80
|
+
onUpdate(handler: PresentationUpdateHandler): Unsubscribe {
|
|
81
|
+
this.updateHandlers.add(handler);
|
|
82
|
+
// Immediately call with current state
|
|
83
|
+
handler(this.state);
|
|
84
|
+
return () => {
|
|
85
|
+
this.updateHandlers.delete(handler);
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to errors
|
|
91
|
+
*/
|
|
92
|
+
onError(handler: PresentationErrorHandler): Unsubscribe {
|
|
93
|
+
this.errorHandlers.add(handler);
|
|
94
|
+
return () => {
|
|
95
|
+
this.errorHandlers.delete(handler);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Send a message
|
|
101
|
+
*/
|
|
102
|
+
async send(content: string): Promise<void> {
|
|
103
|
+
// Add user conversation
|
|
104
|
+
this.state = addUserConversation(this.state, content);
|
|
105
|
+
this.notify();
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Send message via agentx
|
|
109
|
+
await this.agentx.sendMessage(this.agentId, content);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
this.notifyError(error instanceof Error ? error : new Error(String(error)));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Interrupt current response
|
|
117
|
+
*/
|
|
118
|
+
async interrupt(): Promise<void> {
|
|
119
|
+
try {
|
|
120
|
+
await this.agentx.interrupt(this.agentId);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
this.notifyError(error instanceof Error ? error : new Error(String(error)));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Reset state
|
|
128
|
+
*/
|
|
129
|
+
reset(): void {
|
|
130
|
+
this.state = createInitialState();
|
|
131
|
+
this.notify();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Dispose and cleanup
|
|
136
|
+
*/
|
|
137
|
+
dispose(): void {
|
|
138
|
+
if (this.eventUnsubscribe) {
|
|
139
|
+
this.eventUnsubscribe();
|
|
140
|
+
this.eventUnsubscribe = null;
|
|
141
|
+
}
|
|
142
|
+
this.updateHandlers.clear();
|
|
143
|
+
this.errorHandlers.clear();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ==================== Private ====================
|
|
147
|
+
|
|
148
|
+
private subscribeToEvents(): void {
|
|
149
|
+
// Subscribe to all events and filter by agentId
|
|
150
|
+
this.eventUnsubscribe = this.agentx.onAny((event: BusEvent) => {
|
|
151
|
+
// Filter events for this agent (if context is available)
|
|
152
|
+
// Note: Events from server may or may not include context with agentId
|
|
153
|
+
const eventWithContext = event as BusEvent & { context?: { agentId?: string } };
|
|
154
|
+
const eventAgentId = eventWithContext.context?.agentId;
|
|
155
|
+
|
|
156
|
+
// Only filter if event has agentId and it doesn't match
|
|
157
|
+
if (eventAgentId && eventAgentId !== this.agentId) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Reduce event into state
|
|
162
|
+
const newState = presentationReducer(this.state, event);
|
|
163
|
+
if (newState !== this.state) {
|
|
164
|
+
this.state = newState;
|
|
165
|
+
this.notify();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private notify(): void {
|
|
171
|
+
for (const handler of this.updateHandlers) {
|
|
172
|
+
try {
|
|
173
|
+
handler(this.state);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error("Presentation update handler error:", error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private notifyError(error: Error): void {
|
|
181
|
+
for (const handler of this.errorHandlers) {
|
|
182
|
+
try {
|
|
183
|
+
handler(error);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
console.error("Presentation error handler error:", e);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation Module
|
|
3
|
+
*
|
|
4
|
+
* UI-friendly data model and state management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
Block,
|
|
9
|
+
TextBlock,
|
|
10
|
+
ToolBlock,
|
|
11
|
+
ImageBlock,
|
|
12
|
+
Conversation,
|
|
13
|
+
UserConversation,
|
|
14
|
+
AssistantConversation,
|
|
15
|
+
ErrorConversation,
|
|
16
|
+
PresentationState,
|
|
17
|
+
} from "./types";
|
|
18
|
+
|
|
19
|
+
export { initialPresentationState } from "./types";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
presentationReducer,
|
|
23
|
+
addUserConversation,
|
|
24
|
+
createInitialState,
|
|
25
|
+
} from "./reducer";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
Presentation,
|
|
29
|
+
type PresentationOptions,
|
|
30
|
+
type PresentationUpdateHandler,
|
|
31
|
+
type PresentationErrorHandler,
|
|
32
|
+
} from "./Presentation";
|