@ynhcj/xiaoyi-channel 0.0.203-beta → 0.0.205-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.
Files changed (118) hide show
  1. package/dist/index.js +3 -3
  2. package/dist/src/bot.js +52 -34
  3. package/dist/src/channel.js +68 -64
  4. package/dist/src/client.d.ts +5 -0
  5. package/dist/src/client.js +10 -0
  6. package/dist/src/cspl/call_api.js +2 -0
  7. package/dist/src/cspl/sentinel_hook.js +9 -8
  8. package/dist/src/formatter.js +21 -31
  9. package/dist/src/monitor.js +20 -9
  10. package/dist/src/outbound.js +5 -0
  11. package/dist/src/provider.js +49 -14
  12. package/dist/src/reply-dispatcher.d.ts +3 -0
  13. package/dist/src/reply-dispatcher.js +152 -159
  14. package/dist/src/self-evolution-handler.js +2 -1
  15. package/dist/src/task-manager.d.ts +5 -6
  16. package/dist/src/task-manager.js +5 -9
  17. package/dist/src/tools/agent-as-skill-tool.d.ts +41 -2
  18. package/dist/src/tools/agent-as-skill-tool.js +144 -146
  19. package/dist/src/tools/calendar-tool.d.ts +24 -2
  20. package/dist/src/tools/calendar-tool.js +115 -117
  21. package/dist/src/tools/call-device-tool.d.ts +20 -6
  22. package/dist/src/tools/call-device-tool.js +116 -138
  23. package/dist/src/tools/call-phone-tool.d.ts +21 -2
  24. package/dist/src/tools/call-phone-tool.js +112 -114
  25. package/dist/src/tools/check-plugin-privilege-tool.d.ts +16 -2
  26. package/dist/src/tools/check-plugin-privilege-tool.js +141 -143
  27. package/dist/src/tools/create-alarm-tool.d.ts +39 -2
  28. package/dist/src/tools/create-alarm-tool.js +229 -231
  29. package/dist/src/tools/delete-alarm-tool.d.ts +15 -2
  30. package/dist/src/tools/delete-alarm-tool.js +134 -136
  31. package/dist/src/tools/device-tool-map.js +3 -1
  32. package/dist/src/tools/discover-cross-devices-tool.d.ts +16 -2
  33. package/dist/src/tools/discover-cross-devices-tool.js +121 -124
  34. package/dist/src/tools/display-a2ui-card-bypath-tool.d.ts +21 -2
  35. package/dist/src/tools/display-a2ui-card-bypath-tool.js +53 -56
  36. package/dist/src/tools/get-alarm-tool-schema.d.ts +1 -2
  37. package/dist/src/tools/get-alarm-tool-schema.js +10 -16
  38. package/dist/src/tools/get-calendar-tool-schema.d.ts +1 -2
  39. package/dist/src/tools/get-calendar-tool-schema.js +8 -12
  40. package/dist/src/tools/get-collection-tool-schema.d.ts +1 -2
  41. package/dist/src/tools/get-collection-tool-schema.js +9 -11
  42. package/dist/src/tools/get-contact-tool-schema.d.ts +1 -2
  43. package/dist/src/tools/get-contact-tool-schema.js +10 -16
  44. package/dist/src/tools/get-device-file-tool-schema.d.ts +1 -2
  45. package/dist/src/tools/get-device-file-tool-schema.js +9 -13
  46. package/dist/src/tools/get-email-tool-schema.d.ts +1 -2
  47. package/dist/src/tools/get-email-tool-schema.js +8 -11
  48. package/dist/src/tools/get-note-tool-schema.d.ts +1 -2
  49. package/dist/src/tools/get-note-tool-schema.js +9 -14
  50. package/dist/src/tools/get-photo-tool-schema.d.ts +1 -2
  51. package/dist/src/tools/get-photo-tool-schema.js +9 -12
  52. package/dist/src/tools/image-reading-tool.d.ts +28 -2
  53. package/dist/src/tools/image-reading-tool.js +76 -76
  54. package/dist/src/tools/location-tool.d.ts +11 -2
  55. package/dist/src/tools/location-tool.js +93 -92
  56. package/dist/src/tools/login-token-tool.d.ts +20 -2
  57. package/dist/src/tools/login-token-tool.js +123 -125
  58. package/dist/src/tools/modify-alarm-tool.d.ts +47 -2
  59. package/dist/src/tools/modify-alarm-tool.js +250 -252
  60. package/dist/src/tools/modify-note-tool.d.ts +20 -2
  61. package/dist/src/tools/modify-note-tool.js +107 -109
  62. package/dist/src/tools/note-tool.d.ts +20 -2
  63. package/dist/src/tools/note-tool.js +106 -108
  64. package/dist/src/tools/query-app-message-tool.d.ts +28 -2
  65. package/dist/src/tools/query-app-message-tool.js +111 -113
  66. package/dist/src/tools/query-memory-data-tool.d.ts +28 -2
  67. package/dist/src/tools/query-memory-data-tool.js +112 -114
  68. package/dist/src/tools/query-todo-task-tool.d.ts +24 -2
  69. package/dist/src/tools/query-todo-task-tool.js +106 -108
  70. package/dist/src/tools/save-file-to-phone-tool.d.ts +24 -2
  71. package/dist/src/tools/save-file-to-phone-tool.js +130 -132
  72. package/dist/src/tools/save-media-to-gallery-tool.d.ts +24 -2
  73. package/dist/src/tools/save-media-to-gallery-tool.js +137 -139
  74. package/dist/src/tools/save-self-evolution-skill-tool.d.ts +54 -2
  75. package/dist/src/tools/save-self-evolution-skill-tool.js +194 -194
  76. package/dist/src/tools/search-alarm-tool.d.ts +34 -2
  77. package/dist/src/tools/search-alarm-tool.js +174 -176
  78. package/dist/src/tools/search-calendar-tool.d.ts +24 -2
  79. package/dist/src/tools/search-calendar-tool.js +148 -150
  80. package/dist/src/tools/search-contact-tool.d.ts +16 -2
  81. package/dist/src/tools/search-contact-tool.js +101 -103
  82. package/dist/src/tools/search-email-tool.d.ts +21 -2
  83. package/dist/src/tools/search-email-tool.js +110 -112
  84. package/dist/src/tools/search-file-tool.d.ts +16 -2
  85. package/dist/src/tools/search-file-tool.js +104 -106
  86. package/dist/src/tools/search-message-tool.d.ts +16 -2
  87. package/dist/src/tools/search-message-tool.js +103 -105
  88. package/dist/src/tools/search-note-tool.d.ts +16 -2
  89. package/dist/src/tools/search-note-tool.js +98 -100
  90. package/dist/src/tools/search-photo-gallery-tool.d.ts +21 -2
  91. package/dist/src/tools/search-photo-gallery-tool.js +35 -37
  92. package/dist/src/tools/send-cross-device-task-tool.d.ts +35 -2
  93. package/dist/src/tools/send-cross-device-task-tool.js +140 -143
  94. package/dist/src/tools/send-email-tool.d.ts +24 -2
  95. package/dist/src/tools/send-email-tool.js +108 -110
  96. package/dist/src/tools/send-file-to-user-tool.d.ts +20 -2
  97. package/dist/src/tools/send-file-to-user-tool.js +174 -176
  98. package/dist/src/tools/send-html-card-tool.d.ts +20 -2
  99. package/dist/src/tools/send-html-card-tool.js +85 -87
  100. package/dist/src/tools/send-message-tool.d.ts +20 -2
  101. package/dist/src/tools/send-message-tool.js +122 -124
  102. package/dist/src/tools/session-manager.d.ts +7 -50
  103. package/dist/src/tools/session-manager.js +40 -231
  104. package/dist/src/tools/upload-file-tool.d.ts +20 -2
  105. package/dist/src/tools/upload-file-tool.js +80 -82
  106. package/dist/src/tools/upload-photo-tool.d.ts +20 -2
  107. package/dist/src/tools/upload-photo-tool.js +68 -70
  108. package/dist/src/tools/xiaoyi-add-collection-tool.d.ts +32 -2
  109. package/dist/src/tools/xiaoyi-add-collection-tool.js +146 -148
  110. package/dist/src/tools/xiaoyi-collection-tool.d.ts +20 -2
  111. package/dist/src/tools/xiaoyi-collection-tool.js +114 -116
  112. package/dist/src/tools/xiaoyi-delete-collection-tool.d.ts +15 -2
  113. package/dist/src/tools/xiaoyi-delete-collection-tool.js +127 -129
  114. package/dist/src/tools/xiaoyi-gui-tool.d.ts +16 -2
  115. package/dist/src/tools/xiaoyi-gui-tool.js +92 -95
  116. package/dist/src/utils/logger.js +3 -14
  117. package/dist/src/websocket.d.ts +1 -1
  118. package/package.json +2 -2
@@ -10,8 +10,9 @@ import { handleLoginTokenEvent } from "./login-token-handler.js";
10
10
  import { handleCronQueryEvent } from "./cron-query-handler.js";
11
11
  import { handleMemoryQueryEvent } from "./memory-query-handler.js";
12
12
  import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
13
- import { cleanupStaleSessions, getActiveSessionCount, cleanupAllSessions } from "./tools/session-manager.js";
14
13
  import { logger } from "./utils/logger.js";
14
+ import { XYFileUploadService } from "./file-upload.js";
15
+ import { startLogReporter } from "./log-reporter/index.js";
15
16
  /**
16
17
  * Per-session serial queue that ensures messages from the same session are processed
17
18
  * in arrival order while allowing different sessions to run concurrently.
@@ -75,6 +76,8 @@ export async function monitorXYProvider(opts = {}) {
75
76
  let globalDispatchInitGate = Promise.resolve();
76
77
  // Health check interval
77
78
  let healthCheckInterval = null;
79
+ // Log reporter stop handle
80
+ let stopLogReporter = null;
78
81
  return new Promise((resolve, reject) => {
79
82
  // Event handlers (defined early so they can be referenced in cleanup)
80
83
  const messageHandler = (message, sessionId, serverId) => {
@@ -214,6 +217,11 @@ export async function monitorXYProvider(opts = {}) {
214
217
  };
215
218
  const cleanup = () => {
216
219
  logger.log("XY gateway: cleaning up...");
220
+ // Stop log reporter
221
+ if (stopLogReporter) {
222
+ stopLogReporter();
223
+ stopLogReporter = null;
224
+ }
217
225
  // 🔍 Diagnose before cleanup
218
226
  logger.log("[DIAGNOSTICS] Checking WebSocket managers before cleanup...");
219
227
  diagnoseAllManagers();
@@ -239,8 +247,7 @@ export async function monitorXYProvider(opts = {}) {
239
247
  wsManager.disconnect();
240
248
  // ✅ Remove manager from cache to prevent reusing dirty state
241
249
  removeXYWebSocketManager(account);
242
- // Clean up all active sessions
243
- cleanupAllSessions();
250
+ // Session context is ALS-scoped now — nothing global to clean up.
244
251
  loggedServers.clear();
245
252
  activeMessages.clear();
246
253
  logger.log(`[MONITOR-HANDLER] Cleanup complete, cleared active messages and sessions`);
@@ -309,12 +316,7 @@ export async function monitorXYProvider(opts = {}) {
309
316
  if (cleaned > 0) {
310
317
  logger.log(`[HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
311
318
  }
312
- // Cleanup stale sessions (older than 10min TTL)
313
- const cleanedSessions = cleanupStaleSessions();
314
- const remainingSessions = getActiveSessionCount();
315
- if (cleanedSessions > 0 || remainingSessions > 0) {
316
- logger.log(`[HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
317
- }
319
+ // Session context is ALS-scoped no global session cleanup needed.
318
320
  // Cleanup stale temp files (older than 24 hours)
319
321
  void cleanupStaleTempFiles();
320
322
  }, 6 * 60 * 60 * 1000); // 6 hours
@@ -322,6 +324,15 @@ export async function monitorXYProvider(opts = {}) {
322
324
  wsManager.connect()
323
325
  .then(() => {
324
326
  logger.log("XY gateway: started successfully");
327
+ // Start log reporter (independent periodic scanner + uploader)
328
+ startLogReporter({
329
+ configPath: "/home/ynhcj/.openclaw/log-reporter-config.json",
330
+ uploadService: new XYFileUploadService(account.fileUploadUrl, account.apiKey, account.uid),
331
+ }).then((stop) => {
332
+ stopLogReporter = stop;
333
+ }).catch((err) => {
334
+ logger.warn(`Log reporter not started: ${String(err)}`);
335
+ });
325
336
  })
326
337
  .catch((err) => {
327
338
  // Connection failed but don't reject - continue monitoring for reconnection
@@ -1,3 +1,7 @@
1
+ // Outbound adapter for XY channel
2
+ // Following feishu/outbound.ts pattern
3
+ // NOTE: Using any for compatibility with SDK 2026.3.24
4
+ import os from "os";
1
5
  import { resolveXYConfig } from "./config.js";
2
6
  import { XYFileUploadService } from "./file-upload.js";
3
7
  import { XYPushService } from "./push.js";
@@ -219,6 +223,7 @@ export const xyOutbound = {
219
223
  },
220
224
  },
221
225
  error: { code: 0 },
226
+ hostname: os.hostname(),
222
227
  }),
223
228
  };
224
229
  // Get WebSocket manager and send message
@@ -128,6 +128,7 @@ function createRetryingStream(createStream, cronJob) {
128
128
  let hasContent = false;
129
129
  const buffer = [];
130
130
  let errorResult = null;
131
+ const fullResponseEvents = [];
131
132
  for await (const event of stream) {
132
133
  const isContent = CONTENT_EVENT_TYPES.has(event.type);
133
134
  if (!hasContent && !isContent) {
@@ -150,9 +151,14 @@ function createRetryingStream(createStream, cronJob) {
150
151
  if (!hasContent) {
151
152
  logger.log("[xiaoyiprovider] first content event received, switching to streaming mode");
152
153
  hasContent = true;
153
- for (const b of buffer)
154
+ for (const b of buffer) {
155
+ if (CONTENT_EVENT_TYPES.has(b.type))
156
+ fullResponseEvents.push(b);
154
157
  yield b;
158
+ }
155
159
  }
160
+ if (isContent)
161
+ fullResponseEvents.push(event);
156
162
  // IMPORTANT: resolve result() BEFORE yielding terminal events to avoid deadlock.
157
163
  // The SDK calls result() when it sees done/error — if we yield first, the generator
158
164
  // suspends and can never reach resolve, causing a permanent deadlock.
@@ -398,6 +404,10 @@ function trimUserMetadata(text) {
398
404
  * bot.ts stores them as MessageSid = "xiaoyi_taskId_deviceType".
399
405
  * The "xiaoyi_" prefix ensures extraction only happens for messages
400
406
  * routed through xiaoyi-channel, not other channels sharing the provider.
407
+ *
408
+ * This is the most reliable source during steer scenarios: ALS still holds
409
+ * the first message's taskId, but the text extraction sees the latest
410
+ * injected user message which carries the updated taskId.
401
411
  */
402
412
  function extractA2AFromConversationInfo(text) {
403
413
  const match = text.match(/Conversation info \(untrusted metadata\):\n```json\n([\s\S]*?)\n```/);
@@ -424,7 +434,12 @@ export const xiaoyiProvider = {
424
434
  * xiaoyiprovider as long as the provider has a configured baseUrl.
425
435
  */
426
436
  resolveDynamicModel: (ctx) => {
427
- const baseUrl = ctx.providerConfig?.baseUrl;
437
+ // providerConfig from models.providers.xiaoyiprovider is preferred;
438
+ // fall back to zai baseUrl when config-driven provider setup is unavailable.
439
+ let baseUrl = ctx.providerConfig?.baseUrl;
440
+ if (!baseUrl || typeof baseUrl !== "string") {
441
+ baseUrl = ctx.config?.models?.providers?.zai?.baseUrl;
442
+ }
428
443
  if (!baseUrl || typeof baseUrl !== "string")
429
444
  return null;
430
445
  return {
@@ -433,7 +448,7 @@ export const xiaoyiProvider = {
433
448
  api: ctx.providerConfig?.api ?? "openai-completions",
434
449
  provider: "xiaoyiprovider",
435
450
  baseUrl,
436
- reasoning: false,
451
+ reasoning: true,
437
452
  input: ["text"],
438
453
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
439
454
  contextWindow: 256_000,
@@ -477,6 +492,8 @@ export const xiaoyiProvider = {
477
492
  // ── Extract A2A taskId/deviceType from Conversation info ──
478
493
  // bot.ts stores taskId_deviceType as MessageSid, which the framework
479
494
  // renders as message_id in the Conversation info JSON block.
495
+ // This is the priority source: it reflects the latest user message
496
+ // even during steer when ALS still holds the first message's taskId.
480
497
  let extractedTaskId = null;
481
498
  let extractedDeviceType = null;
482
499
  if (context.messages) {
@@ -502,8 +519,9 @@ export const xiaoyiProvider = {
502
519
  // ── Build dynamic headers ────────────────────────────
503
520
  // Priority:
504
521
  // 1. Cron-triggered: uid → cronUuid, with cron-specific headers
505
- // 2. Xiaoyi A2A: taskId extracted from Conversation info (xiaoyi_ prefix)
506
- // 3. UID-based fallback: sha256(uid).hex[:32]_timestamp
522
+ // 2. Xiaoyi A2A: taskId from Conversation info text (latest msg, steer-safe)
523
+ // 3. ALS fallback: rawTaskId from AsyncLocalStorage
524
+ // 4. UID-based fallback: sha256(uid).hex[:32]_timestamp
507
525
  const isCron = isCronTriggered(context.messages);
508
526
  if (isCron) {
509
527
  // fire 期 jobId 桥:把首条消息 `[cron:<jobId> ...]` 解析出的真实 jobId
@@ -533,6 +551,7 @@ export const xiaoyiProvider = {
533
551
  dynamicHeaders["x-cron-title"] = encodeURIComponent(cronTitle);
534
552
  if (context.messages?.length === 1)
535
553
  dynamicHeaders["x-cron-flag"] = "begin";
554
+ logger.log(`[ALS-PROOF] provider headers source=cron`);
536
555
  }
537
556
  else if (extractedTaskId) {
538
557
  const sessionId = extractedTaskId.split("&")[0];
@@ -540,14 +559,31 @@ export const xiaoyiProvider = {
540
559
  dynamicHeaders[HEADER_TRACE_ID] = extractedTaskId;
541
560
  dynamicHeaders[HEADER_SESSION_ID] = sessionId;
542
561
  dynamicHeaders[HEADER_INTERACTION_ID] = interactionId;
562
+ logger.log(`[ALS-PROOF] provider headers source=text-extract traceId=${extractedTaskId} sessionId=${sessionId} interactionId=${interactionId}`);
543
563
  }
544
564
  else {
545
- const fallbackPrefix = ctx.extraParams?.[FALLBACK_PREFIX_KEY];
546
- if (typeof fallbackPrefix === "string") {
547
- const fallbackValue = `${fallbackPrefix}_${Date.now()}`;
548
- dynamicHeaders[HEADER_TRACE_ID] = fallbackValue;
549
- dynamicHeaders[HEADER_SESSION_ID] = fallbackValue;
550
- dynamicHeaders[HEADER_INTERACTION_ID] = fallbackValue;
565
+ // ALS fallback: rawTaskId from the per-turn AsyncLocalStorage scope.
566
+ // Text extraction (above) is preferred because during steer the ALS
567
+ // scope may still hold the first message's taskId.
568
+ const als = getCurrentSessionContext();
569
+ const rawTaskId = als?.taskId;
570
+ if (rawTaskId) {
571
+ const sessionId = rawTaskId.split("&")[0];
572
+ const interactionId = rawTaskId.split("&")[1] ?? "";
573
+ dynamicHeaders[HEADER_TRACE_ID] = rawTaskId;
574
+ dynamicHeaders[HEADER_SESSION_ID] = sessionId;
575
+ dynamicHeaders[HEADER_INTERACTION_ID] = interactionId;
576
+ logger.log(`[ALS-PROOF] provider headers source=ALS traceId=${rawTaskId} sessionId=${sessionId} interactionId=${interactionId}`);
577
+ }
578
+ else {
579
+ const fallbackPrefix = ctx.extraParams?.[FALLBACK_PREFIX_KEY];
580
+ if (typeof fallbackPrefix === "string") {
581
+ const fallbackValue = `${fallbackPrefix}_${Date.now()}`;
582
+ dynamicHeaders[HEADER_TRACE_ID] = fallbackValue;
583
+ dynamicHeaders[HEADER_SESSION_ID] = fallbackValue;
584
+ dynamicHeaders[HEADER_INTERACTION_ID] = fallbackValue;
585
+ }
586
+ logger.log(`[ALS-PROOF] provider headers source=uid-fallback (ALS miss)`);
551
587
  }
552
588
  }
553
589
  // 记录输入
@@ -560,8 +596,7 @@ export const xiaoyiProvider = {
560
596
  if (context.systemPrompt) {
561
597
  logger.log(`[xiaoyiprovider] system prompt length: ${context.systemPrompt.length}`);
562
598
  }
563
- // deviceType: prefer value extracted from Conversation info,
564
- // then ALS fallback.
599
+ // deviceType: prefer text-extracted value, ALS as fallback.
565
600
  const deviceType = extractedDeviceType
566
601
  ?? getCurrentSessionContext()?.deviceType;
567
602
  // app_ver and sdk_api_version from session context (ALS)
@@ -618,7 +653,7 @@ export const xiaoyiProvider = {
618
653
  deviceSection += `当前用户小艺APP版本是${appVer}\n`;
619
654
  }
620
655
  if (sdkApiVersion) {
621
- deviceSection += `当前用户系统软件API版本是${sdkApiVersion}\n`;
656
+ deviceSection += `当前用户系统Rom版本是${sdkApiVersion}\n`;
622
657
  }
623
658
  deviceSection += `You need to be aware of the user's current device and provide guidance accordingly. If the response involves device-related tools or actions, you must tailor the reply based on the user's current device, using device-specific references such as "saved to the Notes/Calendar on your {deviceType}.\n"`;
624
659
  context.systemPrompt = (context.systemPrompt ?? "") + deviceSection;
@@ -9,6 +9,9 @@ export interface CreateXYReplyDispatcherParams {
9
9
  steerState: {
10
10
  steered: boolean;
11
11
  };
12
+ /** Called at end of onIdle, after final frame is sent. openclaw's waitForIdle() does
13
+ * not await the async onIdle, so cleanup must happen inside onIdle itself. */
14
+ onIdleComplete?: () => void | Promise<void>;
12
15
  }
13
16
  /**
14
17
  * 清理 /tmp/xy_channel 目录中超过 24 小时的旧文件