agentxjs 2.6.1 → 2.8.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * AgentX Server Implementation (JSON-RPC 2.0)\n *\n * Creates a WebSocket server that:\n * 1. Accepts client connections\n * 2. Handles JSON-RPC requests directly via CommandHandler\n * 3. Broadcasts stream events as JSON-RPC notifications\n *\n * Message Types:\n * - RPC Request (has id): Client → Server → Client (direct response)\n * - RPC Notification (no id): Server → Client (stream events)\n */\n\nimport type { CreateDriver } from \"@agentxjs/core/driver\";\nimport type { BusEvent, SystemEvent } from \"@agentxjs/core/event\";\nimport type { ChannelConnection } from \"@agentxjs/core/network\";\nimport {\n createErrorResponse,\n createStreamEvent,\n createSuccessResponse,\n isNotification,\n isRequest,\n parseMessage,\n RpcErrorCodes,\n type RpcMethod,\n} from \"@agentxjs/core/network\";\nimport type { AgentXPlatform } from \"@agentxjs/core/runtime\";\nimport { createAgentXRuntime } from \"@agentxjs/core/runtime\";\nimport { createLogger } from \"@deepracticex/logger\";\nimport { CommandHandler } from \"./CommandHandler\";\nimport type { AgentXServer } from \"./types\";\n\nconst logger = createLogger(\"server/Server\");\n\n/**\n * Connection state\n */\ninterface ConnectionState {\n connection: ChannelConnection;\n subscribedTopics: Set<string>;\n}\n\n/**\n * Server configuration (supports both immediate and deferred platforms)\n */\nexport interface ServerConfig {\n /**\n * AgentX Platform — must provide `channelServer` for accepting WebSocket connections.\n */\n platform: AgentXPlatform;\n\n /**\n * LLM Driver factory function - creates Driver per Agent\n */\n createDriver: CreateDriver;\n\n /**\n * Port to listen on (standalone mode)\n */\n port?: number;\n\n /**\n * Host to bind to (default: \"0.0.0.0\")\n */\n host?: string;\n\n /**\n * Existing HTTP server to attach to (attached mode)\n */\n server?: import(\"@agentxjs/core/network\").MinimalHTTPServer;\n\n /**\n * WebSocket path when attached (default: \"/ws\")\n */\n wsPath?: string;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n/**\n * Create an AgentX server\n */\nexport async function createServer(config: ServerConfig): Promise<AgentXServer> {\n const { wsPath = \"/ws\" } = config;\n const platform = config.platform;\n\n // Create runtime from platform + driver\n const runtime = createAgentXRuntime(platform, config.createDriver);\n\n // Get channel server from platform\n const wsServer = platform.channelServer;\n if (!wsServer) {\n throw new Error(\"Platform must provide channelServer for server mode\");\n }\n\n // Create command handler (no longer needs eventBus)\n const commandHandler = new CommandHandler(runtime);\n\n // Track connections\n const connections = new Map<string, ConnectionState>();\n\n /**\n * Subscribe connection to a topic\n */\n function subscribeToTopic(connectionId: string, topic: string): void {\n const state = connections.get(connectionId);\n if (!state || state.subscribedTopics.has(topic)) return;\n\n state.subscribedTopics.add(topic);\n logger.debug(\"Connection subscribed to topic\", { connectionId, topic });\n }\n\n /**\n * Check if event should be sent to connection based on subscriptions\n */\n function shouldSendToConnection(state: ConnectionState, event: BusEvent): boolean {\n // Skip internal driver events\n if (event.source === \"driver\" && event.intent !== \"notification\") {\n return false;\n }\n\n // Skip command events (they are handled via RPC, not broadcast)\n if (event.source === \"command\") {\n return false;\n }\n\n // Check if subscribed to event's session\n const eventWithContext = event as BusEvent & { context?: { sessionId?: string } };\n const sessionId = eventWithContext.context?.sessionId;\n if (sessionId && state.subscribedTopics.has(sessionId)) {\n return true;\n }\n\n // Send to global subscribers\n return state.subscribedTopics.has(\"global\");\n }\n\n /**\n * Send JSON-RPC response to a specific connection\n */\n function sendResponse(connection: ChannelConnection, id: string | number, result: unknown): void {\n const response = createSuccessResponse(id, result);\n connection.send(JSON.stringify(response));\n }\n\n /**\n * Send JSON-RPC error to a specific connection\n */\n function sendError(\n connection: ChannelConnection,\n id: string | number | null,\n code: number,\n message: string\n ): void {\n const response = createErrorResponse(id, code, message);\n connection.send(JSON.stringify(response));\n }\n\n // Handle new connections\n wsServer.onConnection((connection) => {\n const state: ConnectionState = {\n connection,\n subscribedTopics: new Set([\"global\"]),\n };\n connections.set(connection.id, state);\n\n logger.info(\"Client connected\", {\n connectionId: connection.id,\n totalConnections: connections.size,\n });\n\n // Handle messages from client\n connection.onMessage(async (message) => {\n try {\n const parsed = parseMessage(message);\n\n // Handle single message (not batch)\n if (!Array.isArray(parsed)) {\n await handleParsedMessage(connection, state, parsed);\n } else {\n // Handle batch (not common, but supported by JSON-RPC 2.0)\n for (const item of parsed) {\n await handleParsedMessage(connection, state, item);\n }\n }\n } catch (err) {\n logger.error(\"Failed to parse message\", { error: (err as Error).message });\n sendError(connection, null, RpcErrorCodes.PARSE_ERROR, \"Parse error\");\n }\n });\n\n // Cleanup on disconnect\n connection.onClose(() => {\n connections.delete(connection.id);\n logger.info(\"Client disconnected\", {\n connectionId: connection.id,\n totalConnections: connections.size,\n });\n });\n });\n\n /**\n * Handle a parsed JSON-RPC message\n */\n async function handleParsedMessage(\n connection: ChannelConnection,\n state: ConnectionState,\n parsed: import(\"jsonrpc-lite\").IParsedObject\n ): Promise<void> {\n if (isRequest(parsed)) {\n // JSON-RPC Request - handle and respond directly\n const payload = parsed.payload as {\n id: string | number;\n method: string;\n params: unknown;\n };\n const { id, method, params } = payload;\n\n logger.debug(\"Received RPC request\", { id, method });\n\n // Call command handler\n const result = await commandHandler.handle(method as RpcMethod, params);\n\n if (result.success) {\n sendResponse(connection, id, result.data);\n } else {\n sendError(connection, id, result.code, result.message);\n }\n } else if (isNotification(parsed)) {\n // JSON-RPC Notification - control messages\n const payload = parsed.payload as {\n method: string;\n params: unknown;\n };\n const { method, params } = payload;\n\n logger.debug(\"Received notification\", { method });\n\n if (method === \"subscribe\") {\n const { topic } = params as { topic: string };\n subscribeToTopic(connection.id, topic);\n } else if (method === \"unsubscribe\") {\n const { topic } = params as { topic: string };\n state.subscribedTopics.delete(topic);\n logger.debug(\"Connection unsubscribed from topic\", { connectionId: connection.id, topic });\n } else if (method === \"control.ack\") {\n // ACK for reliable delivery - handled by network layer\n logger.debug(\"Received ACK notification\");\n }\n } else {\n // Invalid message\n logger.warn(\"Received invalid JSON-RPC message\");\n }\n }\n\n // Route internal events to connected clients as JSON-RPC notifications\n platform.eventBus.onAny((event) => {\n // Only broadcast broadcastable events\n if (!shouldBroadcastEvent(event)) {\n return;\n }\n\n // Get topic from event context\n const eventWithContext = event as BusEvent & { context?: { sessionId?: string } };\n const topic = eventWithContext.context?.sessionId || \"global\";\n\n // Wrap as JSON-RPC notification\n const notification = createStreamEvent(topic, event as SystemEvent);\n const message = JSON.stringify(notification);\n\n for (const [connectionId, state] of connections) {\n if (shouldSendToConnection(state, event)) {\n state.connection.sendReliable(message, {\n timeout: 10000,\n onTimeout: () => {\n logger.warn(\"Event ACK timeout\", {\n connectionId,\n eventType: event.type,\n });\n },\n });\n }\n }\n });\n\n /**\n * Check if event should be broadcast\n */\n function shouldBroadcastEvent(event: BusEvent): boolean {\n // Skip internal driver events\n if (event.source === \"driver\" && event.intent !== \"notification\") {\n return false;\n }\n\n // Skip command events (handled via RPC)\n if (event.source === \"command\") {\n return false;\n }\n\n // Check broadcastable flag\n const systemEvent = event as SystemEvent;\n if (systemEvent.broadcastable === false) {\n return false;\n }\n\n return true;\n }\n\n // Attach to existing server if provided\n if (config.server) {\n wsServer.attach(config.server, wsPath);\n logger.info(\"WebSocket attached to existing server\", { path: wsPath });\n }\n\n return {\n async listen(port?: number, host?: string) {\n if (config.server) {\n throw new Error(\n \"Cannot listen when attached to existing server. The server should call listen() instead.\"\n );\n }\n\n const listenPort = port ?? config.port ?? 5200;\n const listenHost = host ?? config.host ?? \"0.0.0.0\";\n\n await wsServer.listen(listenPort, listenHost);\n logger.info(\"Server listening\", { port: listenPort, host: listenHost });\n },\n\n async close() {\n await wsServer.close();\n logger.info(\"Server closed\");\n },\n\n async dispose() {\n // Cleanup in order\n await wsServer.dispose();\n commandHandler.dispose();\n await runtime.shutdown();\n logger.info(\"Server disposed\");\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxjs",
3
- "version": "2.6.1",
3
+ "version": "2.8.0",
4
4
  "description": "AgentX Client SDK - Local and remote AI agent management",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,12 +23,12 @@
23
23
  "test": "bun test bdd/"
24
24
  },
25
25
  "dependencies": {
26
- "@agentxjs/core": "^2.6.1",
26
+ "@agentxjs/core": "^2.8.0",
27
27
  "@deepracticex/id": "^0.2.0",
28
28
  "@deepracticex/logger": "^1.2.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@agentxjs/devtools": "^2.6.1",
31
+ "@agentxjs/devtools": "^2.8.0",
32
32
  "@deepracticex/bdd": "^0.3.0",
33
33
  "tsx": "^4.19.0",
34
34
  "typescript": "^5.3.3"
@@ -7,15 +7,15 @@
7
7
  import type { Message } from "@agentxjs/core/agent";
8
8
  import type { Presentation, PresentationOptions } from "./presentation";
9
9
  import type {
10
+ AgentConfig,
10
11
  AgentHandle,
11
12
  BaseResponse,
12
- Embodiment,
13
13
  MessageSendResponse,
14
14
  RuntimeNamespace,
15
15
  } from "./types";
16
16
 
17
17
  export class AgentHandleImpl implements AgentHandle {
18
- readonly agentId: string;
18
+ readonly instanceId: string;
19
19
  readonly imageId: string;
20
20
  readonly containerId: string;
21
21
  readonly sessionId: string;
@@ -23,10 +23,10 @@ export class AgentHandleImpl implements AgentHandle {
23
23
  private readonly ns: RuntimeNamespace;
24
24
 
25
25
  constructor(
26
- ids: { agentId: string; imageId: string; containerId: string; sessionId: string },
26
+ ids: { instanceId: string; imageId: string; containerId: string; sessionId: string },
27
27
  ns: RuntimeNamespace
28
28
  ) {
29
- this.agentId = ids.agentId;
29
+ this.instanceId = ids.instanceId;
30
30
  this.imageId = ids.imageId;
31
31
  this.containerId = ids.containerId;
32
32
  this.sessionId = ids.sessionId;
@@ -34,11 +34,11 @@ export class AgentHandleImpl implements AgentHandle {
34
34
  }
35
35
 
36
36
  async send(content: string | unknown[]): Promise<MessageSendResponse> {
37
- return this.ns.session.send(this.agentId, content);
37
+ return this.ns.session.send(this.instanceId, content);
38
38
  }
39
39
 
40
40
  async interrupt(): Promise<BaseResponse> {
41
- return this.ns.session.interrupt(this.agentId);
41
+ return this.ns.session.interrupt(this.instanceId);
42
42
  }
43
43
 
44
44
  async history(): Promise<Message[]> {
@@ -46,20 +46,22 @@ export class AgentHandleImpl implements AgentHandle {
46
46
  }
47
47
 
48
48
  async present(options?: PresentationOptions): Promise<Presentation> {
49
- return this.ns.present.create(this.agentId, options);
49
+ return this.ns.present.create(this.instanceId, options);
50
50
  }
51
51
 
52
- async update(updates: {
53
- name?: string;
54
- description?: string;
55
- embody?: Embodiment;
56
- customData?: Record<string, unknown>;
57
- }): Promise<void> {
52
+ async update(
53
+ updates: Partial<
54
+ Pick<
55
+ AgentConfig,
56
+ "name" | "description" | "model" | "systemPrompt" | "mcpServers" | "customData"
57
+ >
58
+ >
59
+ ): Promise<void> {
58
60
  await this.ns.image.update(this.imageId, updates);
59
61
  }
60
62
 
61
63
  async delete(): Promise<void> {
62
- await this.ns.agent.destroy(this.agentId);
64
+ await this.ns.instance.destroy(this.instanceId);
63
65
  await this.ns.image.delete(this.imageId);
64
66
  }
65
67
  }
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { UserContentPart } from "@agentxjs/core/agent";
11
+ import { DEFAULT_CONTAINER_ID } from "@agentxjs/core/container";
11
12
  import type { RpcMethod } from "@agentxjs/core/network";
12
13
  import type { AgentXRuntime } from "@agentxjs/core/runtime";
13
14
  import { createLogger } from "@deepracticex/logger";
@@ -63,14 +64,6 @@ export class CommandHandler {
63
64
 
64
65
  try {
65
66
  switch (method) {
66
- // Container
67
- case "container.create":
68
- return await this.handleContainerCreate(params);
69
- case "container.get":
70
- return await this.handleContainerGet(params);
71
- case "container.list":
72
- return await this.handleContainerList(params);
73
-
74
67
  // Image
75
68
  case "image.create":
76
69
  return await this.handleImageCreate(params);
@@ -89,34 +82,22 @@ export class CommandHandler {
89
82
  case "image.messages":
90
83
  return await this.handleImageMessages(params);
91
84
 
92
- // Agent
93
- case "agent.get":
85
+ // Instance
86
+ case "instance.get":
94
87
  return await this.handleAgentGet(params);
95
- case "agent.list":
88
+ case "instance.list":
96
89
  return await this.handleAgentList(params);
97
- case "agent.destroy":
90
+ case "instance.destroy":
98
91
  return await this.handleAgentDestroy(params);
99
- case "agent.destroyAll":
92
+ case "instance.destroyAll":
100
93
  return await this.handleAgentDestroyAll(params);
101
- case "agent.interrupt":
94
+ case "instance.interrupt":
102
95
  return await this.handleAgentInterrupt(params);
103
96
 
104
97
  // Message
105
98
  case "message.send":
106
99
  return await this.handleMessageSend(params);
107
100
 
108
- // Prototype
109
- case "prototype.create":
110
- return await this.handlePrototypeCreate(params);
111
- case "prototype.get":
112
- return await this.handlePrototypeGet(params);
113
- case "prototype.list":
114
- return await this.handlePrototypeList(params);
115
- case "prototype.update":
116
- return await this.handlePrototypeUpdate(params);
117
- case "prototype.delete":
118
- return await this.handlePrototypeDelete(params);
119
-
120
101
  // LLM Provider
121
102
  case "llm.create":
122
103
  return await this.handleLLMCreate(params);
@@ -140,50 +121,39 @@ export class CommandHandler {
140
121
  }
141
122
  }
142
123
 
143
- // ==================== Container Commands ====================
144
-
145
- private async handleContainerCreate(params: unknown): Promise<RpcResponse> {
146
- const { containerId } = params as { containerId: string };
147
- const { getOrCreateContainer } = await import("@agentxjs/core/container");
148
- const { containerRepository, imageRepository, sessionRepository } = this.runtime.platform;
149
-
150
- const container = await getOrCreateContainer(containerId, {
151
- containerRepository,
152
- imageRepository,
153
- sessionRepository,
154
- });
155
-
156
- return ok({ containerId: container.containerId });
157
- }
158
-
159
- private async handleContainerGet(params: unknown): Promise<RpcResponse> {
160
- const { containerId } = params as { containerId: string };
161
- const exists = await this.runtime.platform.containerRepository.containerExists(containerId);
162
- return ok({ containerId, exists });
163
- }
164
-
165
- private async handleContainerList(_params: unknown): Promise<RpcResponse> {
166
- const containers = await this.runtime.platform.containerRepository.findAllContainers();
167
- return ok({ containerIds: containers.map((c) => c.containerId) });
168
- }
169
-
170
124
  // ==================== Image Commands ====================
171
125
 
172
126
  private async handleImageCreate(params: unknown): Promise<RpcResponse> {
173
- const { containerId, name, description, contextId, embody, customData } = params as {
174
- containerId: string;
127
+ const {
128
+ containerId: _cid,
129
+ name,
130
+ description,
131
+ contextId,
132
+ model,
133
+ systemPrompt,
134
+ mcpServers,
135
+ customData,
136
+ } = params as {
137
+ containerId?: string;
175
138
  name?: string;
176
139
  description?: string;
177
140
  contextId?: string;
178
- embody?: import("@agentxjs/core/persistence").Embodiment;
141
+ model?: string;
142
+ systemPrompt?: string;
143
+ mcpServers?: Record<string, unknown>;
179
144
  customData?: Record<string, unknown>;
180
145
  };
181
146
 
182
147
  const { imageRepository, sessionRepository } = this.runtime.platform;
183
148
  const { createImage } = await import("@agentxjs/core/image");
184
149
 
150
+ const embody =
151
+ model || systemPrompt || mcpServers
152
+ ? ({ model, systemPrompt, mcpServers } as import("@agentxjs/core/persistence").Embodiment)
153
+ : undefined;
154
+
185
155
  const image = await createImage(
186
- { containerId, name, description, contextId, embody, customData },
156
+ { containerId: DEFAULT_CONTAINER_ID, name, description, contextId, embody, customData },
187
157
  { imageRepository, sessionRepository }
188
158
  );
189
159
 
@@ -228,9 +198,9 @@ export class CommandHandler {
228
198
  }
229
199
 
230
200
  private async handleImageRun(params: unknown): Promise<RpcResponse> {
231
- const { imageId, agentId: requestedAgentId } = params as {
201
+ const { imageId, instanceId: requestedInstanceId } = params as {
232
202
  imageId: string;
233
- agentId?: string;
203
+ instanceId?: string;
234
204
  };
235
205
 
236
206
  // Check if already have a running agent for this image
@@ -241,30 +211,30 @@ export class CommandHandler {
241
211
  if (existingAgent) {
242
212
  logger.debug("Reusing existing agent for image", {
243
213
  imageId,
244
- agentId: existingAgent.agentId,
214
+ instanceId: existingAgent.instanceId,
245
215
  });
246
216
  return ok({
247
217
  imageId,
248
- agentId: existingAgent.agentId,
218
+ instanceId: existingAgent.instanceId,
249
219
  sessionId: existingAgent.sessionId,
250
220
  containerId: existingAgent.containerId,
251
221
  reused: true,
252
222
  });
253
223
  }
254
224
 
255
- // Create new agent (with optional custom agentId)
225
+ // Create new agent (with optional custom instanceId)
256
226
  const agent = await this.runtime.createAgent({
257
227
  imageId,
258
- agentId: requestedAgentId,
228
+ instanceId: requestedInstanceId,
259
229
  });
260
230
  logger.info("Created new agent for image", {
261
231
  imageId,
262
- agentId: agent.agentId,
232
+ instanceId: agent.instanceId,
263
233
  });
264
234
 
265
235
  return ok({
266
236
  imageId,
267
- agentId: agent.agentId,
237
+ instanceId: agent.instanceId,
268
238
  sessionId: agent.sessionId,
269
239
  containerId: agent.containerId,
270
240
  reused: false,
@@ -280,8 +250,8 @@ export class CommandHandler {
280
250
  .find((a) => a.imageId === imageId && a.lifecycle === "running");
281
251
 
282
252
  if (agent) {
283
- await this.runtime.stopAgent(agent.agentId);
284
- logger.info("Stopped agent for image", { imageId, agentId: agent.agentId });
253
+ await this.runtime.stopAgent(agent.instanceId);
254
+ logger.info("Stopped agent for image", { imageId, instanceId: agent.instanceId });
285
255
  } else {
286
256
  logger.debug("No running agent found for image", { imageId });
287
257
  }
@@ -295,7 +265,9 @@ export class CommandHandler {
295
265
  updates: {
296
266
  name?: string;
297
267
  description?: string;
298
- embody?: import("@agentxjs/core/persistence").Embodiment;
268
+ model?: string;
269
+ systemPrompt?: string;
270
+ mcpServers?: Record<string, unknown>;
299
271
  customData?: Record<string, unknown>;
300
272
  };
301
273
  };
@@ -306,12 +278,22 @@ export class CommandHandler {
306
278
  return err(404, `Image not found: ${imageId}`);
307
279
  }
308
280
 
309
- // Update image record (embody is merged, not replaced)
310
- const { embody: embodyUpdates, ...otherUpdates } = updates;
281
+ // Extract embody fields from flat updates
282
+ const { model, systemPrompt, mcpServers, ...otherUpdates } = updates;
283
+ const embodyUpdates =
284
+ model !== undefined || systemPrompt !== undefined || mcpServers !== undefined
285
+ ? ({
286
+ ...imageRecord.embody,
287
+ model,
288
+ systemPrompt,
289
+ mcpServers,
290
+ } as import("@agentxjs/core/persistence").Embodiment)
291
+ : imageRecord.embody;
292
+
311
293
  const updatedRecord = {
312
294
  ...imageRecord,
313
295
  ...otherUpdates,
314
- embody: embodyUpdates ? { ...imageRecord.embody, ...embodyUpdates } : imageRecord.embody,
296
+ embody: embodyUpdates,
315
297
  updatedAt: Date.now(),
316
298
  };
317
299
 
@@ -344,13 +326,13 @@ export class CommandHandler {
344
326
  // ==================== Agent Commands ====================
345
327
 
346
328
  private async handleAgentGet(params: unknown): Promise<RpcResponse> {
347
- const { agentId } = params as { agentId: string };
348
- const agent = this.runtime.getAgent(agentId);
329
+ const { instanceId } = params as { instanceId: string };
330
+ const agent = this.runtime.getAgent(instanceId);
349
331
 
350
332
  return ok({
351
333
  agent: agent
352
334
  ? {
353
- agentId: agent.agentId,
335
+ instanceId: agent.instanceId,
354
336
  imageId: agent.imageId,
355
337
  containerId: agent.containerId,
356
338
  sessionId: agent.sessionId,
@@ -369,7 +351,7 @@ export class CommandHandler {
369
351
 
370
352
  return ok({
371
353
  agents: agents.map((a) => ({
372
- agentId: a.agentId,
354
+ instanceId: a.instanceId,
373
355
  imageId: a.imageId,
374
356
  containerId: a.containerId,
375
357
  sessionId: a.sessionId,
@@ -379,47 +361,47 @@ export class CommandHandler {
379
361
  }
380
362
 
381
363
  private async handleAgentDestroy(params: unknown): Promise<RpcResponse> {
382
- const { agentId } = params as { agentId: string };
364
+ const { instanceId } = params as { instanceId: string };
383
365
 
384
366
  // Check if agent exists first
385
- const agent = this.runtime.getAgent(agentId);
367
+ const agent = this.runtime.getAgent(instanceId);
386
368
  if (!agent) {
387
- return ok({ agentId, success: false });
369
+ return ok({ instanceId, success: false });
388
370
  }
389
371
 
390
- await this.runtime.destroyAgent(agentId);
391
- return ok({ agentId, success: true });
372
+ await this.runtime.destroyAgent(instanceId);
373
+ return ok({ instanceId, success: true });
392
374
  }
393
375
 
394
376
  private async handleAgentDestroyAll(params: unknown): Promise<RpcResponse> {
395
377
  const { containerId } = params as { containerId: string };
396
378
  const agents = this.runtime.getAgentsByContainer(containerId);
397
379
  for (const agent of agents) {
398
- await this.runtime.destroyAgent(agent.agentId);
380
+ await this.runtime.destroyAgent(agent.instanceId);
399
381
  }
400
382
  return ok({ containerId });
401
383
  }
402
384
 
403
385
  private async handleAgentInterrupt(params: unknown): Promise<RpcResponse> {
404
- const { agentId } = params as { agentId: string };
405
- this.runtime.interrupt(agentId);
406
- return ok({ agentId });
386
+ const { instanceId } = params as { instanceId: string };
387
+ this.runtime.interrupt(instanceId);
388
+ return ok({ instanceId });
407
389
  }
408
390
 
409
391
  // ==================== Message Commands ====================
410
392
 
411
393
  private async handleMessageSend(params: unknown): Promise<RpcResponse> {
412
- const { agentId, imageId, content } = params as {
413
- agentId?: string;
394
+ const { instanceId, imageId, content } = params as {
395
+ instanceId?: string;
414
396
  imageId?: string;
415
397
  content: string | UserContentPart[];
416
398
  };
417
399
 
418
- let targetAgentId: string;
400
+ let targetInstanceId: string;
419
401
 
420
- if (agentId) {
402
+ if (instanceId) {
421
403
  // Direct agent reference
422
- targetAgentId = agentId;
404
+ targetInstanceId = instanceId;
423
405
  } else if (imageId) {
424
406
  // Auto-activate image: find or create agent
425
407
  const existingAgent = this.runtime
@@ -427,138 +409,41 @@ export class CommandHandler {
427
409
  .find((a) => a.imageId === imageId && a.lifecycle === "running");
428
410
 
429
411
  if (existingAgent) {
430
- targetAgentId = existingAgent.agentId;
412
+ targetInstanceId = existingAgent.instanceId;
431
413
  logger.debug("Using existing agent for message", {
432
414
  imageId,
433
- agentId: targetAgentId,
415
+ instanceId: targetInstanceId,
434
416
  });
435
417
  } else {
436
418
  // Create new agent for this image
437
419
  const agent = await this.runtime.createAgent({ imageId });
438
- targetAgentId = agent.agentId;
420
+ targetInstanceId = agent.instanceId;
439
421
  logger.info("Auto-created agent for message", {
440
422
  imageId,
441
- agentId: targetAgentId,
423
+ instanceId: targetInstanceId,
442
424
  });
443
425
  }
444
426
  } else {
445
- return err(-32602, "Either agentId or imageId is required");
446
- }
447
-
448
- await this.runtime.receive(targetAgentId, content);
449
- return ok({ agentId: targetAgentId, imageId });
450
- }
451
-
452
- // ==================== Prototype Commands ====================
453
-
454
- private async handlePrototypeCreate(params: unknown): Promise<RpcResponse> {
455
- const { containerId, name, description, contextId, embody, customData } = params as {
456
- containerId: string;
457
- name: string;
458
- description?: string;
459
- contextId?: string;
460
- embody?: import("@agentxjs/core/persistence").Embodiment;
461
- customData?: Record<string, unknown>;
462
- };
463
-
464
- const repo = this.runtime.platform.prototypeRepository;
465
- if (!repo) {
466
- return err(-32000, "Prototype repository not available");
467
- }
468
-
469
- const now = Date.now();
470
- const random = Math.random().toString(36).slice(2, 8);
471
- const record = {
472
- prototypeId: `proto_${now}_${random}`,
473
- containerId,
474
- name,
475
- description,
476
- contextId,
477
- embody,
478
- customData,
479
- createdAt: now,
480
- updatedAt: now,
481
- };
482
-
483
- await repo.savePrototype(record);
484
- return ok({ record });
485
- }
486
-
487
- private async handlePrototypeGet(params: unknown): Promise<RpcResponse> {
488
- const { prototypeId } = params as { prototypeId: string };
489
- const repo = this.runtime.platform.prototypeRepository;
490
- if (!repo) {
491
- return err(-32000, "Prototype repository not available");
492
- }
493
-
494
- const record = await repo.findPrototypeById(prototypeId);
495
- return ok({ record });
496
- }
497
-
498
- private async handlePrototypeList(params: unknown): Promise<RpcResponse> {
499
- const { containerId } = params as { containerId?: string };
500
- const repo = this.runtime.platform.prototypeRepository;
501
- if (!repo) {
502
- return err(-32000, "Prototype repository not available");
503
- }
504
-
505
- const records = containerId
506
- ? await repo.findPrototypesByContainerId(containerId)
507
- : await repo.findAllPrototypes();
508
-
509
- return ok({ records });
510
- }
511
-
512
- private async handlePrototypeUpdate(params: unknown): Promise<RpcResponse> {
513
- const { prototypeId, updates } = params as {
514
- prototypeId: string;
515
- updates: {
516
- name?: string;
517
- description?: string;
518
- contextId?: string;
519
- embody?: import("@agentxjs/core/persistence").Embodiment;
520
- customData?: Record<string, unknown>;
521
- };
522
- };
523
-
524
- const repo = this.runtime.platform.prototypeRepository;
525
- if (!repo) {
526
- return err(-32000, "Prototype repository not available");
527
- }
528
-
529
- const existing = await repo.findPrototypeById(prototypeId);
530
- if (!existing) {
531
- return err(404, `Prototype not found: ${prototypeId}`);
427
+ return err(-32602, "Either instanceId or imageId is required");
532
428
  }
533
429
 
534
- const { embody: embodyUpdates, ...otherUpdates } = updates;
535
- const updated = {
536
- ...existing,
537
- ...otherUpdates,
538
- embody: embodyUpdates ? { ...existing.embody, ...embodyUpdates } : existing.embody,
539
- updatedAt: Date.now(),
540
- };
541
-
542
- await repo.savePrototype(updated);
543
- return ok({ record: updated });
544
- }
545
-
546
- private async handlePrototypeDelete(params: unknown): Promise<RpcResponse> {
547
- const { prototypeId } = params as { prototypeId: string };
548
- const repo = this.runtime.platform.prototypeRepository;
549
- if (!repo) {
550
- return err(-32000, "Prototype repository not available");
551
- }
552
-
553
- await repo.deletePrototype(prototypeId);
554
- return ok({ prototypeId });
430
+ await this.runtime.receive(targetInstanceId, content);
431
+ return ok({ instanceId: targetInstanceId, imageId });
555
432
  }
556
433
 
557
434
  // ==================== LLM Provider Commands ====================
558
435
 
559
436
  private async handleLLMCreate(params: unknown): Promise<RpcResponse> {
560
- const { containerId, name, vendor, protocol, apiKey, baseUrl, model } = params as {
561
- containerId: string;
437
+ const {
438
+ containerId: _cid,
439
+ name,
440
+ vendor,
441
+ protocol,
442
+ apiKey,
443
+ baseUrl,
444
+ model,
445
+ } = params as {
446
+ containerId?: string;
562
447
  name: string;
563
448
  vendor: string;
564
449
  protocol: string;
@@ -576,7 +461,7 @@ export class CommandHandler {
576
461
  const now = Date.now();
577
462
  const record = {
578
463
  id: generateId("llm"),
579
- containerId,
464
+ containerId: DEFAULT_CONTAINER_ID,
580
465
  name,
581
466
  vendor,
582
467
  protocol: protocol as "anthropic" | "openai",