@ynhcj/xiaoyi-channel 0.0.176-beta → 0.0.178-beta

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
@@ -1,4 +1,4 @@
1
- import { updateSessionStoreEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
1
+ import { updateSessionStoreEntry, updateSessionStore, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
2
2
  import { getXYRuntime } from "./runtime.js";
3
3
  import { createXYReplyDispatcher } from "./reply-dispatcher.js";
4
4
  import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId, extractDeviceType, extractModelName, extractTriggerData, extractRunCrossTaskContext } from "./parser.js";
@@ -33,7 +33,6 @@ export async function handleXYMessage(params) {
33
33
  try {
34
34
  // Check for special messages BEFORE parsing (these have different param structures)
35
35
  const messageMethod = message.method;
36
- logger.log(`[BOT] Received A2A message: ${JSON.stringify(message)}`);
37
36
  // Handle clearContext messages (sessionId at top level, no params)
38
37
  if (messageMethod === "clearContext" || messageMethod === "clear_context") {
39
38
  const sessionId = message.sessionId ?? message.params?.sessionId;
@@ -179,16 +178,41 @@ export async function handleXYMessage(params) {
179
178
  // configured default model instead of the A2A-specified one.
180
179
  if (modelName && modelName.trim() !== "" && modelName.toLowerCase() !== "none") {
181
180
  try {
182
- await updateSessionStoreEntry({
183
- storePath: resolveStorePath(),
181
+ const storePath = resolveStorePath();
182
+ const result = await updateSessionStoreEntry({
183
+ storePath,
184
184
  sessionKey: route.sessionKey,
185
185
  update: async () => ({
186
186
  providerOverride: "xiaoyiprovider",
187
187
  modelOverride: modelName,
188
188
  modelOverrideSource: "user",
189
+ model: "",
190
+ modelProvider: "",
189
191
  }),
190
192
  });
191
- log.log(`[BOT] Patched session store model override: xiaoyiprovider/${modelName}`);
193
+ if (!result) {
194
+ // Session entry doesn't exist yet (first message, xy_channel
195
+ // bypasses the standard turn kernel). Create a minimal entry
196
+ // with the override via updateSessionStore.
197
+ await updateSessionStore(storePath, (store) => {
198
+ if (!store[route.sessionKey]) {
199
+ store[route.sessionKey] = {
200
+ // sessionId must pass validateSessionId regex /^[a-z0-9][a-z0-9._-]{0,127}$/i
201
+ // route.sessionKey like "agent:main:direct:xxx" contains colons which are invalid.
202
+ // Use parsed.sessionId (raw UUID from A2A) which is always safe.
203
+ sessionId: parsed.sessionId,
204
+ updatedAt: Date.now(),
205
+ providerOverride: "xiaoyiprovider",
206
+ modelOverride: modelName,
207
+ modelOverrideSource: "user",
208
+ };
209
+ }
210
+ });
211
+ log.log(`[BOT] Created session entry with model override: xiaoyiprovider/${modelName}`);
212
+ }
213
+ else {
214
+ log.log(`[BOT] Patched session store model override: xiaoyiprovider/${modelName}`);
215
+ }
192
216
  }
193
217
  catch (patchErr) {
194
218
  log.error(`[BOT] Failed to patch session model override:`, patchErr);
@@ -417,6 +417,32 @@ export const xiaoyiProvider = {
417
417
  docsPath: "/providers/models",
418
418
  auth: [],
419
419
  isCacheTtlEligible: () => true,
420
+ /**
421
+ * Dynamic model resolution for A2A-specified model names.
422
+ * A2A messages carry a dynamic modelName that isn't in any static catalog.
423
+ * This hook lets OpenClaw's resolveModelAsync accept any model ID under
424
+ * xiaoyiprovider as long as the provider has a configured baseUrl.
425
+ */
426
+ resolveDynamicModel: (ctx) => {
427
+ const baseUrl = ctx.providerConfig?.baseUrl;
428
+ if (!baseUrl || typeof baseUrl !== "string")
429
+ return null;
430
+ return {
431
+ id: ctx.modelId,
432
+ name: ctx.modelId,
433
+ api: ctx.providerConfig?.api ?? "openai-completions",
434
+ provider: "xiaoyiprovider",
435
+ baseUrl,
436
+ reasoning: false,
437
+ input: ["text"],
438
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
439
+ contextWindow: 128_000,
440
+ maxTokens: 8192,
441
+ ...(ctx.providerConfig?.headers && typeof ctx.providerConfig.headers === "object"
442
+ ? { headers: ctx.providerConfig.headers }
443
+ : {}),
444
+ };
445
+ },
420
446
  /**
421
447
  * Store uid-based fallback prefix for lazy timestamp generation in wrapStreamFn.
422
448
  * Session-level headers (traceId / sessionId / interactionId) are resolved
@@ -116,10 +116,6 @@ export function createXYReplyDispatcher(params) {
116
116
  let hasSentResponse = false;
117
117
  let finalSent = false;
118
118
  let accumulatedText = "";
119
- let accumulatedReasoningHistory = "";
120
- let lastReasoningText = "";
121
- let accumulatedReplyHistory = "";
122
- let lastReplyText = "";
123
119
  const initialRunCrossTaskContext = getCurrentSessionContext()?.runCrossTaskContext;
124
120
  const getRunCrossTaskContext = () => {
125
121
  return getCurrentSessionContext()?.runCrossTaskContext ?? initialRunCrossTaskContext;
@@ -408,26 +404,12 @@ export function createXYReplyDispatcher(params) {
408
404
  }
409
405
  try {
410
406
  if (text.length > 0) {
411
- // 🔑 检测是否是新一轮思考:当前text比上一次短,或不以上次内容开头
412
- const isNewRound = lastReasoningText.length > 0 &&
413
- text.length < lastReasoningText.length;
414
- if (isNewRound) {
415
- // 将上一轮思考追加到历史
416
- accumulatedReasoningHistory += (accumulatedReasoningHistory ? "\n\n" : "") + lastReasoningText;
417
- }
418
- // 更新当前轮最后一次text
419
- lastReasoningText = text;
420
- // 🔑 拼接历史 + 当前轮内容
421
- const fullText = accumulatedReasoningHistory
422
- ? accumulatedReasoningHistory + "\n\n" + text
423
- : text;
424
- // 🔑 将模型真实的thinking/reasoning内容通过reasoningText转发
425
407
  await sendReasoningTextUpdate({
426
408
  config,
427
409
  sessionId,
428
410
  taskId: currentTaskId,
429
411
  messageId: currentMessageId,
430
- text: fullText,
412
+ text,
431
413
  append: false,
432
414
  });
433
415
  }
@@ -443,31 +425,17 @@ export function createXYReplyDispatcher(params) {
443
425
  }
444
426
  const currentTaskId = getActiveTaskId();
445
427
  const currentMessageId = getActiveMessageId();
446
- let text = payload.text ?? "";
428
+ const text = payload.text ?? "";
447
429
  try {
448
430
  if (text.length > 0) {
449
- // 🔑 检测是否是新一轮回复:当前text比上一次短,或不以上次内容开头
450
- const isNewRound = lastReplyText.length > 0 &&
451
- (text.length < lastReplyText.length || !text.startsWith(lastReplyText));
452
- if (isNewRound) {
453
- // 将上一轮回复追加到历史
454
- accumulatedReplyHistory += (accumulatedReplyHistory ? "\n\n" : "") + lastReplyText;
455
- }
456
- // 更新当前轮最后一次text
457
- lastReplyText = text;
458
- // 🔑 拼接历史 + 当前轮内容
459
- const fullText = accumulatedReplyHistory
460
- ? accumulatedReplyHistory + "\n\n" + text
461
- : text;
462
431
  accumulatedText += text;
463
432
  hasSentResponse = true;
464
- // 🔑 流式文本通过 A2A text 通道发送(而非 reasoningText)
465
433
  await sendA2AResponse({
466
434
  config,
467
435
  sessionId,
468
436
  taskId: currentTaskId,
469
437
  messageId: currentMessageId,
470
- text: fullText,
438
+ text,
471
439
  append: false,
472
440
  final: false,
473
441
  log: false,
@@ -17,10 +17,6 @@ import { createGetCollectionToolSchemaTool } from "./get-collection-tool-schema.
17
17
  // import { createGetEmailToolSchemaTool } from "./get-email-tool-schema.js";
18
18
  import { createLoginTokenTool } from "./login-token-tool.js";
19
19
  import { createAgentAsSkillTool } from "./agent-as-skill-tool.js";
20
- import { createDiscoverCrossDevicesTool } from "./discover-cross-devices-tool.js";
21
- import { createSendCrossDeviceTaskTool } from "./send-cross-device-task-tool.js";
22
- import { createDisplayA2UICardTool } from "./display-a2ui-card-tool.js";
23
- import { createCheckPluginPrivilegeTool } from "./check-plugin-privilege-tool.js";
24
20
  import { logger } from "../utils/logger.js";
25
21
  /**
26
22
  * Create all XY channel tools for the given session context.
@@ -36,9 +32,9 @@ export function createAllTools(ctx) {
36
32
  logger.log(`[CREATE-ALL-TOOLS] creating tools`);
37
33
  return [
38
34
  createLocationTool(ctx),
39
- createDiscoverCrossDevicesTool(ctx),
40
- createSendCrossDeviceTaskTool(ctx),
41
- createDisplayA2UICardTool(ctx),
35
+ // createDiscoverCrossDevicesTool(ctx),
36
+ // createSendCrossDeviceTaskTool(ctx),
37
+ // createDisplayA2UICardTool(ctx),
42
38
  createCallDeviceTool(ctx),
43
39
  createGetNoteToolSchemaTool(ctx),
44
40
  createGetCalendarToolSchemaTool(ctx),
@@ -57,6 +53,6 @@ export function createAllTools(ctx) {
57
53
  createSaveSelfEvolutionSkillTool(ctx),
58
54
  createLoginTokenTool(ctx),
59
55
  createAgentAsSkillTool(ctx),
60
- createCheckPluginPrivilegeTool(ctx),
56
+ // createCheckPluginPrivilegeTool(ctx),
61
57
  ];
62
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.176-beta",
3
+ "version": "0.0.178-beta",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",