agentxjs 0.0.0-dev-20260312143810
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 +293 -0
- package/dist/chunk-X44CQZPK.js +569 -0
- package/dist/chunk-X44CQZPK.js.map +1 -0
- package/dist/index.d.ts +787 -0
- package/dist/index.js +1088 -0
- package/dist/index.js.map +1 -0
- package/dist/server-BWI5JE4B.js +7 -0
- package/dist/server-BWI5JE4B.js.map +1 -0
- package/package.json +39 -0
- package/src/CommandHandler.ts +557 -0
- package/src/LocalClient.ts +118 -0
- package/src/RemoteClient.ts +132 -0
- package/src/index.ts +228 -0
- package/src/namespaces/agents.ts +121 -0
- package/src/namespaces/containers.ts +68 -0
- package/src/namespaces/images.ts +192 -0
- package/src/namespaces/llm.ts +140 -0
- package/src/namespaces/presentations.ts +22 -0
- package/src/namespaces/sessions.ts +60 -0
- package/src/presentation/Presentation.ts +193 -0
- package/src/presentation/index.ts +31 -0
- package/src/presentation/reducer.ts +486 -0
- package/src/presentation/types.ts +121 -0
- package/src/server.ts +346 -0
- package/src/types.ts +558 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image namespace factories
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Message } from "@agentxjs/core/agent";
|
|
6
|
+
import type { RpcClient } from "@agentxjs/core/network";
|
|
7
|
+
import type { AgentXPlatform } from "@agentxjs/core/runtime";
|
|
8
|
+
import type {
|
|
9
|
+
BaseResponse,
|
|
10
|
+
ImageCreateResponse,
|
|
11
|
+
ImageGetResponse,
|
|
12
|
+
ImageListResponse,
|
|
13
|
+
ImageNamespace,
|
|
14
|
+
ImageUpdateResponse,
|
|
15
|
+
} from "../types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create local image namespace backed by embedded runtime
|
|
19
|
+
*/
|
|
20
|
+
export function createLocalImages(platform: AgentXPlatform): ImageNamespace {
|
|
21
|
+
return {
|
|
22
|
+
async create(params: {
|
|
23
|
+
containerId: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
systemPrompt?: string;
|
|
27
|
+
mcpServers?: Record<string, unknown>;
|
|
28
|
+
customData?: Record<string, unknown>;
|
|
29
|
+
}): Promise<ImageCreateResponse> {
|
|
30
|
+
const { imageRepository, sessionRepository } = platform;
|
|
31
|
+
const { createImage } = await import("@agentxjs/core/image");
|
|
32
|
+
|
|
33
|
+
const image = await createImage(
|
|
34
|
+
{
|
|
35
|
+
containerId: params.containerId,
|
|
36
|
+
name: params.name,
|
|
37
|
+
description: params.description,
|
|
38
|
+
systemPrompt: params.systemPrompt,
|
|
39
|
+
mcpServers: params.mcpServers as any,
|
|
40
|
+
customData: params.customData,
|
|
41
|
+
},
|
|
42
|
+
{ imageRepository, sessionRepository }
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
record: image.toRecord(),
|
|
47
|
+
__subscriptions: [image.sessionId],
|
|
48
|
+
requestId: "",
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async get(imageId: string): Promise<ImageGetResponse> {
|
|
53
|
+
const record = await platform.imageRepository.findImageById(imageId);
|
|
54
|
+
return {
|
|
55
|
+
record,
|
|
56
|
+
__subscriptions: record?.sessionId ? [record.sessionId] : undefined,
|
|
57
|
+
requestId: "",
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async list(containerId?: string): Promise<ImageListResponse> {
|
|
62
|
+
const records = containerId
|
|
63
|
+
? await platform.imageRepository.findImagesByContainerId(containerId)
|
|
64
|
+
: await platform.imageRepository.findAllImages();
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
records,
|
|
68
|
+
__subscriptions: records.map((r) => r.sessionId),
|
|
69
|
+
requestId: "",
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async update(
|
|
74
|
+
imageId: string,
|
|
75
|
+
updates: {
|
|
76
|
+
name?: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
customData?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
): Promise<ImageUpdateResponse> {
|
|
81
|
+
const { loadImage } = await import("@agentxjs/core/image");
|
|
82
|
+
const { imageRepository, sessionRepository } = platform;
|
|
83
|
+
|
|
84
|
+
const image = await loadImage(imageId, { imageRepository, sessionRepository });
|
|
85
|
+
if (!image) {
|
|
86
|
+
throw new Error(`Image not found: ${imageId}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const updated = await image.update(updates);
|
|
90
|
+
return { record: updated.toRecord(), requestId: "" };
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async delete(imageId: string): Promise<BaseResponse> {
|
|
94
|
+
const { loadImage } = await import("@agentxjs/core/image");
|
|
95
|
+
const { imageRepository, sessionRepository } = platform;
|
|
96
|
+
|
|
97
|
+
const image = await loadImage(imageId, { imageRepository, sessionRepository });
|
|
98
|
+
if (image) {
|
|
99
|
+
await image.delete();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { requestId: "" };
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async getMessages(imageId: string): Promise<Message[]> {
|
|
106
|
+
const imageRecord = await platform.imageRepository.findImageById(imageId);
|
|
107
|
+
if (!imageRecord) return [];
|
|
108
|
+
return platform.sessionRepository.getMessages(imageRecord.sessionId);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Create remote image namespace backed by RPC client
|
|
115
|
+
*/
|
|
116
|
+
export function createRemoteImages(
|
|
117
|
+
rpcClient: RpcClient,
|
|
118
|
+
subscribeFn: (sessionId: string) => void
|
|
119
|
+
): ImageNamespace {
|
|
120
|
+
return {
|
|
121
|
+
async create(params: {
|
|
122
|
+
containerId: string;
|
|
123
|
+
name?: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
systemPrompt?: string;
|
|
126
|
+
mcpServers?: Record<string, unknown>;
|
|
127
|
+
customData?: Record<string, unknown>;
|
|
128
|
+
}): Promise<ImageCreateResponse> {
|
|
129
|
+
const result = await rpcClient.call<ImageCreateResponse>("image.create", params);
|
|
130
|
+
|
|
131
|
+
// Auto subscribe to session events
|
|
132
|
+
if (result.__subscriptions) {
|
|
133
|
+
for (const sessionId of result.__subscriptions) {
|
|
134
|
+
subscribeFn(sessionId);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { ...result, requestId: "" };
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async get(imageId: string): Promise<ImageGetResponse> {
|
|
142
|
+
const result = await rpcClient.call<ImageGetResponse>("image.get", { imageId });
|
|
143
|
+
|
|
144
|
+
// Auto subscribe
|
|
145
|
+
if (result.__subscriptions) {
|
|
146
|
+
for (const sessionId of result.__subscriptions) {
|
|
147
|
+
subscribeFn(sessionId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { ...result, requestId: "" };
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
async list(containerId?: string): Promise<ImageListResponse> {
|
|
155
|
+
const result = await rpcClient.call<ImageListResponse>("image.list", { containerId });
|
|
156
|
+
|
|
157
|
+
// Auto subscribe
|
|
158
|
+
if (result.__subscriptions) {
|
|
159
|
+
for (const sessionId of result.__subscriptions) {
|
|
160
|
+
subscribeFn(sessionId);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return { ...result, requestId: "" };
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
async update(
|
|
168
|
+
imageId: string,
|
|
169
|
+
updates: {
|
|
170
|
+
name?: string;
|
|
171
|
+
description?: string;
|
|
172
|
+
customData?: Record<string, unknown>;
|
|
173
|
+
}
|
|
174
|
+
): Promise<ImageUpdateResponse> {
|
|
175
|
+
const result = await rpcClient.call<ImageUpdateResponse>("image.update", {
|
|
176
|
+
imageId,
|
|
177
|
+
updates,
|
|
178
|
+
});
|
|
179
|
+
return { ...result, requestId: "" };
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
async delete(imageId: string): Promise<BaseResponse> {
|
|
183
|
+
const result = await rpcClient.call<BaseResponse>("image.delete", { imageId });
|
|
184
|
+
return { ...result, requestId: "" };
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
async getMessages(imageId: string): Promise<Message[]> {
|
|
188
|
+
const result = await rpcClient.call<{ messages: Message[] }>("image.messages", { imageId });
|
|
189
|
+
return result.messages ?? [];
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Provider namespace factories
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { RpcClient } from "@agentxjs/core/network";
|
|
6
|
+
import type { LLMProviderRecord } from "@agentxjs/core/persistence";
|
|
7
|
+
import type { AgentXPlatform } from "@agentxjs/core/runtime";
|
|
8
|
+
import { generateId } from "@deepracticex/id";
|
|
9
|
+
import type {
|
|
10
|
+
BaseResponse,
|
|
11
|
+
LLMNamespace,
|
|
12
|
+
LLMProviderCreateResponse,
|
|
13
|
+
LLMProviderDefaultResponse,
|
|
14
|
+
LLMProviderGetResponse,
|
|
15
|
+
LLMProviderListResponse,
|
|
16
|
+
LLMProviderUpdateResponse,
|
|
17
|
+
} from "../types";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create local LLM namespace backed by platform repository
|
|
21
|
+
*/
|
|
22
|
+
export function createLocalLLM(platform: AgentXPlatform): LLMNamespace {
|
|
23
|
+
const repo = platform.llmProviderRepository;
|
|
24
|
+
if (!repo) {
|
|
25
|
+
throw new Error("LLM provider repository not available on this platform");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
async create(params): Promise<LLMProviderCreateResponse> {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const record: LLMProviderRecord = {
|
|
32
|
+
id: generateId("llm"),
|
|
33
|
+
containerId: params.containerId,
|
|
34
|
+
name: params.name,
|
|
35
|
+
vendor: params.vendor,
|
|
36
|
+
protocol: params.protocol,
|
|
37
|
+
apiKey: params.apiKey,
|
|
38
|
+
baseUrl: params.baseUrl,
|
|
39
|
+
model: params.model,
|
|
40
|
+
isDefault: false,
|
|
41
|
+
createdAt: now,
|
|
42
|
+
updatedAt: now,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
await repo.saveLLMProvider(record);
|
|
46
|
+
return { record, requestId: "" };
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async get(id: string): Promise<LLMProviderGetResponse> {
|
|
50
|
+
const record = await repo.findLLMProviderById(id);
|
|
51
|
+
return { record, requestId: "" };
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
async list(containerId: string): Promise<LLMProviderListResponse> {
|
|
55
|
+
const records = await repo.findLLMProvidersByContainerId(containerId);
|
|
56
|
+
return { records, requestId: "" };
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
async update(id, updates): Promise<LLMProviderUpdateResponse> {
|
|
60
|
+
const existing = await repo.findLLMProviderById(id);
|
|
61
|
+
if (!existing) {
|
|
62
|
+
throw new Error(`LLM provider not found: ${id}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const updated: LLMProviderRecord = {
|
|
66
|
+
...existing,
|
|
67
|
+
...updates,
|
|
68
|
+
id: existing.id,
|
|
69
|
+
containerId: existing.containerId,
|
|
70
|
+
createdAt: existing.createdAt,
|
|
71
|
+
updatedAt: Date.now(),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
await repo.saveLLMProvider(updated);
|
|
75
|
+
return { record: updated, requestId: "" };
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
async delete(id: string): Promise<BaseResponse> {
|
|
79
|
+
await repo.deleteLLMProvider(id);
|
|
80
|
+
return { requestId: "" };
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async setDefault(id: string): Promise<BaseResponse> {
|
|
84
|
+
await repo.setDefaultLLMProvider(id);
|
|
85
|
+
return { requestId: "" };
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
async getDefault(containerId: string): Promise<LLMProviderDefaultResponse> {
|
|
89
|
+
const record = await repo.findDefaultLLMProvider(containerId);
|
|
90
|
+
return { record, requestId: "" };
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create remote LLM namespace backed by RPC client
|
|
97
|
+
*/
|
|
98
|
+
export function createRemoteLLM(rpcClient: RpcClient): LLMNamespace {
|
|
99
|
+
return {
|
|
100
|
+
async create(params): Promise<LLMProviderCreateResponse> {
|
|
101
|
+
const result = await rpcClient.call<LLMProviderCreateResponse>("llm.create", params);
|
|
102
|
+
return { ...result, requestId: "" };
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async get(id: string): Promise<LLMProviderGetResponse> {
|
|
106
|
+
const result = await rpcClient.call<LLMProviderGetResponse>("llm.get", { id });
|
|
107
|
+
return { ...result, requestId: "" };
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async list(containerId: string): Promise<LLMProviderListResponse> {
|
|
111
|
+
const result = await rpcClient.call<LLMProviderListResponse>("llm.list", { containerId });
|
|
112
|
+
return { ...result, requestId: "" };
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
async update(id, updates): Promise<LLMProviderUpdateResponse> {
|
|
116
|
+
const result = await rpcClient.call<LLMProviderUpdateResponse>("llm.update", {
|
|
117
|
+
id,
|
|
118
|
+
updates,
|
|
119
|
+
});
|
|
120
|
+
return { ...result, requestId: "" };
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
async delete(id: string): Promise<BaseResponse> {
|
|
124
|
+
const result = await rpcClient.call<BaseResponse>("llm.delete", { id });
|
|
125
|
+
return { ...result, requestId: "" };
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
async setDefault(id: string): Promise<BaseResponse> {
|
|
129
|
+
const result = await rpcClient.call<BaseResponse>("llm.default", { id });
|
|
130
|
+
return { ...result, requestId: "" };
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
async getDefault(containerId: string): Promise<LLMProviderDefaultResponse> {
|
|
134
|
+
const result = await rpcClient.call<LLMProviderDefaultResponse>("llm.default", {
|
|
135
|
+
containerId,
|
|
136
|
+
});
|
|
137
|
+
return { ...result, requestId: "" };
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation namespace factory
|
|
3
|
+
*
|
|
4
|
+
* Single factory for both local and remote modes —
|
|
5
|
+
* Presentation only depends on the AgentX interface.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { messagesToConversations, Presentation, type PresentationOptions } from "../presentation";
|
|
9
|
+
import type { AgentX, PresentationNamespace } from "../types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create presentation namespace backed by any AgentX client
|
|
13
|
+
*/
|
|
14
|
+
export function createPresentations(agentx: AgentX): PresentationNamespace {
|
|
15
|
+
return {
|
|
16
|
+
async create(agentId: string, options?: PresentationOptions): Promise<Presentation> {
|
|
17
|
+
const messages = await agentx.session.getMessages(agentId);
|
|
18
|
+
const conversations = messagesToConversations(messages);
|
|
19
|
+
return new Presentation(agentx, agentId, options, conversations);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session namespace factories (messaging)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Message, UserContentPart } from "@agentxjs/core/agent";
|
|
6
|
+
import type { RpcClient } from "@agentxjs/core/network";
|
|
7
|
+
import type { AgentXRuntime } from "@agentxjs/core/runtime";
|
|
8
|
+
import type { AgentInfo, BaseResponse, MessageSendResponse, SessionNamespace } from "../types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create local session namespace backed by embedded runtime
|
|
12
|
+
*/
|
|
13
|
+
export function createLocalSessions(runtime: AgentXRuntime): SessionNamespace {
|
|
14
|
+
return {
|
|
15
|
+
async send(agentId: string, content: string | unknown[]): Promise<MessageSendResponse> {
|
|
16
|
+
await runtime.receive(agentId, content as string | UserContentPart[]);
|
|
17
|
+
return { agentId, requestId: "" };
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async interrupt(agentId: string): Promise<BaseResponse> {
|
|
21
|
+
runtime.interrupt(agentId);
|
|
22
|
+
return { requestId: "" };
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
async getMessages(agentId: string): Promise<Message[]> {
|
|
26
|
+
const agent = runtime.getAgent(agentId);
|
|
27
|
+
if (!agent) return [];
|
|
28
|
+
return runtime.platform.sessionRepository.getMessages(agent.sessionId);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create remote session namespace backed by RPC client
|
|
35
|
+
*/
|
|
36
|
+
export function createRemoteSessions(rpcClient: RpcClient): SessionNamespace {
|
|
37
|
+
return {
|
|
38
|
+
async send(agentId: string, content: string | unknown[]): Promise<MessageSendResponse> {
|
|
39
|
+
const result = await rpcClient.call<MessageSendResponse>("message.send", {
|
|
40
|
+
agentId,
|
|
41
|
+
content,
|
|
42
|
+
});
|
|
43
|
+
return { ...result, requestId: "" };
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async interrupt(agentId: string): Promise<BaseResponse> {
|
|
47
|
+
const result = await rpcClient.call<BaseResponse>("agent.interrupt", { agentId });
|
|
48
|
+
return { ...result, requestId: "" };
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async getMessages(agentId: string): Promise<Message[]> {
|
|
52
|
+
const agentRes = await rpcClient.call<{ agent: AgentInfo | null }>("agent.get", { agentId });
|
|
53
|
+
if (!agentRes.agent) return [];
|
|
54
|
+
const msgRes = await rpcClient.call<{ messages: Message[] }>("image.messages", {
|
|
55
|
+
imageId: agentRes.agent.imageId,
|
|
56
|
+
});
|
|
57
|
+
return msgRes.messages ?? [];
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
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 { BusEvent, Unsubscribe } from "@agentxjs/core/event";
|
|
9
|
+
import type { AgentX } from "../types";
|
|
10
|
+
import { addUserConversation, createInitialState, presentationReducer } from "./reducer";
|
|
11
|
+
import type { Conversation, PresentationState } from "./types";
|
|
12
|
+
import { initialPresentationState } from "./types";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Presentation update handler
|
|
16
|
+
*/
|
|
17
|
+
export type PresentationUpdateHandler = (state: PresentationState) => void;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Presentation error handler
|
|
21
|
+
*/
|
|
22
|
+
export type PresentationErrorHandler = (error: Error) => void;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Presentation options
|
|
26
|
+
*/
|
|
27
|
+
export interface PresentationOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Called on every state update
|
|
30
|
+
*/
|
|
31
|
+
onUpdate?: PresentationUpdateHandler;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Called on errors
|
|
35
|
+
*/
|
|
36
|
+
onError?: PresentationErrorHandler;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Presentation - UI-friendly wrapper for AgentX
|
|
41
|
+
*/
|
|
42
|
+
export class Presentation {
|
|
43
|
+
private agentx: AgentX;
|
|
44
|
+
private agentId: string;
|
|
45
|
+
private state: PresentationState;
|
|
46
|
+
private updateHandlers: Set<PresentationUpdateHandler> = new Set();
|
|
47
|
+
private errorHandlers: Set<PresentationErrorHandler> = new Set();
|
|
48
|
+
private eventUnsubscribe: Unsubscribe | null = null;
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
agentx: AgentX,
|
|
52
|
+
agentId: string,
|
|
53
|
+
options?: PresentationOptions,
|
|
54
|
+
initialConversations?: Conversation[]
|
|
55
|
+
) {
|
|
56
|
+
this.agentx = agentx;
|
|
57
|
+
this.agentId = agentId;
|
|
58
|
+
this.state = initialConversations?.length
|
|
59
|
+
? { ...initialPresentationState, conversations: initialConversations }
|
|
60
|
+
: createInitialState();
|
|
61
|
+
|
|
62
|
+
// Register initial handlers
|
|
63
|
+
if (options?.onUpdate) {
|
|
64
|
+
this.updateHandlers.add(options.onUpdate);
|
|
65
|
+
}
|
|
66
|
+
if (options?.onError) {
|
|
67
|
+
this.errorHandlers.add(options.onError);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Subscribe to all events
|
|
71
|
+
this.subscribeToEvents();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get current state
|
|
76
|
+
*/
|
|
77
|
+
getState(): PresentationState {
|
|
78
|
+
return this.state;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Subscribe to state updates
|
|
83
|
+
*/
|
|
84
|
+
onUpdate(handler: PresentationUpdateHandler): Unsubscribe {
|
|
85
|
+
this.updateHandlers.add(handler);
|
|
86
|
+
// Immediately call with current state
|
|
87
|
+
handler(this.state);
|
|
88
|
+
return () => {
|
|
89
|
+
this.updateHandlers.delete(handler);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Subscribe to errors
|
|
95
|
+
*/
|
|
96
|
+
onError(handler: PresentationErrorHandler): Unsubscribe {
|
|
97
|
+
this.errorHandlers.add(handler);
|
|
98
|
+
return () => {
|
|
99
|
+
this.errorHandlers.delete(handler);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Send a message
|
|
105
|
+
*/
|
|
106
|
+
async send(content: string): Promise<void> {
|
|
107
|
+
// Add user conversation
|
|
108
|
+
this.state = addUserConversation(this.state, content);
|
|
109
|
+
this.notify();
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// Send message via agentx
|
|
113
|
+
await this.agentx.session.send(this.agentId, content);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
this.notifyError(error instanceof Error ? error : new Error(String(error)));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Interrupt current response
|
|
121
|
+
*/
|
|
122
|
+
async interrupt(): Promise<void> {
|
|
123
|
+
try {
|
|
124
|
+
await this.agentx.session.interrupt(this.agentId);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.notifyError(error instanceof Error ? error : new Error(String(error)));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Reset state
|
|
132
|
+
*/
|
|
133
|
+
reset(): void {
|
|
134
|
+
this.state = createInitialState();
|
|
135
|
+
this.notify();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Dispose and cleanup
|
|
140
|
+
*/
|
|
141
|
+
dispose(): void {
|
|
142
|
+
if (this.eventUnsubscribe) {
|
|
143
|
+
this.eventUnsubscribe();
|
|
144
|
+
this.eventUnsubscribe = null;
|
|
145
|
+
}
|
|
146
|
+
this.updateHandlers.clear();
|
|
147
|
+
this.errorHandlers.clear();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ==================== Private ====================
|
|
151
|
+
|
|
152
|
+
private subscribeToEvents(): void {
|
|
153
|
+
// Subscribe to all events and filter by agentId
|
|
154
|
+
this.eventUnsubscribe = this.agentx.onAny((event: BusEvent) => {
|
|
155
|
+
// Filter events for this agent (if context is available)
|
|
156
|
+
// Note: Events from server may or may not include context with agentId
|
|
157
|
+
const eventWithContext = event as BusEvent & { context?: { agentId?: string } };
|
|
158
|
+
const eventAgentId = eventWithContext.context?.agentId;
|
|
159
|
+
|
|
160
|
+
// Only filter if event has agentId and it doesn't match
|
|
161
|
+
if (eventAgentId && eventAgentId !== this.agentId) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Reduce event into state
|
|
166
|
+
const newState = presentationReducer(this.state, event);
|
|
167
|
+
if (newState !== this.state) {
|
|
168
|
+
this.state = newState;
|
|
169
|
+
this.notify();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private notify(): void {
|
|
175
|
+
for (const handler of this.updateHandlers) {
|
|
176
|
+
try {
|
|
177
|
+
handler(this.state);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error("Presentation update handler error:", error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private notifyError(error: Error): void {
|
|
185
|
+
for (const handler of this.errorHandlers) {
|
|
186
|
+
try {
|
|
187
|
+
handler(error);
|
|
188
|
+
} catch (e) {
|
|
189
|
+
console.error("Presentation error handler error:", e);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation Module
|
|
3
|
+
*
|
|
4
|
+
* UI-friendly data model and state management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
Presentation,
|
|
9
|
+
type PresentationErrorHandler,
|
|
10
|
+
type PresentationOptions,
|
|
11
|
+
type PresentationUpdateHandler,
|
|
12
|
+
} from "./Presentation";
|
|
13
|
+
export {
|
|
14
|
+
addUserConversation,
|
|
15
|
+
createInitialState,
|
|
16
|
+
messagesToConversations,
|
|
17
|
+
presentationReducer,
|
|
18
|
+
} from "./reducer";
|
|
19
|
+
export type {
|
|
20
|
+
AssistantConversation,
|
|
21
|
+
Block,
|
|
22
|
+
Conversation,
|
|
23
|
+
ErrorConversation,
|
|
24
|
+
ImageBlock,
|
|
25
|
+
PresentationState,
|
|
26
|
+
TextBlock,
|
|
27
|
+
TokenUsage,
|
|
28
|
+
ToolBlock,
|
|
29
|
+
UserConversation,
|
|
30
|
+
} from "./types";
|
|
31
|
+
export { initialPresentationState } from "./types";
|