@maudecode/openclaw-cowtail 0.14.0 → 0.14.1

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/dist/index.js CHANGED
@@ -14545,6 +14545,30 @@ function normalizeCowtailTarget(raw) {
14545
14545
  function buildCowtailTarget(threadId) {
14546
14546
  return `cowtail:${normalizeCowtailTarget(threadId)}`;
14547
14547
  }
14548
+ function resolveCowtailThreadIdFromSessionKey(sessionKey) {
14549
+ const raw = (sessionKey ?? "").trim();
14550
+ if (!raw) {
14551
+ return;
14552
+ }
14553
+ const parts = raw.split(":").filter(Boolean);
14554
+ if (parts.length < 4 || parts[0]?.toLowerCase() !== "agent") {
14555
+ return;
14556
+ }
14557
+ const cowtailIndex = parts.findIndex((part, index) => index >= 2 && part.toLowerCase() === "cowtail");
14558
+ if (cowtailIndex < 0) {
14559
+ if (parts[2]?.toLowerCase() !== "direct") {
14560
+ return;
14561
+ }
14562
+ const directTarget = normalizeCowtailTarget(parts.slice(3).join(":"));
14563
+ return directTarget || undefined;
14564
+ }
14565
+ const rest = parts.slice(cowtailIndex + 1);
14566
+ const first = rest[0]?.toLowerCase();
14567
+ const second = rest[1]?.toLowerCase();
14568
+ const target = first === "direct" || first === "group" || first === "channel" ? rest.slice(1).join(":") : second === "direct" || second === "group" || second === "channel" ? rest.slice(2).join(":") : rest.join(":");
14569
+ const normalized = normalizeCowtailTarget(target);
14570
+ return normalized || undefined;
14571
+ }
14548
14572
  function isSupportedCowtailAgent(agentId) {
14549
14573
  return (agentId ?? "main").trim() === "main";
14550
14574
  }
@@ -14581,10 +14605,11 @@ async function handleCowtailEvent(params) {
14581
14605
  return;
14582
14606
  }
14583
14607
  if (!thread.sessionKey) {
14608
+ const threadId = resolveCowtailThreadId({ route, thread });
14584
14609
  await client.sendSessionBound({
14585
14610
  type: "openclaw_session_bound",
14586
- idempotencyKey: `cowtail:session-bound:${thread.id}`,
14587
- threadId: thread.id,
14611
+ idempotencyKey: `cowtail:session-bound:${threadId}`,
14612
+ threadId,
14588
14613
  sessionKey: route.sessionKey
14589
14614
  });
14590
14615
  }
@@ -15052,11 +15077,13 @@ async function deliverCowtailReply(params) {
15052
15077
  }
15053
15078
  async function createDurableCowtailReply(params, message) {
15054
15079
  try {
15080
+ const routedThreadId = resolveCowtailThreadIdFromSessionKey(params.route.sessionKey);
15055
15081
  const result = await params.client.sendOpenClawMessage({
15056
15082
  type: "openclaw_message",
15057
15083
  idempotencyKey: params.streamState.idempotencyKey,
15058
15084
  streamId: params.streamState.streamId,
15059
- sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
15085
+ sessionKey: params.route.sessionKey,
15086
+ ...routedThreadId ? { threadId: routedThreadId } : {},
15060
15087
  title: params.thread.title,
15061
15088
  text: message.text,
15062
15089
  authorLabel: "OpenClaw",
@@ -15176,8 +15203,8 @@ function emitCowtailStreamSnapshot(params) {
15176
15203
  params.client.sendOpenClawStreamSnapshot({
15177
15204
  type: "openclaw_message_stream_snapshot",
15178
15205
  streamId: params.streamState.streamId,
15179
- sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
15180
- threadId: params.thread.id,
15206
+ sessionKey: params.route.sessionKey,
15207
+ threadId: resolveCowtailThreadId(params),
15181
15208
  text: params.text,
15182
15209
  links: params.streamState.links.map((link) => ({ ...link })),
15183
15210
  toolCalls: structuredClone(params.streamState.toolCalls),
@@ -15403,7 +15430,7 @@ function buildCowtailInboundBody(params) {
15403
15430
  };
15404
15431
  }
15405
15432
  function finalizeInboundContext(params) {
15406
- const target = buildCowtailTarget(params.thread.id);
15433
+ const target = buildCowtailTarget(resolveCowtailThreadId(params));
15407
15434
  return params.runtime.channel.reply.finalizeInboundContext({
15408
15435
  Body: params.body,
15409
15436
  BodyForAgent: params.text,
@@ -15447,14 +15474,17 @@ function resolveThreadRoute(params) {
15447
15474
  sessionKey: params.thread.sessionKey
15448
15475
  };
15449
15476
  }
15450
- const route = params.runtime.channel.routing.resolveAgentRoute({
15451
- cfg: params.runtime.config.loadConfig(),
15452
- channel: CHANNEL_ID,
15477
+ const threadId = normalizeCowtailTarget(params.thread.id);
15478
+ if (!threadId) {
15479
+ params.logger?.warn?.("Cowtail thread_created event has a blank thread id");
15480
+ return null;
15481
+ }
15482
+ const cfg = params.runtime.config.loadConfig();
15483
+ const route = resolveCowtailAgentRoute({
15484
+ cfg,
15485
+ threadId,
15453
15486
  accountId: params.account.accountId,
15454
- peer: {
15455
- kind: "direct",
15456
- id: buildCowtailTarget(params.thread.id)
15457
- }
15487
+ runtime: params.runtime
15458
15488
  });
15459
15489
  if (!isSupportedCowtailAgent(route.agentId)) {
15460
15490
  params.logger?.warn?.(`Cowtail thread ${params.thread.id} resolved unsupported agent ${route.agentId}`);
@@ -15465,6 +15495,45 @@ function resolveThreadRoute(params) {
15465
15495
  sessionKey: route.sessionKey
15466
15496
  };
15467
15497
  }
15498
+ function resolveCowtailAgentRoute(params) {
15499
+ const rawRoute = resolveAgentRouteForPeer({
15500
+ cfg: params.cfg,
15501
+ accountId: params.accountId,
15502
+ runtime: params.runtime,
15503
+ peerId: params.threadId
15504
+ });
15505
+ if (isPeerBindingRoute(rawRoute)) {
15506
+ return rawRoute;
15507
+ }
15508
+ const prefixedThreadId = buildCowtailTarget(params.threadId);
15509
+ if (prefixedThreadId === params.threadId) {
15510
+ return rawRoute;
15511
+ }
15512
+ const prefixedRoute = resolveAgentRouteForPeer({
15513
+ cfg: params.cfg,
15514
+ accountId: params.accountId,
15515
+ runtime: params.runtime,
15516
+ peerId: prefixedThreadId
15517
+ });
15518
+ return isPeerBindingRoute(prefixedRoute) ? prefixedRoute : rawRoute;
15519
+ }
15520
+ function resolveAgentRouteForPeer(params) {
15521
+ return params.runtime.channel.routing.resolveAgentRoute({
15522
+ cfg: params.cfg,
15523
+ channel: CHANNEL_ID,
15524
+ accountId: params.accountId,
15525
+ peer: {
15526
+ kind: "direct",
15527
+ id: params.peerId
15528
+ }
15529
+ });
15530
+ }
15531
+ function isPeerBindingRoute(route) {
15532
+ return route.matchedBy === "binding.peer";
15533
+ }
15534
+ function resolveCowtailThreadId(params) {
15535
+ return resolveCowtailThreadIdFromSessionKey(params.route.sessionKey) ?? normalizeCowtailTarget(params.thread.id);
15536
+ }
15468
15537
  function errorMessage(error48) {
15469
15538
  return error48 instanceof Error ? error48.message : String(error48);
15470
15539
  }
@@ -14545,6 +14545,30 @@ function normalizeCowtailTarget(raw) {
14545
14545
  function buildCowtailTarget(threadId) {
14546
14546
  return `cowtail:${normalizeCowtailTarget(threadId)}`;
14547
14547
  }
14548
+ function resolveCowtailThreadIdFromSessionKey(sessionKey) {
14549
+ const raw = (sessionKey ?? "").trim();
14550
+ if (!raw) {
14551
+ return;
14552
+ }
14553
+ const parts = raw.split(":").filter(Boolean);
14554
+ if (parts.length < 4 || parts[0]?.toLowerCase() !== "agent") {
14555
+ return;
14556
+ }
14557
+ const cowtailIndex = parts.findIndex((part, index) => index >= 2 && part.toLowerCase() === "cowtail");
14558
+ if (cowtailIndex < 0) {
14559
+ if (parts[2]?.toLowerCase() !== "direct") {
14560
+ return;
14561
+ }
14562
+ const directTarget = normalizeCowtailTarget(parts.slice(3).join(":"));
14563
+ return directTarget || undefined;
14564
+ }
14565
+ const rest = parts.slice(cowtailIndex + 1);
14566
+ const first = rest[0]?.toLowerCase();
14567
+ const second = rest[1]?.toLowerCase();
14568
+ const target = first === "direct" || first === "group" || first === "channel" ? rest.slice(1).join(":") : second === "direct" || second === "group" || second === "channel" ? rest.slice(2).join(":") : rest.join(":");
14569
+ const normalized = normalizeCowtailTarget(target);
14570
+ return normalized || undefined;
14571
+ }
14548
14572
  function isSupportedCowtailAgent(agentId) {
14549
14573
  return (agentId ?? "main").trim() === "main";
14550
14574
  }
@@ -14581,10 +14605,11 @@ async function handleCowtailEvent(params) {
14581
14605
  return;
14582
14606
  }
14583
14607
  if (!thread.sessionKey) {
14608
+ const threadId = resolveCowtailThreadId({ route, thread });
14584
14609
  await client.sendSessionBound({
14585
14610
  type: "openclaw_session_bound",
14586
- idempotencyKey: `cowtail:session-bound:${thread.id}`,
14587
- threadId: thread.id,
14611
+ idempotencyKey: `cowtail:session-bound:${threadId}`,
14612
+ threadId,
14588
14613
  sessionKey: route.sessionKey
14589
14614
  });
14590
14615
  }
@@ -15052,11 +15077,13 @@ async function deliverCowtailReply(params) {
15052
15077
  }
15053
15078
  async function createDurableCowtailReply(params, message) {
15054
15079
  try {
15080
+ const routedThreadId = resolveCowtailThreadIdFromSessionKey(params.route.sessionKey);
15055
15081
  const result = await params.client.sendOpenClawMessage({
15056
15082
  type: "openclaw_message",
15057
15083
  idempotencyKey: params.streamState.idempotencyKey,
15058
15084
  streamId: params.streamState.streamId,
15059
- sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
15085
+ sessionKey: params.route.sessionKey,
15086
+ ...routedThreadId ? { threadId: routedThreadId } : {},
15060
15087
  title: params.thread.title,
15061
15088
  text: message.text,
15062
15089
  authorLabel: "OpenClaw",
@@ -15176,8 +15203,8 @@ function emitCowtailStreamSnapshot(params) {
15176
15203
  params.client.sendOpenClawStreamSnapshot({
15177
15204
  type: "openclaw_message_stream_snapshot",
15178
15205
  streamId: params.streamState.streamId,
15179
- sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
15180
- threadId: params.thread.id,
15206
+ sessionKey: params.route.sessionKey,
15207
+ threadId: resolveCowtailThreadId(params),
15181
15208
  text: params.text,
15182
15209
  links: params.streamState.links.map((link) => ({ ...link })),
15183
15210
  toolCalls: structuredClone(params.streamState.toolCalls),
@@ -15403,7 +15430,7 @@ function buildCowtailInboundBody(params) {
15403
15430
  };
15404
15431
  }
15405
15432
  function finalizeInboundContext(params) {
15406
- const target = buildCowtailTarget(params.thread.id);
15433
+ const target = buildCowtailTarget(resolveCowtailThreadId(params));
15407
15434
  return params.runtime.channel.reply.finalizeInboundContext({
15408
15435
  Body: params.body,
15409
15436
  BodyForAgent: params.text,
@@ -15447,14 +15474,17 @@ function resolveThreadRoute(params) {
15447
15474
  sessionKey: params.thread.sessionKey
15448
15475
  };
15449
15476
  }
15450
- const route = params.runtime.channel.routing.resolveAgentRoute({
15451
- cfg: params.runtime.config.loadConfig(),
15452
- channel: CHANNEL_ID,
15477
+ const threadId = normalizeCowtailTarget(params.thread.id);
15478
+ if (!threadId) {
15479
+ params.logger?.warn?.("Cowtail thread_created event has a blank thread id");
15480
+ return null;
15481
+ }
15482
+ const cfg = params.runtime.config.loadConfig();
15483
+ const route = resolveCowtailAgentRoute({
15484
+ cfg,
15485
+ threadId,
15453
15486
  accountId: params.account.accountId,
15454
- peer: {
15455
- kind: "direct",
15456
- id: buildCowtailTarget(params.thread.id)
15457
- }
15487
+ runtime: params.runtime
15458
15488
  });
15459
15489
  if (!isSupportedCowtailAgent(route.agentId)) {
15460
15490
  params.logger?.warn?.(`Cowtail thread ${params.thread.id} resolved unsupported agent ${route.agentId}`);
@@ -15465,6 +15495,45 @@ function resolveThreadRoute(params) {
15465
15495
  sessionKey: route.sessionKey
15466
15496
  };
15467
15497
  }
15498
+ function resolveCowtailAgentRoute(params) {
15499
+ const rawRoute = resolveAgentRouteForPeer({
15500
+ cfg: params.cfg,
15501
+ accountId: params.accountId,
15502
+ runtime: params.runtime,
15503
+ peerId: params.threadId
15504
+ });
15505
+ if (isPeerBindingRoute(rawRoute)) {
15506
+ return rawRoute;
15507
+ }
15508
+ const prefixedThreadId = buildCowtailTarget(params.threadId);
15509
+ if (prefixedThreadId === params.threadId) {
15510
+ return rawRoute;
15511
+ }
15512
+ const prefixedRoute = resolveAgentRouteForPeer({
15513
+ cfg: params.cfg,
15514
+ accountId: params.accountId,
15515
+ runtime: params.runtime,
15516
+ peerId: prefixedThreadId
15517
+ });
15518
+ return isPeerBindingRoute(prefixedRoute) ? prefixedRoute : rawRoute;
15519
+ }
15520
+ function resolveAgentRouteForPeer(params) {
15521
+ return params.runtime.channel.routing.resolveAgentRoute({
15522
+ cfg: params.cfg,
15523
+ channel: CHANNEL_ID,
15524
+ accountId: params.accountId,
15525
+ peer: {
15526
+ kind: "direct",
15527
+ id: params.peerId
15528
+ }
15529
+ });
15530
+ }
15531
+ function isPeerBindingRoute(route) {
15532
+ return route.matchedBy === "binding.peer";
15533
+ }
15534
+ function resolveCowtailThreadId(params) {
15535
+ return resolveCowtailThreadIdFromSessionKey(params.route.sessionKey) ?? normalizeCowtailTarget(params.thread.id);
15536
+ }
15468
15537
  function errorMessage(error48) {
15469
15538
  return error48 instanceof Error ? error48.message : String(error48);
15470
15539
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maudecode/openclaw-cowtail",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Cowtail channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "repository": {