agent-world 0.4.6 → 0.6.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/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  Traditional AI frameworks force you to write hundreds of lines of code just to make agents talk to each other. Agent World lets you create intelligent agent teams using nothing but plain natural language.
8
8
 
9
+ https://github.com/user-attachments/assets/cc507c95-01a4-4c27-975a-f8f67d8cf0d7
10
+
9
11
  Audio introduction: [Listen here](https://yysun.github.io/agent-world)
10
12
 
11
13
  **Other frameworks:**
@@ -80,13 +82,14 @@ Each Agent World has a collection of agents that can communicate through a share
80
82
  | **Direct mention** | `@alice Can you help?` | Only @alice |
81
83
  | **Paragraph mention** | `Please review this:\n@alice` | Only @alice |
82
84
  | **Mid-text mention** | `I think @alice should help` | Nobody (saved to memory) |
85
+ | **Stop World** | `<world>pass</world>` | No agents |
83
86
 
84
87
  ### Agent Behavior
85
88
 
86
89
  **Agents always respond to:**
87
90
  - Human messages (unless mentioned agents exist)
88
91
  - Direct @mentions at paragraph start
89
- - System messages
92
+ - World messages
90
93
 
91
94
  **Agents never respond to:**
92
95
  - Their own messages
@@ -126,58 +129,9 @@ npx agent-world -w default-world "hi"
126
129
  echo "hi" | npx agent-world -w default-world
127
130
  ```
128
131
 
129
- ## Project Structure (npm workspaces)
130
-
131
- This project uses npm workspaces with two main packages:
132
-
133
- - **`core/`** - Reusable agent management library
134
- - World-mediated agent management system
135
- - Event-driven architecture
136
- - LLM provider abstraction
137
- - Cross-platform compatibility (Node.js/Browser)
138
-
139
- - **`next/`** - Next.js web application
140
- - React frontend with Tailwind CSS
141
- - API routes for CRUD operations
142
- - Real-time chat with streaming support
143
- - Modern, minimalistic UI
144
-
145
- ### Cross-workspace imports
146
- ```typescript
147
- // In next/ workspace
148
- import { createWorld, listAgents, publishMessage } from '@agent-world/core';
149
- ```
150
-
151
- ### Web Interface Features
152
-
153
- The Next.js workspace provides a modern web interface with:
154
-
155
- - **Home Page**: World selector and creator with clean card-based UI
156
- - **World Page**: Chat interface with agent management sidebar
157
- - Real-time messaging with agents
158
- - Toggle between streaming and non-streaming modes
159
- - Agent creation with custom system prompts
160
- - Responsive design with Tailwind CSS
161
-
162
- ### API Endpoints
132
+ ## Project Structure
163
133
 
164
- The Next.js workspace exposes REST APIs for integration:
165
-
166
- ```
167
- GET /api/worlds # List all worlds
168
- POST /api/worlds # Create new world
169
- GET /api/worlds/:id # Get world details
170
- PUT /api/worlds/:id # Update world
171
- DELETE /api/worlds/:id # Delete world
172
-
173
- GET /api/worlds/:id/agents # List agents in world
174
- POST /api/worlds/:id/agents # Create new agent
175
- GET /api/worlds/:id/agents/:agentId # Get agent details
176
- PUT /api/worlds/:id/agents/:agentId # Update agent
177
- DELETE /api/worlds/:id/agents/:agentId # Delete agent
178
-
179
- POST /api/worlds/:id/chat # Send message (streaming/non-streaming)
180
- ```
134
+ See [Project Structure Documentation](project.md)
181
135
 
182
136
  ### Environment Setup
183
137
 
@@ -219,11 +173,30 @@ export AGENT_WORLD_DATA_PATH=./data/worlds
219
173
  - **[Building Agents with Just Words](docs/Building%20Agents%20with%20Just%20Words.md)** - Complete guide with examples
220
174
 
221
175
 
176
+ ## Experimental Features
177
+
178
+ - **MCP Support** - *Currently in experiment* - Model Context Protocol integration for tools like search and code execution. e.g.,
179
+
180
+ ```json
181
+ {
182
+ "servers": {
183
+ "playwright": {
184
+ "command": "npx",
185
+ "args": [
186
+ "@playwright/mcp@latest"
187
+ ]
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
193
+ It supports transport types `stdio` and `http`.
194
+
195
+
222
196
  ## Future Plans
223
197
 
224
198
  - **Long Run Worlds** - Worlds can run for days or weeks, with agents evolving over time
225
199
  - **Dynamic Worlds** - Worlds can provide real-time data to agents, e.g. date and time
226
- - **Tools / MCP Support** - Worlds can have tools for agents to use, like search or code execution
227
200
  - **Agent Learning** - Agents will evolve based on interactions
228
201
  - **Agent Replication** - Agents can create new agents
229
202
 
package/dist/cli/index.js CHANGED
@@ -39,9 +39,9 @@ import { fileURLToPath } from 'url';
39
39
  import { program } from 'commander';
40
40
  import readline from 'readline';
41
41
  import { listWorlds, subscribeWorld, createCategoryLogger, LLMProvider, enableStreaming, disableStreaming } from '../core/index.js';
42
- import { getDefaultRootPath } from '../core/storage-factory.js';
42
+ import { getDefaultRootPath } from '../core/storage/storage-factory.js';
43
43
  import { processCLIInput } from './commands.js';
44
- import { createStreamingState, handleWorldEventWithStreaming } from './stream.js';
44
+ import { createStreamingState, handleWorldEventWithStreaming, } from './stream.js';
45
45
  import { configureLLMProvider } from '../core/llm-config.js';
46
46
  // Create CLI category logger after logger auto-initialization
47
47
  const logger = createCategoryLogger('cli');
@@ -100,12 +100,12 @@ function configureLLMProvidersFromEnv() {
100
100
  logger.debug('Configured Google provider from environment');
101
101
  }
102
102
  // Azure
103
- if (process.env.AZURE_OPENAI_API_KEY && process.env.AZURE_ENDPOINT && process.env.AZURE_DEPLOYMENT) {
103
+ if (process.env.AZURE_OPENAI_API_KEY && process.env.AZURE_RESOURCE_NAME && process.env.AZURE_DEPLOYMENT) {
104
104
  configureLLMProvider(LLMProvider.AZURE, {
105
105
  apiKey: process.env.AZURE_OPENAI_API_KEY,
106
- endpoint: process.env.AZURE_ENDPOINT,
106
+ resourceName: process.env.AZURE_RESOURCE_NAME,
107
107
  deployment: process.env.AZURE_DEPLOYMENT,
108
- apiVersion: process.env.AZURE_API_VERSION || '2023-12-01-preview'
108
+ apiVersion: process.env.AZURE_API_VERSION || '2024-10-21-preview'
109
109
  });
110
110
  logger.debug('Configured Azure provider from environment');
111
111
  }
@@ -124,19 +124,19 @@ function configureLLMProvidersFromEnv() {
124
124
  });
125
125
  logger.debug('Configured OpenAI-Compatible provider from environment');
126
126
  }
127
- // Ollama
127
+ // Ollama (OpenAI-compatible endpoint)
128
128
  if (process.env.OLLAMA_BASE_URL) {
129
129
  configureLLMProvider(LLMProvider.OLLAMA, {
130
130
  baseUrl: process.env.OLLAMA_BASE_URL
131
131
  });
132
- logger.debug('Configured Ollama provider from environment');
132
+ logger.debug('Configured Ollama provider (OpenAI-compatible) from environment');
133
133
  }
134
134
  else {
135
- // Configure Ollama with default URL if not specified
135
+ // Configure Ollama with default OpenAI-compatible URL if not specified
136
136
  configureLLMProvider(LLMProvider.OLLAMA, {
137
- baseUrl: 'http://localhost:11434/api'
137
+ baseUrl: 'http://localhost:11434/v1'
138
138
  });
139
- logger.debug('Configured Ollama provider with default URL');
139
+ logger.debug('Configured Ollama provider (OpenAI-compatible) with default URL');
140
140
  }
141
141
  }
142
142
  // Get default root path from storage-factory (no local defaults)
@@ -158,7 +158,6 @@ function printCLIResult(result) {
158
158
  }
159
159
  // Pipeline mode execution with timer-based cleanup
160
160
  async function runPipelineMode(options, messageFromArgs) {
161
- const rootPath = options.root || DEFAULT_ROOT_PATH;
162
161
  disableStreaming();
163
162
  try {
164
163
  let world = null;
@@ -198,7 +197,7 @@ async function runPipelineMode(options, messageFromArgs) {
198
197
  }, delay);
199
198
  };
200
199
  if (options.world) {
201
- worldSubscription = await subscribeWorld(options.world, rootPath, pipelineClient);
200
+ worldSubscription = await subscribeWorld(options.world, pipelineClient);
202
201
  if (!worldSubscription) {
203
202
  console.error(boldRed(`Error: World '${options.world}' not found`));
204
203
  process.exit(1);
@@ -211,7 +210,7 @@ async function runPipelineMode(options, messageFromArgs) {
211
210
  console.error(boldRed('Error: World must be specified to send user messages'));
212
211
  process.exit(1);
213
212
  }
214
- const result = await processCLIInput(options.command, world, rootPath, 'HUMAN');
213
+ const result = await processCLIInput(options.command, world, 'HUMAN');
215
214
  printCLIResult(result);
216
215
  // Only set timer if sending message to world (not for commands)
217
216
  if (!options.command.startsWith('/') && world) {
@@ -234,7 +233,7 @@ async function runPipelineMode(options, messageFromArgs) {
234
233
  console.error(boldRed('Error: World must be specified to send user messages'));
235
234
  process.exit(1);
236
235
  }
237
- const result = await processCLIInput(messageFromArgs, world, rootPath, 'HUMAN');
236
+ const result = await processCLIInput(messageFromArgs, world, 'HUMAN');
238
237
  printCLIResult(result);
239
238
  // Set timer with longer delay for message processing (always needed for messages)
240
239
  setupExitTimer(8000);
@@ -254,7 +253,7 @@ async function runPipelineMode(options, messageFromArgs) {
254
253
  console.error(boldRed('Error: World must be specified to send user messages'));
255
254
  process.exit(1);
256
255
  }
257
- const result = await processCLIInput(input.trim(), world, rootPath, 'HUMAN');
256
+ const result = await processCLIInput(input.trim(), world, 'HUMAN');
258
257
  printCLIResult(result);
259
258
  // Set timer with longer delay for message processing (always needed for stdin messages)
260
259
  setupExitTimer(8000);
@@ -290,7 +289,7 @@ async function handleSubscribe(rootPath, worldName, streaming, globalState, rl)
290
289
  console.log(red(`Error: ${error}`));
291
290
  }
292
291
  };
293
- const subscription = await subscribeWorld(worldName, rootPath, cliClient);
292
+ const subscription = await subscribeWorld(worldName, cliClient);
294
293
  if (!subscription)
295
294
  throw new Error('Failed to load world');
296
295
  return { subscription, world: subscription.world };
@@ -313,7 +312,7 @@ function handleWorldEvent(eventType, eventData, streaming, globalState, rl) {
313
312
  // World discovery and selection
314
313
  async function getAvailableWorldNames(rootPath) {
315
314
  try {
316
- const worldInfos = await listWorlds(rootPath);
315
+ const worldInfos = await listWorlds();
317
316
  return worldInfos.map(info => info.id);
318
317
  }
319
318
  catch (error) {
@@ -469,11 +468,10 @@ async function runInteractiveMode(options) {
469
468
  cleanupWorldSubscription(worldState);
470
469
  rl.close();
471
470
  process.exit(0);
472
- return;
473
471
  }
474
472
  console.log(`\n${boldYellow('● you:')} ${trimmedInput}`);
475
473
  try {
476
- const result = await processCLIInput(trimmedInput, worldState?.world || null, rootPath, 'HUMAN');
474
+ const result = await processCLIInput(trimmedInput, worldState?.world || null, 'HUMAN');
477
475
  // Handle exit commands from result (redundant, but keep for safety)
478
476
  if (result.data?.exit) {
479
477
  if (isExiting)
@@ -487,7 +485,6 @@ async function runInteractiveMode(options) {
487
485
  cleanupWorldSubscription(worldState);
488
486
  rl.close();
489
487
  process.exit(0);
490
- return;
491
488
  }
492
489
  // Handle world selection command
493
490
  if (result.data?.selectWorld) {
@@ -600,6 +597,7 @@ async function runInteractiveMode(options) {
600
597
  if (isExiting)
601
598
  return; // Prevent duplicate cleanup
602
599
  isExiting = true;
600
+ console.log(`\n${boldCyan('Shutting down...')}`);
603
601
  clearPromptTimer(globalState);
604
602
  if (streaming.stopWait)
605
603
  streaming.stopWait();
@@ -10,13 +10,11 @@
10
10
  *
11
11
  * This module re-exports the core functionality of Agent World for npm package usage.
12
12
  */
13
-
14
13
  // Re-export all core functionality
15
14
  export * from './core/index';
16
-
17
15
  // Package information
18
16
  export const PACKAGE_INFO = {
19
- name: 'agent-world',
20
- version: '0.3.0',
21
- description: 'A agent management system for building AI agent teams with just words.',
22
- } as const;
17
+ name: 'agent-world',
18
+ version: '0.5.0',
19
+ description: 'A agent management system for building AI agent teams with just words.',
20
+ };
@@ -23,6 +23,7 @@ import cors from 'cors';
23
23
  import path from 'path';
24
24
  import { fileURLToPath, pathToFileURL } from 'url';
25
25
  import apiRouter from './api.js';
26
+ import { initializeMCPRegistry, shutdownAllMCPServers } from '../core/mcp-server-registry.js';
26
27
  // ES modules setup
27
28
  const __filename = fileURLToPath(import.meta.url);
28
29
  const __dirname = path.dirname(__filename);
@@ -47,12 +48,12 @@ function configureLLMProvidersFromEnv() {
47
48
  }
48
49
  });
49
50
  // Azure (requires multiple env vars)
50
- if (process.env.AZURE_OPENAI_API_KEY && process.env.AZURE_ENDPOINT && process.env.AZURE_DEPLOYMENT) {
51
+ if (process.env.AZURE_OPENAI_API_KEY && process.env.AZURE_RESOURCE_NAME && process.env.AZURE_DEPLOYMENT) {
51
52
  configureLLMProvider(LLMProvider.AZURE, {
52
53
  apiKey: process.env.AZURE_OPENAI_API_KEY,
53
- endpoint: process.env.AZURE_ENDPOINT,
54
+ resourceName: process.env.AZURE_RESOURCE_NAME,
54
55
  deployment: process.env.AZURE_DEPLOYMENT,
55
- apiVersion: process.env.AZURE_API_VERSION || '2023-12-01-preview'
56
+ apiVersion: process.env.AZURE_API_VERSION || '2024-10-21-preview'
56
57
  });
57
58
  serverLogger.debug('Configured Azure provider from environment');
58
59
  }
@@ -64,10 +65,10 @@ function configureLLMProvidersFromEnv() {
64
65
  });
65
66
  serverLogger.debug('Configured OpenAI-Compatible provider from environment');
66
67
  }
67
- // Ollama
68
- const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434/api';
68
+ // Ollama (OpenAI-compatible endpoint)
69
+ const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434/v1';
69
70
  configureLLMProvider(LLMProvider.OLLAMA, { baseUrl: ollamaUrl });
70
- serverLogger.debug('Configured Ollama provider', { baseUrl: ollamaUrl });
71
+ serverLogger.debug('Configured Ollama provider (OpenAI-compatible)', { baseUrl: ollamaUrl });
71
72
  }
72
73
  // Express app setup
73
74
  const app = express();
@@ -116,6 +117,9 @@ app.use((req, res) => {
116
117
  export function startWebServer(port = PORT, host = HOST) {
117
118
  return new Promise((resolve, reject) => {
118
119
  configureLLMProvidersFromEnv();
120
+ // Initialize MCP registry
121
+ initializeMCPRegistry();
122
+ serverLogger.info('MCP registry initialized');
119
123
  const server = app.listen(port, host, () => {
120
124
  const serverAddress = server.address();
121
125
  if (serverAddress && typeof serverAddress === 'object') {
@@ -129,6 +133,46 @@ export function startWebServer(port = PORT, host = HOST) {
129
133
  }
130
134
  });
131
135
  server.on('error', reject);
136
+ // Setup graceful shutdown handlers
137
+ const gracefulShutdown = async (signal) => {
138
+ serverLogger.info(`Received ${signal}, initiating graceful shutdown`);
139
+ try {
140
+ // Shutdown MCP servers first
141
+ await shutdownAllMCPServers();
142
+ serverLogger.info('MCP servers shut down');
143
+ // Close Express server
144
+ server.close((err) => {
145
+ if (err) {
146
+ serverLogger.error('Error closing server', { error: err.message });
147
+ process.exit(1);
148
+ }
149
+ else {
150
+ serverLogger.info('Express server closed');
151
+ process.exit(0);
152
+ }
153
+ });
154
+ }
155
+ catch (error) {
156
+ serverLogger.error('Error during graceful shutdown', {
157
+ error: error instanceof Error ? error.message : error
158
+ });
159
+ process.exit(1);
160
+ }
161
+ };
162
+ // Register shutdown handlers
163
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
164
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
165
+ // Handle uncaught exceptions and unhandled rejections
166
+ process.on('uncaughtException', (error) => {
167
+ serverLogger.error('Uncaught exception', { error: error.message });
168
+ gracefulShutdown('uncaughtException');
169
+ });
170
+ process.on('unhandledRejection', (reason, promise) => {
171
+ serverLogger.error('Unhandled rejection', {
172
+ reason: reason instanceof Error ? reason.message : reason,
173
+ promise: promise.toString()
174
+ });
175
+ });
132
176
  });
133
177
  }
134
178
  // Direct execution handling - check both direct execution and npm bin execution
package/package.json CHANGED
@@ -1,11 +1,24 @@
1
1
  {
2
2
  "name": "agent-world",
3
- "version": "0.4.6",
4
- "main": "index.ts",
3
+ "version": "0.6.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ },
11
+ "./core": {
12
+ "import": "./dist/core/index.js",
13
+ "types": "./dist/core/index.d.ts"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
5
17
  "type": "module",
6
18
  "workspaces": [
7
19
  "core",
8
- "next"
20
+ "next",
21
+ "web"
9
22
  ],
10
23
  "bin": {
11
24
  "agent-world": "dist/cli/index.js",
@@ -16,12 +29,13 @@
16
29
  "start": "node dist/server/index.js",
17
30
  "cli": "npx tsx cli/index.ts",
18
31
  "server": "npx tsx server/index.ts",
19
- "dev-web": "concurrently \"npm run server\" \"cd web && npm run dev\"",
32
+ "dev": "concurrently \"npm run server\" \"npm run dev --workspace=web\"",
20
33
  "test": "jest --config jest.config.js",
21
- "check": "tsc --noEmit",
22
- "build": "tsc && cd web && npm run build",
34
+ "test:db": "npx tsx tests/db/migration-tests.ts",
35
+ "check": "tsc --noEmit && npm run check --workspace=web",
36
+ "build": "npm run build --workspace=core && tsc --project tsconfig.build.json && npm run build --workspace=web",
23
37
  "pkill": "pkill -f tsx",
24
- "dev": "cd next && npm run dev"
38
+ "dev-next": "cd next && npm run dev"
25
39
  },
26
40
  "description": "World-mediated agent management system with clean API surface",
27
41
  "keywords": [
@@ -34,53 +48,24 @@
34
48
  ],
35
49
  "author": "",
36
50
  "license": "ISC",
37
- "exports": {
38
- ".": "./index.ts",
39
- "./package.json": "./package.json"
40
- },
41
51
  "dependencies": {
42
- "@ai-sdk/anthropic": "^1.2.12",
43
- "@ai-sdk/azure": "^1.3.23",
44
- "@ai-sdk/google": "^1.2.19",
45
- "@ai-sdk/openai": "^1.3.22",
46
- "@ai-sdk/openai-compatible": "^0.2.14",
47
- "@ai-sdk/xai": "^1.2.16",
48
- "@types/terminal-kit": "^2.5.7",
49
- "ai": "^4.3.16",
50
- "chalk": "^4.1.2",
52
+ "chalk": "^5.5.0",
51
53
  "cors": "^2.8.5",
52
- "dotenv": "^16.5.0",
53
54
  "enquirer": "^2.4.1",
54
- "events": "^3.3.0",
55
55
  "express": "^4.21.2",
56
- "ollama-ai-provider": "^1.2.0",
57
56
  "open": "^10.2.0",
58
- "pino": "^9.7.0",
59
- "pino-pretty": "^13.0.0",
60
- "sqlite3": "^5.1.7",
61
- "tsx": "^4.19.2",
62
- "typescript": "^5.8.3",
63
- "uuid": "^11.1.0",
64
- "zod": "^3.25.67"
57
+ "tsx": "^4.20.3",
58
+ "typescript": "^5.8.3"
65
59
  },
66
60
  "devDependencies": {
67
- "@types/chalk": "^0.4.31",
68
- "@types/cors": "^2.8.14",
61
+ "@types/cors": "^2.8.19",
69
62
  "@types/express": "^4.17.23",
70
- "@types/jest": "^29.5.14",
71
- "@types/node": "^20.19.9",
72
- "@types/pino": "^7.0.4",
73
- "@types/sqlite3": "^3.1.11",
74
- "@types/tmp": "^0.2.0",
75
- "@types/uuid": "^10.0.0",
76
- "@types/ws": "^8.18.1",
63
+ "@types/jest": "^30.0.0",
64
+ "@types/node": "^24.3.0",
77
65
  "commander": "^14.0.0",
78
- "concurrently": "^9.1.2",
79
- "esbuild": "^0.21.5",
80
- "jest": "^29.5.0",
81
- "nodemon": "^3.1.10",
82
- "tmp": "^0.2.0",
83
- "ts-jest": "^29.1.0"
66
+ "concurrently": "^9.2.0",
67
+ "jest": "^30.0.5",
68
+ "ts-jest": "^29.4.1"
84
69
  },
85
70
  "engines": {
86
71
  "node": ">=20.0.0"