@ynhcj/xiaoyi-channel 0.0.112-next → 0.0.113-next

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/src/bot.js CHANGED
@@ -209,10 +209,6 @@ export async function handleXYMessage(params) {
209
209
  const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
210
210
  // Build message body with speaker prefix (following feishu pattern)
211
211
  let messageBody = textForAgent;
212
- // Embed A2A taskId marker before the user query so the provider can
213
- // extract it from messages without relying on AsyncLocalStorage.
214
- // The provider strips this marker before sending to the model.
215
- messageBody = `xiaoyiA2A info[taskId:${parsed.taskId},deviceType:${deviceType}] ${messageBody}`;
216
212
  // Add speaker prefix for clarity
217
213
  const speaker = parsed.sessionId;
218
214
  messageBody = `${speaker}: ${messageBody}`;
@@ -240,7 +236,7 @@ export async function handleXYMessage(params) {
240
236
  SenderId: parsed.sessionId,
241
237
  Provider: "xiaoyi-channel",
242
238
  Surface: "xiaoyi-channel",
243
- MessageSid: parsed.messageId,
239
+ MessageSid: `${parsed.taskId}_${deviceType}`,
244
240
  Timestamp: Date.now(),
245
241
  WasMentioned: false,
246
242
  CommandAuthorized: true,
@@ -388,6 +388,22 @@ function trimUserMetadata(text) {
388
388
  text = text.replace(/\n*Sender \(untrusted metadata\):\n```json\n[\s\S]*?\n```\n*/, "\n");
389
389
  return text.replace(/\n{3,}/g, "\n\n");
390
390
  }
391
+ /**
392
+ * Extract A2A taskId and deviceType from Conversation info JSON.
393
+ * bot.ts stores them as MessageSid = "taskId_deviceType".
394
+ */
395
+ function extractA2AFromConversationInfo(text) {
396
+ const match = text.match(/Conversation info \(untrusted metadata\):\n```json\n([\s\S]*?)\n```/);
397
+ if (!match)
398
+ return null;
399
+ const msgIdMatch = match[1].match(/"message_id"\s*:\s*"([^"]+)"/);
400
+ if (!msgIdMatch)
401
+ return null;
402
+ const parts = msgIdMatch[1].split("_");
403
+ if (parts.length < 2)
404
+ return null;
405
+ return { taskId: parts[0], deviceType: parts[1] };
406
+ }
391
407
  export const xiaoyiProvider = {
392
408
  id: "xiaoyiprovider",
393
409
  label: "Xiaoyi Provider",
@@ -442,53 +458,30 @@ export const xiaoyiProvider = {
442
458
  const underlying = ctx.streamFn;
443
459
  if (!underlying)
444
460
  return underlying;
445
- // ── Regex to extract A2A taskId/deviceType from user messages ──
446
- // bot.ts embeds: xiaoyiA2A info[taskId:<id>,deviceType:<type>]
447
- const A2A_MARKER_RE = /xiaoyiA2A info\[taskId:([^\],]+)(?:,deviceType:([^\]]+))?\]\s*/;
448
461
  return async (model, context, options) => {
449
462
  const dynamicHeaders = {};
450
- // ── Extract A2A taskId/deviceType from user messages ──
451
- // Scan from last user message backwards; strip the marker from all.
452
- let resolvedTaskId = null;
453
- let resolvedDeviceType = null;
463
+ // ── Extract A2A taskId/deviceType from Conversation info ──
464
+ // bot.ts stores taskId_deviceType as MessageSid, which the framework
465
+ // renders as message_id in the Conversation info JSON block.
466
+ let extractedTaskId = null;
467
+ let extractedDeviceType = null;
454
468
  if (context.messages) {
455
- console.log(`[xiaoyiprovider] user messages BEFORE strip (count=${context.messages.filter(m => m.role === "user").length}):`);
456
- for (const msg of context.messages) {
457
- if (msg.role !== "user")
458
- continue;
459
- const preview = typeof msg.content === "string"
460
- ? msg.content.slice(0, 200)
461
- : JSON.stringify(msg.content).slice(0, 200);
462
- console.log(`[xiaoyiprovider] user: ${preview}`);
463
- }
464
469
  for (let i = context.messages.length - 1; i >= 0; i--) {
465
470
  const msg = context.messages[i];
466
- if (msg.role !== "user" || !msg.content)
471
+ if (msg.role !== "user")
467
472
  continue;
468
- const extract = (text) => {
469
- const match = text.match(A2A_MARKER_RE);
470
- if (match) {
471
- if (!resolvedTaskId)
472
- resolvedTaskId = match[1] ?? null;
473
- if (!resolvedDeviceType)
474
- resolvedDeviceType = match[2] ?? null;
475
- return text.replace(A2A_MARKER_RE, "");
476
- }
477
- return null;
478
- };
479
- if (typeof msg.content === "string") {
480
- const stripped = extract(msg.content);
481
- if (stripped !== null)
482
- msg.content = stripped;
483
- }
484
- else if (Array.isArray(msg.content)) {
485
- for (const block of msg.content) {
486
- if (block.type === "text" && typeof block.text === "string") {
487
- const stripped = extract(block.text);
488
- if (stripped !== null)
489
- block.text = stripped;
490
- }
491
- }
473
+ const text = typeof msg.content === "string"
474
+ ? msg.content
475
+ : Array.isArray(msg.content)
476
+ ? msg.content.find((b) => b.type === "text")?.text ?? ""
477
+ : "";
478
+ if (!text)
479
+ continue;
480
+ const extracted = extractA2AFromConversationInfo(text);
481
+ if (extracted) {
482
+ extractedTaskId = extracted.taskId;
483
+ extractedDeviceType = extracted.deviceType;
484
+ break;
492
485
  }
493
486
  }
494
487
  }
@@ -510,9 +503,9 @@ export const xiaoyiProvider = {
510
503
  dynamicHeaders["x-cron-flag"] = "begin";
511
504
  }
512
505
  }
513
- else if (resolvedTaskId) {
514
- // Session mode: taskId extracted from user message marker
515
- const traceId = resolvedTaskId;
506
+ else if (extractedTaskId) {
507
+ // Session mode: taskId extracted from Conversation info
508
+ const traceId = extractedTaskId;
516
509
  const sessionId = traceId.split("&")[0];
517
510
  const interactionId = traceId.split("&")[1] ?? "";
518
511
  const isCron = isCronTriggered(context.messages);
@@ -524,13 +517,11 @@ export const xiaoyiProvider = {
524
517
  if (context.messages?.length === 1)
525
518
  dynamicHeaders["x-cron-flag"] = "begin";
526
519
  }
527
- if (typeof sessionId === "string")
528
- dynamicHeaders[HEADER_SESSION_ID] = sessionId;
529
- if (typeof interactionId === "string")
530
- dynamicHeaders[HEADER_INTERACTION_ID] = interactionId;
520
+ dynamicHeaders[HEADER_SESSION_ID] = sessionId;
521
+ dynamicHeaders[HEADER_INTERACTION_ID] = interactionId;
531
522
  }
532
523
  else {
533
- // No marker found – fall back to extraParams cached values
524
+ // Fallback: use extraParams cached values
534
525
  const traceId = ctx.extraParams[HEADER_TRACE_ID];
535
526
  const sessionId = ctx.extraParams[HEADER_SESSION_ID];
536
527
  const interactionId = ctx.extraParams[HEADER_INTERACTION_ID];
@@ -547,9 +538,11 @@ export const xiaoyiProvider = {
547
538
  if (context.systemPrompt) {
548
539
  console.log(`[xiaoyiprovider] system prompt length: ${context.systemPrompt.length}`);
549
540
  }
550
- // deviceType: prefer marker-extracted value, fall back to extraParams
541
+ // deviceType: prefer value extracted from Conversation info,
542
+ // then extraParams, then ALS fallback.
551
543
  const extraParamsDeviceType = ctx.extraParams?.[DEVICE_TYPE_KEY] || undefined;
552
- const deviceType = resolvedDeviceType || extraParamsDeviceType || undefined;
544
+ const deviceType = (extractedDeviceType || extraParamsDeviceType)
545
+ ?? getCurrentSessionContext()?.deviceType;
553
546
  // 在发送给模型前,优化 systemPrompt 结构
554
547
  if (context.systemPrompt) {
555
548
  let sp = context.systemPrompt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.112-next",
3
+ "version": "0.0.113-next",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",