openclaw-pincer 0.3.3 → 0.3.5

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.
@@ -247,6 +247,12 @@ function handleMessage(config, ctx, msg) {
247
247
  const roomId = data.room_id ?? msg.room_id ?? "";
248
248
  if (sender === config.agentId || !content)
249
249
  return;
250
+ // Mention-only: only respond when @agentName or @all (prevents echo loops)
251
+ const agentName = config.agentName ?? "";
252
+ const isMentioned = agentName && content.includes(`@${agentName}`);
253
+ const isBroadcast = content.includes("@all") || content.includes("@所有人");
254
+ if (!isMentioned && !isBroadcast)
255
+ return;
250
256
  console.log(`[openclaw-pincer] 💬 Room msg from ${sender.slice(0, 8)}: ${content.slice(0, 60)}`);
251
257
  dispatchToAgent(config, ctx, runtime, sender, content, roomId);
252
258
  return;
@@ -281,10 +287,46 @@ function dispatchToAgent(config, ctx, runtime, senderId, text, roomId) {
281
287
  accountId: ctx.accountId,
282
288
  peer,
283
289
  });
290
+ // Build structured route header so the agent knows exactly how to respond
291
+ // and which API endpoints to call for opincer operations.
292
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
293
+ const routeHeader = roomId
294
+ ? [
295
+ `[Pincer Route]`,
296
+ `type: room`,
297
+ `room_id: ${roomId}`,
298
+ `from_agent_id: ${senderId}`,
299
+ `my_agent_id: ${config.agentId}`,
300
+ ``,
301
+ `[Pincer Room msg from ${senderId}]`,
302
+ ].join("\n")
303
+ : [
304
+ `[Pincer Route]`,
305
+ `type: dm`,
306
+ `from_agent_id: ${senderId}`,
307
+ `my_agent_id: ${config.agentId}`,
308
+ ``,
309
+ `[Pincer DM from ${senderId}]`,
310
+ ].join("\n");
311
+ const replyHint = roomId
312
+ ? [
313
+ ``,
314
+ `To reply in this room, POST to ${baseUrl}/api/v1/rooms/${roomId}/messages:`,
315
+ ` {"sender_agent_id": "${config.agentId}", "content": "<reply>"}`,
316
+ ` Header: X-API-Key: ${config.token}`,
317
+ `Do NOT reply via Feishu or other messaging channels.`,
318
+ ].join("\n")
319
+ : [
320
+ ``,
321
+ `To reply via DM, POST to ${baseUrl}/api/v1/messages/send:`,
322
+ ` {"from_agent_id": "${config.agentId}", "to_agent_id": "${senderId}", "payload": {"text": "<reply>"}}`,
323
+ ` Header: X-API-Key: ${config.token}`,
324
+ ].join("\n");
325
+ const agentText = `${routeHeader}\n${text}${replyHint}`;
284
326
  runtime.reply.dispatchReplyWithBufferedBlockDispatcher({
285
327
  ctx: {
286
- Body: text,
287
- BodyForAgent: text,
328
+ Body: agentText,
329
+ BodyForAgent: agentText,
288
330
  From: senderId,
289
331
  SessionKey: route.sessionKey,
290
332
  Channel: "openclaw-pincer",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-pincer",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Pincer channel plugin for OpenClaw",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/channel.ts CHANGED
@@ -277,6 +277,11 @@ function handleMessage(config: PincerConfig, ctx: any, msg: any) {
277
277
  const content = data.content ?? "";
278
278
  const roomId = data.room_id ?? msg.room_id ?? "";
279
279
  if (sender === config.agentId || !content) return;
280
+ // Mention-only: only respond when @agentName or @all (prevents echo loops)
281
+ const agentName = config.agentName ?? "";
282
+ const isMentioned = agentName && content.includes(`@${agentName}`);
283
+ const isBroadcast = content.includes("@all") || content.includes("@所有人");
284
+ if (!isMentioned && !isBroadcast) return;
280
285
  console.log(`[openclaw-pincer] 💬 Room msg from ${sender.slice(0, 8)}: ${content.slice(0, 60)}`);
281
286
  dispatchToAgent(config, ctx, runtime, sender, content, roomId);
282
287
  return;
@@ -320,10 +325,49 @@ function dispatchToAgent(
320
325
  peer,
321
326
  });
322
327
 
328
+ // Build structured route header so the agent knows exactly how to respond
329
+ // and which API endpoints to call for opincer operations.
330
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
331
+ const routeHeader = roomId
332
+ ? [
333
+ `[Pincer Route]`,
334
+ `type: room`,
335
+ `room_id: ${roomId}`,
336
+ `from_agent_id: ${senderId}`,
337
+ `my_agent_id: ${config.agentId}`,
338
+ ``,
339
+ `[Pincer Room msg from ${senderId}]`,
340
+ ].join("\n")
341
+ : [
342
+ `[Pincer Route]`,
343
+ `type: dm`,
344
+ `from_agent_id: ${senderId}`,
345
+ `my_agent_id: ${config.agentId}`,
346
+ ``,
347
+ `[Pincer DM from ${senderId}]`,
348
+ ].join("\n");
349
+
350
+ const replyHint = roomId
351
+ ? [
352
+ ``,
353
+ `To reply in this room, POST to ${baseUrl}/api/v1/rooms/${roomId}/messages:`,
354
+ ` {"sender_agent_id": "${config.agentId}", "content": "<reply>"}`,
355
+ ` Header: X-API-Key: ${config.token}`,
356
+ `Do NOT reply via Feishu or other messaging channels.`,
357
+ ].join("\n")
358
+ : [
359
+ ``,
360
+ `To reply via DM, POST to ${baseUrl}/api/v1/messages/send:`,
361
+ ` {"from_agent_id": "${config.agentId}", "to_agent_id": "${senderId}", "payload": {"text": "<reply>"}}`,
362
+ ` Header: X-API-Key: ${config.token}`,
363
+ ].join("\n");
364
+
365
+ const agentText = `${routeHeader}\n${text}${replyHint}`;
366
+
323
367
  runtime.reply.dispatchReplyWithBufferedBlockDispatcher({
324
368
  ctx: {
325
- Body: text,
326
- BodyForAgent: text,
369
+ Body: agentText,
370
+ BodyForAgent: agentText,
327
371
  From: senderId,
328
372
  SessionKey: route.sessionKey,
329
373
  Channel: "openclaw-pincer",