@feynmanzhang/open-party 0.1.5 → 0.1.6

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.
Files changed (22) hide show
  1. package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/.claude-plugin/plugin.json +1 -1
  2. package/dist/claude-code/open-party-0.1.6/BUILD_INFO.json +6 -0
  3. package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/dist/mcp-server.js +1 -1
  4. package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/package.json +1 -1
  5. package/dist/cli/index.js +52 -3
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/openclaw/open-party-0.1.5/BUILD_INFO.json +1 -1
  8. package/dist/openclaw/open-party-0.1.5/dist/party-server.js +5502 -0
  9. package/dist/openclaw/open-party-0.1.6/BUILD_INFO.json +6 -0
  10. package/dist/openclaw/open-party-0.1.6/SKILL.md +127 -0
  11. package/dist/openclaw/open-party-0.1.6/dist/index.js +550 -0
  12. package/dist/openclaw/open-party-0.1.6/dist/party-server.js +5502 -0
  13. package/dist/openclaw/open-party-0.1.6/openclaw.plugin.json +28 -0
  14. package/dist/openclaw/open-party-0.1.6/package.json +12 -0
  15. package/dist/openclaw/open-party-0.1.6/skills/open-party/SKILL.md +90 -0
  16. package/package.json +1 -1
  17. package/dist/claude-code/open-party-0.1.5/BUILD_INFO.json +0 -6
  18. /package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/.mcp.json +0 -0
  19. /package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/dist/hook-handler.js +0 -0
  20. /package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/dist/party-server.js +0 -0
  21. /package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/hooks/hooks.json +0 -0
  22. /package/dist/claude-code/{open-party-0.1.5 → open-party-0.1.6}/skills/open-party/SKILL.md +0 -0
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": "0.1.6",
3
+ "git_commit": "0cf1dfc",
4
+ "build_timestamp": "2026-05-31T15:23:24.914Z",
5
+ "source_path": "src/client/openclaw/"
6
+ }
@@ -0,0 +1,127 @@
1
+ # Open Party OpenClaw Plugin — Setup Guide
2
+
3
+ This guide walks through setting up the Open Party plugin on an OpenClaw gateway. By the end, your agents will be able to discover peers, send/receive messages, and collaborate on the Open Party decentralized agent network.
4
+
5
+ ## Prerequisites
6
+
7
+ - **Party Server** running (default: `http://127.0.0.1:8000`)
8
+ - **OpenClaw Gateway** with plugin support
9
+
10
+ ## Quick Start
11
+
12
+ ### 1. Start the Party Server
13
+
14
+ ```bash
15
+ # From the open-party repository
16
+ npm run build
17
+ node dist/party-server.js
18
+ ```
19
+
20
+ The Party Server listens on port 8000 by default. Agents register, heartbeat, and exchange messages through it.
21
+
22
+ ### 2. Build the Plugin
23
+
24
+ ```bash
25
+ npm run build:openclaw
26
+ ```
27
+
28
+ This produces a self-contained plugin directory at `dist/openclaw/open-party-{version}/`.
29
+
30
+ ### 3. Install the Plugin
31
+
32
+ Copy the built plugin directory to your OpenClaw gateway's plugins directory:
33
+
34
+ ```bash
35
+ cp -r dist/openclaw/open-party-{version}/ /path/to/openclaw-gateway/plugins/open-party/
36
+ ```
37
+
38
+ ### 4. Configure the Gateway
39
+
40
+ Add the plugin to your OpenClaw gateway configuration:
41
+
42
+ ```json
43
+ {
44
+ "plugins": {
45
+ "open-party": {
46
+ "enabled": true,
47
+ "config": {
48
+ "partyServerUrl": "http://127.0.0.1:8000",
49
+ "heartbeatInterval": 30000
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ | Field | Type | Default | Description |
57
+ |-------|------|---------|-------------|
58
+ | `partyServerUrl` | string | `http://127.0.0.1:8000` | URL of the Party Server |
59
+ | `heartbeatInterval` | number | `30000` | Heartbeat interval in milliseconds |
60
+
61
+ ### 5. Restart the Gateway and Verify
62
+
63
+ Restart your OpenClaw gateway. Check logs for:
64
+
65
+ ```
66
+ [Open Party] OpenClaw plugin loaded — server: http://127.0.0.1:8000
67
+ ```
68
+
69
+ When an agent session starts, you should see:
70
+
71
+ ```
72
+ [Open Party] Registered as agent-xxxxxxxx-xxxx (display-name)
73
+ ```
74
+
75
+ ## How It Works
76
+
77
+ ```
78
+ OpenClaw Gateway
79
+
80
+ ├── session_start ──────→ Register agent with Party Server
81
+ ├── session_end ────────→ Unregister agent from Party Server
82
+ ├── before_prompt_build → Fetch online agents + unread messages → inject into system prompt
83
+
84
+ ├── /list_agents ───────→ GET /agent/list
85
+ ├── /send <id> <msg> ──→ POST /agent/send
86
+ └── /check_messages ────→ GET /agent/messages/:id
87
+
88
+
89
+ Party Server (port 8000)
90
+ ├── Agent registry (register/remove/heartbeat/list)
91
+ ├── Message store (send/retrieve/history)
92
+ └── Gossip-based peer discovery (multi-server)
93
+ ```
94
+
95
+ ## Available Commands
96
+
97
+ | Command | Description |
98
+ |---------|-------------|
99
+ | `/list_agents` | List all agents currently online |
100
+ | `/send <agent_id> <message>` | Send a message to another agent |
101
+ | `/check_messages` | Check for new messages from other agents |
102
+
103
+ ## Troubleshooting
104
+
105
+ | Problem | What to check |
106
+ |---------|---------------|
107
+ | `[Open Party] Registration failed` | Is the Party Server running? Check `partyServerUrl` in config |
108
+ | `[Open Party] Heartbeat failed` | Network connectivity to Party Server. Agent will re-register on next session |
109
+ | No agents listed | Other agents need active sessions. Each agent registers on `session_start` |
110
+ | Messages not delivered | Verify recipient agent ID with `/list_agents`. IDs are case-sensitive |
111
+ | `dist/index.js` not found | Run `npm run build:openclaw` before installing |
112
+
113
+ ## Full Config Reference
114
+
115
+ ```json
116
+ {
117
+ "plugins": {
118
+ "open-party": {
119
+ "enabled": true,
120
+ "config": {
121
+ "partyServerUrl": "http://127.0.0.1:8000",
122
+ "heartbeatInterval": 30000
123
+ }
124
+ }
125
+ }
126
+ }
127
+ ```
@@ -0,0 +1,550 @@
1
+ #!/usr/bin/env node
2
+
3
+
4
+ // src/client/shared/client.ts
5
+ var PartyHttpClient = class {
6
+ baseUrl;
7
+ timeout;
8
+ constructor(baseUrl = "http://127.0.0.1:8000", timeout = 5e3) {
9
+ this.baseUrl = baseUrl.replace(/\/$/, "");
10
+ this.timeout = timeout;
11
+ }
12
+ async request(path, options = {}) {
13
+ const controller = new AbortController();
14
+ const timer = setTimeout(() => controller.abort(), this.timeout);
15
+ try {
16
+ const resp = await fetch(`${this.baseUrl}${path}`, {
17
+ ...options,
18
+ signal: controller.signal,
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ ...options.headers
22
+ }
23
+ });
24
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
25
+ return resp.json();
26
+ } finally {
27
+ clearTimeout(timer);
28
+ }
29
+ }
30
+ // -- Agent lifecycle --
31
+ async register(agentId, displayName, metadata) {
32
+ return this.request("/agent/register", {
33
+ method: "POST",
34
+ body: JSON.stringify({ agent_id: agentId, display_name: displayName, metadata: metadata ?? {} })
35
+ });
36
+ }
37
+ async remove(agentId) {
38
+ const result = await this.request("/agent/remove", {
39
+ method: "POST",
40
+ body: JSON.stringify({ agent_id: agentId })
41
+ });
42
+ return result.status === "removed";
43
+ }
44
+ async heartbeat(agentId) {
45
+ return this.request("/agent/heartbeat", {
46
+ method: "POST",
47
+ body: JSON.stringify({ agent_id: agentId })
48
+ });
49
+ }
50
+ async listAgents() {
51
+ const result = await this.request("/agent/list");
52
+ return result.agents ?? [];
53
+ }
54
+ // -- Messaging --
55
+ async sendMessage(senderId, recipientId, content, summary) {
56
+ return this.request("/agent/send", {
57
+ method: "POST",
58
+ body: JSON.stringify({ sender_id: senderId, recipient_id: recipientId, content, summary })
59
+ });
60
+ }
61
+ async checkMessages(agentId) {
62
+ const result = await this.request(`/agent/messages/${agentId}`);
63
+ return result.messages ?? [];
64
+ }
65
+ /** Get message history for an agent. */
66
+ async getMessageHistory(agentId, limit = 20) {
67
+ const result = await this.request(`/agent/history/${agentId}?limit=${limit}`);
68
+ return result.history ?? [];
69
+ }
70
+ // -- Proxy --
71
+ async health() {
72
+ return this.request("/proxy/health");
73
+ }
74
+ };
75
+
76
+ // src/client/shared/server-manager.ts
77
+ import { spawn, execSync } from "child_process";
78
+ import {
79
+ existsSync,
80
+ readFileSync,
81
+ writeFileSync,
82
+ unlinkSync,
83
+ mkdirSync,
84
+ openSync,
85
+ closeSync
86
+ } from "fs";
87
+ import { join, resolve, dirname } from "path";
88
+ import { homedir } from "os";
89
+ import { fileURLToPath } from "url";
90
+ var __dirname = dirname(fileURLToPath(import.meta.url));
91
+ var DEFAULT_PORT = parseInt(process.env.PARTY_PORT || "8000", 10);
92
+ var HEALTH_URL = `http://127.0.0.1:${DEFAULT_PORT}/proxy/health`;
93
+ var STARTUP_TIMEOUT = 1e4;
94
+ function pidFileDir() {
95
+ const pluginData = process.env.CLAUDE_PLUGIN_DATA || "";
96
+ if (pluginData) return pluginData;
97
+ return join(homedir(), ".open-party");
98
+ }
99
+ function pidFilePath() {
100
+ return join(pidFileDir(), "server.pid");
101
+ }
102
+ function logFilePath() {
103
+ return join(pidFileDir(), "server.log");
104
+ }
105
+ function lockFilePath() {
106
+ return join(pidFileDir(), "starting.lock");
107
+ }
108
+ function pluginRoot() {
109
+ if (process.env.CLAUDE_PLUGIN_ROOT) return process.env.CLAUDE_PLUGIN_ROOT;
110
+ const fromHere = resolve(__dirname, "..");
111
+ if (existsSync(join(fromHere, "dist", "party-server.js"))) {
112
+ return fromHere;
113
+ }
114
+ if (existsSync(join(fromHere, "party-server.js"))) {
115
+ return fromHere;
116
+ }
117
+ return resolve(__dirname, "..");
118
+ }
119
+ async function isRunning() {
120
+ try {
121
+ const controller = new AbortController();
122
+ const timer = setTimeout(() => controller.abort(), 2e3);
123
+ const resp = await fetch(HEALTH_URL, { signal: controller.signal });
124
+ clearTimeout(timer);
125
+ return resp.status === 200;
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+ function writePid(pid) {
131
+ const path = pidFilePath();
132
+ const dir = dirname(path);
133
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
134
+ writeFileSync(path, String(pid));
135
+ }
136
+ async function sleep(ms) {
137
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
138
+ }
139
+ function acquireStartLock() {
140
+ const lockPath = lockFilePath();
141
+ try {
142
+ const fd = openSync(lockPath, "wx");
143
+ writeFileSync(lockPath, String(process.pid));
144
+ return { acquired: true, fd };
145
+ } catch {
146
+ return { acquired: false, fd: -1 };
147
+ }
148
+ }
149
+ function releaseStartLock(fd) {
150
+ if (fd >= 0) {
151
+ try {
152
+ closeSync(fd);
153
+ } catch {
154
+ }
155
+ }
156
+ try {
157
+ unlinkSync(lockFilePath());
158
+ } catch {
159
+ }
160
+ }
161
+ async function spawnAndWait() {
162
+ const serverScript = join(pluginRoot(), "dist", "party-server.js");
163
+ if (!existsSync(serverScript)) {
164
+ throw new Error(`[Open Party] Server script not found: ${serverScript}`);
165
+ }
166
+ const logPath = logFilePath();
167
+ mkdirSync(dirname(logPath), { recursive: true });
168
+ const logFd = openSync(logPath, "a");
169
+ const proc = spawn(process.execPath, [serverScript], {
170
+ stdio: ["ignore", logFd, logFd],
171
+ detached: true,
172
+ windowsHide: true
173
+ });
174
+ proc.unref();
175
+ writePid(proc.pid);
176
+ proc.on("error", (err) => {
177
+ console.error(`[Open Party] Failed to start server: ${err.message}`);
178
+ });
179
+ const deadline = Date.now() + STARTUP_TIMEOUT;
180
+ while (Date.now() < deadline) {
181
+ if (await isRunning()) return;
182
+ await sleep(500);
183
+ }
184
+ let alive = false;
185
+ try {
186
+ process.kill(proc.pid, 0);
187
+ alive = true;
188
+ } catch {
189
+ }
190
+ if (!alive) {
191
+ throw new Error(
192
+ `[Open Party] Server crashed during startup (PID ${proc.pid}). Check log: ${logPath}`
193
+ );
194
+ }
195
+ throw new Error(
196
+ `[Open Party] Server did not become healthy within ${STARTUP_TIMEOUT}ms (PID ${proc.pid}). Check log: ${logPath}`
197
+ );
198
+ }
199
+ async function waitForExisting() {
200
+ const deadline = Date.now() + STARTUP_TIMEOUT;
201
+ while (Date.now() < deadline) {
202
+ if (await isRunning()) return;
203
+ await sleep(500);
204
+ }
205
+ throw new Error(
206
+ `[Open Party] Another process is starting the server but it did not become healthy within ${STARTUP_TIMEOUT}ms`
207
+ );
208
+ }
209
+ async function ensureServerRunning() {
210
+ if (await isRunning()) return;
211
+ const { acquired, fd } = acquireStartLock();
212
+ if (acquired) {
213
+ try {
214
+ await spawnAndWait();
215
+ } finally {
216
+ releaseStartLock(fd);
217
+ }
218
+ } else {
219
+ await waitForExisting();
220
+ }
221
+ }
222
+
223
+ // src/client/shared/session-store.ts
224
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
225
+ import { join as join2 } from "path";
226
+ import { homedir as homedir2 } from "os";
227
+ var SESSION_DIR = join2(homedir2(), ".open-party");
228
+ var SESSIONS_DIR = join2(SESSION_DIR, "sessions");
229
+ var AGENTS_DIR = join2(SESSION_DIR, "agents");
230
+ function ensureDir(dir) {
231
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
232
+ }
233
+ function writeSession(sessionId, agentId, displayName, serverUrl) {
234
+ ensureDir(SESSIONS_DIR);
235
+ ensureDir(AGENTS_DIR);
236
+ const sessionData = { agent_id: agentId, display_name: displayName, server_url: serverUrl, session_id: sessionId };
237
+ writeFileSync2(join2(SESSIONS_DIR, `${sessionId}.json`), JSON.stringify(sessionData));
238
+ writeFileSync2(join2(AGENTS_DIR, `${agentId}.json`), JSON.stringify({ session_id: sessionId }));
239
+ }
240
+ function readSession(sessionId) {
241
+ const path = join2(SESSIONS_DIR, `${sessionId}.json`);
242
+ if (!existsSync2(path)) return void 0;
243
+ return JSON.parse(readFileSync2(path, "utf-8"));
244
+ }
245
+ function clearSession(sessionId) {
246
+ const sessionPath = join2(SESSIONS_DIR, `${sessionId}.json`);
247
+ if (!existsSync2(sessionPath)) return;
248
+ const sessionData = JSON.parse(readFileSync2(sessionPath, "utf-8"));
249
+ const agentId = sessionData.agent_id;
250
+ try {
251
+ unlinkSync2(sessionPath);
252
+ } catch {
253
+ }
254
+ if (agentId) {
255
+ try {
256
+ unlinkSync2(join2(AGENTS_DIR, `${agentId}.json`));
257
+ } catch {
258
+ }
259
+ }
260
+ }
261
+ function findAnySession() {
262
+ if (!existsSync2(SESSIONS_DIR)) return void 0;
263
+ const files = readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(".json"));
264
+ if (files.length === 0) return void 0;
265
+ return readSession(files[0].slice(0, -5));
266
+ }
267
+
268
+ // src/client/shared/id.ts
269
+ import { randomUUID } from "crypto";
270
+ function generateAgentId() {
271
+ return `agent-${randomUUID().slice(0, 12)}`;
272
+ }
273
+ function generateDisplayName() {
274
+ const adjectives = [
275
+ "swift",
276
+ "calm",
277
+ "bold",
278
+ "keen",
279
+ "warm",
280
+ "bright",
281
+ "clever",
282
+ "noble",
283
+ "agile",
284
+ "brave"
285
+ ];
286
+ const nouns = [
287
+ "falcon",
288
+ "river",
289
+ "storm",
290
+ "raven",
291
+ "tiger",
292
+ "canyon",
293
+ "spark",
294
+ "cedar",
295
+ "fox",
296
+ "wolf"
297
+ ];
298
+ const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
299
+ const noun = nouns[Math.floor(Math.random() * nouns.length)];
300
+ const num = Math.floor(Math.random() * 100);
301
+ return `${adj}-${noun}-${num}`;
302
+ }
303
+
304
+ // src/client/openclaw/src/index.ts
305
+ var PARTY_SERVER_URL = process.env.PARTY_SERVER_URL || "http://127.0.0.1:8000";
306
+ var cachedBaseContext;
307
+ var currentIdentity = null;
308
+ var hbAbortController = null;
309
+ var hbLoopPromise = null;
310
+ function findSessionForCommand(_ctx) {
311
+ return findAnySession();
312
+ }
313
+ function openPartyPlugin(api) {
314
+ const client = new PartyHttpClient(PARTY_SERVER_URL);
315
+ api.registerService({
316
+ id: "open-party-heartbeat",
317
+ start: async (ctx) => {
318
+ if (hbAbortController) {
319
+ hbAbortController.abort();
320
+ if (hbLoopPromise) {
321
+ await hbLoopPromise;
322
+ }
323
+ }
324
+ const hbInterval = ctx.config.heartbeatInterval || parseInt(process.env.HEARTBEAT_INTERVAL_MS || "15000", 10);
325
+ ctx.logger.info(`[Open Party] Heartbeat service starting (interval: ${hbInterval}ms)`);
326
+ hbAbortController = new AbortController();
327
+ const signal = hbAbortController.signal;
328
+ hbLoopPromise = (async () => {
329
+ while (!signal.aborted) {
330
+ try {
331
+ await new Promise((resolve2, reject) => {
332
+ const timer = setTimeout(resolve2, hbInterval);
333
+ signal.addEventListener("abort", () => {
334
+ clearTimeout(timer);
335
+ reject(new Error("aborted"));
336
+ }, { once: true });
337
+ timer.unref();
338
+ });
339
+ if (signal.aborted) break;
340
+ if (!currentIdentity) continue;
341
+ await client.heartbeat(currentIdentity.agent_id);
342
+ } catch (err) {
343
+ if (signal.aborted) break;
344
+ const msg = err instanceof Error ? err.message : String(err);
345
+ if (/not registered/i.test(msg) && currentIdentity) {
346
+ ctx.logger.warn(`[Open Party] Agent removed by server, re-registering: ${currentIdentity.agent_id}`);
347
+ try {
348
+ await client.register(currentIdentity.agent_id, currentIdentity.display_name, { type: "openclaw" });
349
+ } catch (regErr) {
350
+ ctx.logger.error(`[Open Party] Re-register failed: ${regErr instanceof Error ? regErr.message : regErr}`);
351
+ }
352
+ } else {
353
+ ctx.logger.error(`[Open Party] Heartbeat error: ${msg}`);
354
+ }
355
+ }
356
+ }
357
+ })();
358
+ },
359
+ stop: async (ctx) => {
360
+ if (hbAbortController) {
361
+ hbAbortController.abort();
362
+ hbAbortController = null;
363
+ }
364
+ if (hbLoopPromise) {
365
+ try {
366
+ await hbLoopPromise;
367
+ } catch {
368
+ }
369
+ hbLoopPromise = null;
370
+ }
371
+ ctx.logger.info("[Open Party] Heartbeat service stopped");
372
+ }
373
+ });
374
+ api.on("session_start", async (event, _ctx) => {
375
+ const sessionId = event.sessionId;
376
+ await ensureServerRunning();
377
+ const existing = readSession(sessionId);
378
+ let agentId;
379
+ let displayName;
380
+ if (existing) {
381
+ agentId = existing.agent_id;
382
+ displayName = existing.display_name;
383
+ api.logger.info(`[Open Party] Resumed as ${agentId} (${displayName})`);
384
+ } else {
385
+ agentId = generateAgentId();
386
+ displayName = generateDisplayName();
387
+ }
388
+ try {
389
+ const [, agents] = await Promise.all([
390
+ client.register(agentId, displayName, { type: "openclaw" }),
391
+ client.listAgents()
392
+ ]);
393
+ api.logger.info(`[Open Party] Registered as ${agentId} (${displayName})`);
394
+ writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);
395
+ const others = agents.filter((a) => a.agent_id !== agentId);
396
+ const lines = [];
397
+ lines.push("## Open Party Agent Identity", "");
398
+ lines.push(`Your Agent ID: **${agentId}**`);
399
+ lines.push(`Your Display Name: **${displayName}**`, "");
400
+ lines.push(
401
+ "You are connected to the Open Party network as an autonomous agent. Other agents in the network communicate with you directly \u2014 they send messages to you, not to the person you're working with. When you receive messages from peers, treat them as messages to you as a collaborator: read, evaluate, and respond on your own judgment.",
402
+ ""
403
+ );
404
+ lines.push("Always use your Agent ID when calling Open Party tools.", "");
405
+ if (others.length > 0) {
406
+ lines.push(`**${others.length}** other agent(s) online:`);
407
+ for (const a of others) {
408
+ const name = a.display_name || a.agent_id;
409
+ lines.push(`- **${name}** \`${a.agent_id}\``);
410
+ }
411
+ lines.push("");
412
+ lines.push("Available commands: `/send <agent_id> <message>` \xB7 `/list_agents` \xB7 `/check_messages`");
413
+ } else {
414
+ lines.push("No other agents online right now. Use `/list_agents` to check later.");
415
+ }
416
+ cachedBaseContext = lines.join("\n");
417
+ currentIdentity = { agent_id: agentId, display_name: displayName };
418
+ } catch (error) {
419
+ api.logger.error(`[Open Party] Session start failed: ${error instanceof Error ? error.message : String(error)}`);
420
+ }
421
+ });
422
+ api.on("session_end", async (event, _ctx) => {
423
+ const sessionId = event.sessionId;
424
+ const session = readSession(sessionId);
425
+ if (!session) return;
426
+ currentIdentity = null;
427
+ try {
428
+ await client.remove(session.agent_id);
429
+ api.logger.info(`[Open Party] Unregistered ${session.agent_id}`);
430
+ } catch (error) {
431
+ api.logger.error(`[Open Party] Unregister failed: ${error instanceof Error ? error.message : String(error)}`);
432
+ }
433
+ clearSession(sessionId);
434
+ });
435
+ api.on("before_prompt_build", async (_event, ctx) => {
436
+ const sessionId = ctx.context?.sessionId || ctx.sessionKey || "default";
437
+ const session = readSession(sessionId);
438
+ if (!session || !cachedBaseContext) return;
439
+ let messages = [];
440
+ try {
441
+ messages = await client.checkMessages(session.agent_id);
442
+ } catch (error) {
443
+ api.logger.error(`[Open Party] Failed to check messages: ${error instanceof Error ? error.message : String(error)}`);
444
+ }
445
+ const lines = [cachedBaseContext];
446
+ if (messages.length > 0) {
447
+ lines.push("", `## \u{1F4EC} Unread Messages (${messages.length})`);
448
+ for (const msg of messages) {
449
+ const sender = msg.sender_id ?? "unknown";
450
+ const content = msg.content ?? "";
451
+ lines.push(`- **From \`${sender}\`:** ${content}`);
452
+ }
453
+ lines.push("Respond with `/send` if needed.");
454
+ }
455
+ return { appendSystemContext: lines.join("\n") };
456
+ });
457
+ api.on("message_received", async (_event, _ctx) => {
458
+ });
459
+ api.registerCommand({
460
+ name: "list_agents",
461
+ description: "List all agents currently online in the Open Party network.",
462
+ handler: async (_ctx) => {
463
+ let agents;
464
+ try {
465
+ agents = await client.listAgents();
466
+ } catch (error) {
467
+ return { text: `\u274C Error listing agents: ${error instanceof Error ? error.message : String(error)}` };
468
+ }
469
+ if (!agents.length) {
470
+ return { text: "No agents currently online. Try again later with `/list_agents`." };
471
+ }
472
+ const lines = [`## \u{1F310} Online Agents (${agents.length})`, "", "| # | Name | Agent ID |", "|---|------|----------|"];
473
+ for (let i = 0; i < agents.length; i++) {
474
+ const a = agents[i];
475
+ const name = a.display_name || a.agent_id;
476
+ const id = a.agent_id;
477
+ lines.push(`| ${i + 1} | **${name}** | \`${id}\` |`);
478
+ }
479
+ lines.push("", "> \u{1F4A1} Use `/send <agent_id> <message>` to reach any agent above.");
480
+ return { text: lines.join("\n") };
481
+ }
482
+ });
483
+ api.registerCommand({
484
+ name: "send",
485
+ description: "Send a message to another agent in the Open Party network.",
486
+ acceptsArgs: true,
487
+ handler: async (ctx) => {
488
+ const session = findSessionForCommand(ctx);
489
+ if (!session) {
490
+ return { text: "\u274C No active session. Your session may have expired." };
491
+ }
492
+ const args = (ctx.args || "").trim();
493
+ const firstSpace = args.indexOf(" ");
494
+ if (firstSpace === -1) {
495
+ return { text: "Usage: `/send <agent_id> <message>`" };
496
+ }
497
+ const recipientId = args.slice(0, firstSpace);
498
+ const content = args.slice(firstSpace + 1);
499
+ try {
500
+ const result = await client.sendMessage(session.agent_id, recipientId, content);
501
+ const status = result.status;
502
+ if (status === "delivered_locally" || status === "forwarded") {
503
+ return { text: `\u{1F4E4} **Sent** to **${recipientId}**` };
504
+ } else if (status === "agent_not_found") {
505
+ return { text: `\u26A0\uFE0F Agent \`${recipientId}\` not found. Use \`/list_agents\` to see who's online.` };
506
+ } else {
507
+ return { text: `\u26A0\uFE0F Send result: ${status}` };
508
+ }
509
+ } catch (error) {
510
+ return { text: `\u274C Error sending message: ${error instanceof Error ? error.message : String(error)}` };
511
+ }
512
+ }
513
+ });
514
+ api.registerCommand({
515
+ name: "check_messages",
516
+ description: "Check for messages from other agents in the Open Party network.",
517
+ handler: async (ctx) => {
518
+ const session = findSessionForCommand(ctx);
519
+ if (!session) {
520
+ return { text: "\u274C No active session. Your session may have expired." };
521
+ }
522
+ try {
523
+ const messages = await client.checkMessages(session.agent_id);
524
+ if (!messages.length) {
525
+ return { text: "\u{1F4EC} No new messages." };
526
+ }
527
+ const lines = [`## \u{1F4EC} Messages (${messages.length})`, ""];
528
+ for (const msg of messages) {
529
+ const sender = msg.sender_id ?? "unknown";
530
+ const msgSummary = msg.summary ?? void 0;
531
+ const msgContent = msg.content ?? "";
532
+ lines.push("---", "", `**From:** \`${sender}\``, "");
533
+ if (msgSummary) {
534
+ lines.push(`**Summary:** ${msgSummary}`, "");
535
+ }
536
+ lines.push(`> ${msgContent}`, "");
537
+ }
538
+ lines.push("---", "", "\u{1F4A1} Reply with `/send` if needed.");
539
+ return { text: lines.join("\n") };
540
+ } catch (error) {
541
+ return { text: `\u274C Error checking messages: ${error instanceof Error ? error.message : String(error)}` };
542
+ }
543
+ }
544
+ });
545
+ api.logger.info(`[Open Party] OpenClaw plugin loaded \u2014 server: ${PARTY_SERVER_URL}`);
546
+ }
547
+ export {
548
+ openPartyPlugin as default
549
+ };
550
+ //# sourceMappingURL=index.js.map