@ynhcj/xiaoyi 2.2.4 → 2.2.6
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/channel.js +71 -197
- package/dist/config-schema.d.ts +1 -1
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.js +89 -0
- package/package.json +1 -1
package/dist/channel.js
CHANGED
|
@@ -3,57 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.xiaoyiPlugin = void 0;
|
|
4
4
|
const runtime_1 = require("./runtime");
|
|
5
5
|
const file_handler_1 = require("./file-handler");
|
|
6
|
-
// Import agent event functions - will be resolved at runtime
|
|
7
|
-
// Use dynamic require for compatibility
|
|
8
|
-
let onAgentEvent = null;
|
|
9
|
-
let registerAgentRunContext = null;
|
|
10
|
-
try {
|
|
11
|
-
// Try multiple import paths
|
|
12
|
-
let agentEvents = null;
|
|
13
|
-
// Try path 1: openclaw/dist/infra/agent-events
|
|
14
|
-
try {
|
|
15
|
-
agentEvents = require("openclaw/dist/infra/agent-events");
|
|
16
|
-
console.log("XiaoYi: [AGENT EVENT] Imported from openclaw/dist/infra/agent-events");
|
|
17
|
-
}
|
|
18
|
-
catch (e1) {
|
|
19
|
-
console.log("XiaoYi: [AGENT EVENT] Path 1 failed:", e1?.message || e1);
|
|
20
|
-
// Try path 2: ../openclaw/dist/infra/agent-events
|
|
21
|
-
try {
|
|
22
|
-
agentEvents = require("../openclaw/dist/infra/agent-events");
|
|
23
|
-
console.log("XiaoYi: [AGENT EVENT] Imported from ../openclaw/dist/infra/agent-events");
|
|
24
|
-
}
|
|
25
|
-
catch (e2) {
|
|
26
|
-
console.log("XiaoYi: [AGENT EVENT] Path 2 failed:", e2?.message || e2);
|
|
27
|
-
// Try path 3: ../../openclaw/dist/infra/agent-events
|
|
28
|
-
try {
|
|
29
|
-
agentEvents = require("../../openclaw/dist/infra/agent-events");
|
|
30
|
-
console.log("XiaoYi: [AGENT EVENT] Imported from ../../openclaw/dist/infra/agent-events");
|
|
31
|
-
}
|
|
32
|
-
catch (e3) {
|
|
33
|
-
console.error("XiaoYi: [AGENT EVENT] All import paths failed:");
|
|
34
|
-
console.error(" Path 1 (openclaw/dist/infra/agent-events):", e1?.message || e1);
|
|
35
|
-
console.error(" Path 2 (../openclaw/dist/infra/agent-events):", e2?.message || e2);
|
|
36
|
-
console.error(" Path 3 (../../openclaw/dist/infra/agent-events):", e3?.message || e3);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (agentEvents) {
|
|
41
|
-
onAgentEvent = agentEvents.onAgentEvent;
|
|
42
|
-
registerAgentRunContext = agentEvents.registerAgentRunContext;
|
|
43
|
-
if (typeof onAgentEvent === "function") {
|
|
44
|
-
console.log("XiaoYi: [AGENT EVENT] ✓ onAgentEvent function imported successfully");
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
console.error("XiaoYi: [AGENT EVENT] ✗ onAgentEvent is not a function, type:", typeof onAgentEvent);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
console.warn("XiaoYi: [AGENT EVENT] Could not import agent event module");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
console.error("XiaoYi: [AGENT EVENT] Fatal import error:", error?.message || error);
|
|
56
|
-
}
|
|
57
6
|
/**
|
|
58
7
|
* XiaoYi Channel Plugin
|
|
59
8
|
* Implements OpenClaw ChannelPlugin interface for XiaoYi A2A protocol
|
|
@@ -358,164 +307,87 @@ exports.xiaoyiPlugin = {
|
|
|
358
307
|
SenderId: senderId,
|
|
359
308
|
OriginatingChannel: "xiaoyi",
|
|
360
309
|
};
|
|
361
|
-
//
|
|
310
|
+
// Dispatch message using OpenClaw's reply dispatcher
|
|
362
311
|
try {
|
|
363
|
-
const streamingEnabled = resolvedAccount.config.enableStreaming === true;
|
|
364
|
-
const hasAgentEventSupport = typeof onAgentEvent === "function";
|
|
365
312
|
console.log("\n" + "=".repeat(60));
|
|
366
|
-
console.log(`XiaoYi: [
|
|
313
|
+
console.log(`XiaoYi: [MESSAGE] Processing user message`);
|
|
367
314
|
console.log(` Session: ${message.sessionId}`);
|
|
368
315
|
console.log(` Task ID: ${message.params.id}`);
|
|
369
316
|
console.log(` User input: ${bodyText.substring(0, 50)}${bodyText.length > 50 ? "..." : ""}`);
|
|
370
|
-
console.log(`
|
|
371
|
-
console.log(` Agent Event Support: ${hasAgentEventSupport ? "AVAILABLE" : "NOT AVAILABLE"}`);
|
|
372
|
-
console.log(` Effective Streaming: ${streamingEnabled && hasAgentEventSupport ? "ENABLED (via onAgentEvent)" : "DISABLED"}`);
|
|
317
|
+
console.log(` Images: ${images.length}`);
|
|
373
318
|
console.log("=".repeat(60) + "\n");
|
|
374
|
-
|
|
375
|
-
let accumulatedText = "";
|
|
376
|
-
let taskId = runtime.getTaskIdForSession(message.sessionId) || `task_${Date.now()}`;
|
|
377
|
-
let frameCount = 0;
|
|
378
|
-
let partialCount = 0;
|
|
319
|
+
const taskId = runtime.getTaskIdForSession(message.sessionId) || `task_${Date.now()}`;
|
|
379
320
|
const startTime = Date.now();
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
sessionId: message.sessionId,
|
|
415
|
-
messageId: `partial_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
416
|
-
timestamp: Date.now(),
|
|
417
|
-
agentId: resolvedAccount.config.agentId,
|
|
418
|
-
sender: {
|
|
419
|
-
id: resolvedAccount.config.agentId,
|
|
420
|
-
name: "OpenClaw Agent",
|
|
421
|
-
type: "agent",
|
|
422
|
-
},
|
|
423
|
-
content: {
|
|
424
|
-
type: "text",
|
|
425
|
-
text: deltaText || newText,
|
|
426
|
-
},
|
|
427
|
-
status: "processing",
|
|
428
|
-
};
|
|
429
|
-
const conn = runtime.getConnection();
|
|
430
|
-
if (conn) {
|
|
431
|
-
conn.sendResponse(partialResponse, taskId, message.sessionId, false, true)
|
|
432
|
-
.then(() => {
|
|
433
|
-
console.log(` ✓ SENT (isFinal=false, append=true)\n`);
|
|
434
|
-
})
|
|
435
|
-
.catch((err) => {
|
|
436
|
-
console.error(` ✗ Send failed: ${err}\n`);
|
|
437
|
-
});
|
|
438
|
-
}
|
|
321
|
+
let accumulatedText = "";
|
|
322
|
+
// ==================== START TIMEOUT PROTECTION ====================
|
|
323
|
+
// Start 60-second timeout timer
|
|
324
|
+
const timeoutConfig = runtime.getTimeoutConfig();
|
|
325
|
+
console.log(`[TIMEOUT] Starting ${timeoutConfig.duration}ms timeout protection for session ${message.sessionId}`);
|
|
326
|
+
runtime.setTimeoutForSession(message.sessionId, async () => {
|
|
327
|
+
// Timeout callback - send timeout message to user
|
|
328
|
+
const elapsed = Date.now() - startTime;
|
|
329
|
+
console.log("\n" + "=".repeat(60));
|
|
330
|
+
console.log(`[TIMEOUT] Timeout triggered for session ${message.sessionId}`);
|
|
331
|
+
console.log(` Elapsed: ${elapsed}ms`);
|
|
332
|
+
console.log(` Task ID: ${taskId}`);
|
|
333
|
+
console.log("=".repeat(60) + "\n");
|
|
334
|
+
const conn = runtime.getConnection();
|
|
335
|
+
if (conn) {
|
|
336
|
+
const timeoutResponse = {
|
|
337
|
+
sessionId: message.sessionId,
|
|
338
|
+
messageId: `timeout_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
339
|
+
timestamp: Date.now(),
|
|
340
|
+
agentId: resolvedAccount.config.agentId,
|
|
341
|
+
sender: {
|
|
342
|
+
id: resolvedAccount.config.agentId,
|
|
343
|
+
name: "OpenClaw Agent",
|
|
344
|
+
type: "agent",
|
|
345
|
+
},
|
|
346
|
+
content: {
|
|
347
|
+
type: "text",
|
|
348
|
+
text: timeoutConfig.message,
|
|
349
|
+
},
|
|
350
|
+
status: "success",
|
|
351
|
+
};
|
|
352
|
+
try {
|
|
353
|
+
await conn.sendResponse(timeoutResponse, taskId, message.sessionId, true, false);
|
|
354
|
+
console.log(`[TIMEOUT] Timeout message sent successfully to session ${message.sessionId}\n`);
|
|
439
355
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const phase = evt.data?.phase;
|
|
443
|
-
if (phase === "end") {
|
|
444
|
-
isCompleted = true;
|
|
445
|
-
console.log("\n" + "-".repeat(60));
|
|
446
|
-
console.log(`XiaoYi: [AGENT EVENT] Lifecycle phase: end`);
|
|
447
|
-
console.log(` Run ID: ${evt.runId}`);
|
|
448
|
-
console.log(` Elapsed: ${elapsed}ms`);
|
|
449
|
-
console.log(` Total frames: ${partialCount}`);
|
|
450
|
-
console.log(` Final length: ${accumulatedText.length} chars`);
|
|
451
|
-
console.log("-".repeat(60) + "\n");
|
|
452
|
-
// Send FINAL frame
|
|
453
|
-
const response = {
|
|
454
|
-
sessionId: message.sessionId,
|
|
455
|
-
messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
456
|
-
timestamp: Date.now(),
|
|
457
|
-
agentId: resolvedAccount.config.agentId,
|
|
458
|
-
sender: {
|
|
459
|
-
id: resolvedAccount.config.agentId,
|
|
460
|
-
name: "OpenClaw Agent",
|
|
461
|
-
type: "agent",
|
|
462
|
-
},
|
|
463
|
-
content: {
|
|
464
|
-
type: "text",
|
|
465
|
-
text: accumulatedText,
|
|
466
|
-
},
|
|
467
|
-
status: "success",
|
|
468
|
-
};
|
|
469
|
-
const conn = runtime.getConnection();
|
|
470
|
-
if (conn) {
|
|
471
|
-
conn.sendResponse(response, taskId, message.sessionId, true, false)
|
|
472
|
-
.then(() => {
|
|
473
|
-
console.log(`✓ XiaoYi: FINAL frame sent (isFinal=true, append=false)\n`);
|
|
474
|
-
})
|
|
475
|
-
.catch((err) => {
|
|
476
|
-
console.error(`✗ Final send failed: ${err}\n`);
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
// Unsubscribe from events
|
|
480
|
-
if (unsubscribeAgentEvents) {
|
|
481
|
-
unsubscribeAgentEvents();
|
|
482
|
-
unsubscribeAgentEvents = undefined;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
else if (phase === "error") {
|
|
486
|
-
isCompleted = true;
|
|
487
|
-
console.error(`XiaoYi: [AGENT EVENT] Error phase:`, evt.data?.error);
|
|
488
|
-
// Unsubscribe from events
|
|
489
|
-
if (unsubscribeAgentEvents) {
|
|
490
|
-
unsubscribeAgentEvents();
|
|
491
|
-
unsubscribeAgentEvents = undefined;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
console.error(`[TIMEOUT] Failed to send timeout message:`, error);
|
|
494
358
|
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
console.error(`[TIMEOUT] Connection not available, cannot send timeout message\n`);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
// ==================== END TIMEOUT PROTECTION ====================
|
|
501
365
|
await pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
502
366
|
ctx: msgContext,
|
|
503
367
|
cfg: config,
|
|
504
368
|
dispatcherOptions: {
|
|
505
369
|
deliver: async (payload) => {
|
|
506
|
-
// NOTE: onAgentEvent does NOT emit "assistant" stream events in production
|
|
507
|
-
// Only lifecycle and tool events are emitted
|
|
508
|
-
// So we must handle the final result here
|
|
509
370
|
const elapsed = Date.now() - startTime;
|
|
510
371
|
const completeText = payload.text || "";
|
|
511
372
|
accumulatedText = completeText;
|
|
373
|
+
// ==================== CHECK TIMEOUT ====================
|
|
374
|
+
// If timeout already sent, discard this response
|
|
375
|
+
if (runtime.isSessionTimeout(message.sessionId)) {
|
|
376
|
+
console.log("\n" + "=".repeat(60));
|
|
377
|
+
console.log(`[TIMEOUT] Response received AFTER timeout`);
|
|
378
|
+
console.log(` Session: ${message.sessionId}`);
|
|
379
|
+
console.log(` Elapsed: ${elapsed}ms`);
|
|
380
|
+
console.log(` Action: DISCARDING (timeout message already sent)`);
|
|
381
|
+
console.log("=".repeat(60) + "\n");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
512
384
|
console.log("\n" + "-".repeat(60));
|
|
513
|
-
console.log(`XiaoYi: [DELIVER]
|
|
385
|
+
console.log(`XiaoYi: [DELIVER] AI response received`);
|
|
514
386
|
console.log(` Elapsed: ${elapsed}ms`);
|
|
515
387
|
console.log(` Length: ${completeText.length} chars`);
|
|
516
|
-
console.log(`
|
|
388
|
+
console.log(` Preview: ${completeText.substring(0, 100)}...`);
|
|
517
389
|
console.log("-".repeat(60) + "\n");
|
|
518
|
-
// Send
|
|
390
|
+
// Send final response to XiaoYi
|
|
519
391
|
const response = {
|
|
520
392
|
sessionId: message.sessionId,
|
|
521
393
|
messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
@@ -535,31 +407,33 @@ exports.xiaoyiPlugin = {
|
|
|
535
407
|
const conn = runtime.getConnection();
|
|
536
408
|
if (conn) {
|
|
537
409
|
await conn.sendResponse(response, taskId, message.sessionId, true, false);
|
|
538
|
-
console.log(`✓ XiaoYi:
|
|
410
|
+
console.log(`✓ XiaoYi: Response sent successfully\n`);
|
|
539
411
|
}
|
|
412
|
+
else {
|
|
413
|
+
console.error(`✗ XiaoYi: Connection not available\n`);
|
|
414
|
+
}
|
|
415
|
+
// Clear timeout as response was sent successfully
|
|
416
|
+
runtime.markSessionCompleted(message.sessionId);
|
|
540
417
|
},
|
|
541
418
|
onIdle: async () => {
|
|
542
419
|
const elapsed = Date.now() - startTime;
|
|
543
420
|
console.log("\n" + "=".repeat(60));
|
|
544
421
|
console.log(`XiaoYi: [IDLE] Processing complete`);
|
|
545
422
|
console.log(` Total time: ${elapsed}ms`);
|
|
546
|
-
console.log(` Streaming frames: ${partialCount}`);
|
|
547
423
|
console.log(` Final length: ${accumulatedText.length} chars`);
|
|
548
424
|
console.log("=".repeat(60) + "\n");
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
console.log(`XiaoYi: [IDLE] Cleaning up event listener`);
|
|
552
|
-
unsubscribeAgentEvents();
|
|
553
|
-
unsubscribeAgentEvents = undefined;
|
|
554
|
-
}
|
|
425
|
+
// Clear timeout on completion
|
|
426
|
+
runtime.markSessionCompleted(message.sessionId);
|
|
555
427
|
},
|
|
556
428
|
},
|
|
557
|
-
replyOptions: undefined,
|
|
429
|
+
replyOptions: undefined,
|
|
558
430
|
images: images.length > 0 ? images : undefined,
|
|
559
431
|
});
|
|
560
432
|
}
|
|
561
433
|
catch (error) {
|
|
562
434
|
console.error("XiaoYi: [ERROR] Error dispatching message:", error);
|
|
435
|
+
// Clear timeout on error
|
|
436
|
+
runtime.clearSessionTimeout(message.sessionId);
|
|
563
437
|
}
|
|
564
438
|
});
|
|
565
439
|
// Setup cancel handler
|
package/dist/config-schema.d.ts
CHANGED
|
@@ -33,8 +33,8 @@ export declare const XiaoYiConfigSchema: z.ZodObject<{
|
|
|
33
33
|
agentId?: string | undefined;
|
|
34
34
|
accounts?: Record<string, unknown> | undefined;
|
|
35
35
|
}, {
|
|
36
|
-
name?: string | undefined;
|
|
37
36
|
enabled?: boolean | undefined;
|
|
37
|
+
name?: string | undefined;
|
|
38
38
|
wsUrl?: string | undefined;
|
|
39
39
|
ak?: string | undefined;
|
|
40
40
|
sk?: string | undefined;
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { XiaoYiWebSocketManager } from "./websocket";
|
|
2
2
|
import { XiaoYiChannelConfig } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Timeout configuration
|
|
5
|
+
*/
|
|
6
|
+
export interface TimeoutConfig {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
duration: number;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
3
11
|
/**
|
|
4
12
|
* Runtime state for XiaoYi channel
|
|
5
13
|
* Manages single WebSocket connection (single account mode)
|
|
@@ -10,6 +18,9 @@ export declare class XiaoYiRuntime {
|
|
|
10
18
|
private config;
|
|
11
19
|
private sessionToTaskIdMap;
|
|
12
20
|
private instanceId;
|
|
21
|
+
private sessionTimeoutMap;
|
|
22
|
+
private sessionTimeoutSent;
|
|
23
|
+
private timeoutConfig;
|
|
13
24
|
constructor();
|
|
14
25
|
getInstanceId(): string;
|
|
15
26
|
/**
|
|
@@ -28,6 +39,40 @@ export declare class XiaoYiRuntime {
|
|
|
28
39
|
* Stop connection
|
|
29
40
|
*/
|
|
30
41
|
stop(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Set timeout configuration
|
|
44
|
+
*/
|
|
45
|
+
setTimeoutConfig(config: Partial<TimeoutConfig>): void;
|
|
46
|
+
/**
|
|
47
|
+
* Get timeout configuration
|
|
48
|
+
*/
|
|
49
|
+
getTimeoutConfig(): TimeoutConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Set timeout for a session
|
|
52
|
+
* @param sessionId - Session ID
|
|
53
|
+
* @param callback - Function to call when timeout occurs
|
|
54
|
+
* @returns The timeout ID (for cancellation)
|
|
55
|
+
*/
|
|
56
|
+
setTimeoutForSession(sessionId: string, callback: () => void): NodeJS.Timeout | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Clear timeout for a session
|
|
59
|
+
* @param sessionId - Session ID
|
|
60
|
+
*/
|
|
61
|
+
clearSessionTimeout(sessionId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Check if timeout has been sent for a session
|
|
64
|
+
* @param sessionId - Session ID
|
|
65
|
+
*/
|
|
66
|
+
isSessionTimeout(sessionId: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Mark session as completed (clear timeout and timeout flag)
|
|
69
|
+
* @param sessionId - Session ID
|
|
70
|
+
*/
|
|
71
|
+
markSessionCompleted(sessionId: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Clear all timeouts
|
|
74
|
+
*/
|
|
75
|
+
clearAllTimeouts(): void;
|
|
31
76
|
/**
|
|
32
77
|
* Get WebSocket manager
|
|
33
78
|
*/
|
package/dist/runtime.js
CHANGED
|
@@ -4,6 +4,14 @@ exports.XiaoYiRuntime = void 0;
|
|
|
4
4
|
exports.getXiaoYiRuntime = getXiaoYiRuntime;
|
|
5
5
|
exports.setXiaoYiRuntime = setXiaoYiRuntime;
|
|
6
6
|
const websocket_1 = require("./websocket");
|
|
7
|
+
/**
|
|
8
|
+
* Default timeout configuration
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_TIMEOUT_CONFIG = {
|
|
11
|
+
enabled: true,
|
|
12
|
+
duration: 60000, // 60 seconds
|
|
13
|
+
message: "任务还在处理中,请稍后回来查看",
|
|
14
|
+
};
|
|
7
15
|
/**
|
|
8
16
|
* Runtime state for XiaoYi channel
|
|
9
17
|
* Manages single WebSocket connection (single account mode)
|
|
@@ -14,6 +22,10 @@ class XiaoYiRuntime {
|
|
|
14
22
|
this.pluginRuntime = null; // Store PluginRuntime from OpenClaw
|
|
15
23
|
this.config = null;
|
|
16
24
|
this.sessionToTaskIdMap = new Map(); // Map sessionId to taskId
|
|
25
|
+
// Timeout management
|
|
26
|
+
this.sessionTimeoutMap = new Map();
|
|
27
|
+
this.sessionTimeoutSent = new Set();
|
|
28
|
+
this.timeoutConfig = DEFAULT_TIMEOUT_CONFIG;
|
|
17
29
|
this.instanceId = `runtime_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
18
30
|
console.log(`XiaoYi: Created new runtime instance: ${this.instanceId}`);
|
|
19
31
|
}
|
|
@@ -73,6 +85,83 @@ class XiaoYiRuntime {
|
|
|
73
85
|
}
|
|
74
86
|
// Clear session mappings
|
|
75
87
|
this.sessionToTaskIdMap.clear();
|
|
88
|
+
// Clear all timeouts
|
|
89
|
+
this.clearAllTimeouts();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Set timeout configuration
|
|
93
|
+
*/
|
|
94
|
+
setTimeoutConfig(config) {
|
|
95
|
+
this.timeoutConfig = { ...this.timeoutConfig, ...config };
|
|
96
|
+
console.log(`XiaoYi: Timeout config updated:`, this.timeoutConfig);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get timeout configuration
|
|
100
|
+
*/
|
|
101
|
+
getTimeoutConfig() {
|
|
102
|
+
return { ...this.timeoutConfig };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Set timeout for a session
|
|
106
|
+
* @param sessionId - Session ID
|
|
107
|
+
* @param callback - Function to call when timeout occurs
|
|
108
|
+
* @returns The timeout ID (for cancellation)
|
|
109
|
+
*/
|
|
110
|
+
setTimeoutForSession(sessionId, callback) {
|
|
111
|
+
if (!this.timeoutConfig.enabled) {
|
|
112
|
+
console.log(`[TIMEOUT] Timeout disabled, skipping for session ${sessionId}`);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
// Clear existing timeout if any
|
|
116
|
+
this.clearSessionTimeout(sessionId);
|
|
117
|
+
const timeoutId = setTimeout(() => {
|
|
118
|
+
console.log(`[TIMEOUT] Timeout triggered for session ${sessionId}`);
|
|
119
|
+
this.sessionTimeoutMap.delete(sessionId);
|
|
120
|
+
this.sessionTimeoutSent.add(sessionId);
|
|
121
|
+
callback();
|
|
122
|
+
}, this.timeoutConfig.duration);
|
|
123
|
+
this.sessionTimeoutMap.set(sessionId, timeoutId);
|
|
124
|
+
console.log(`[TIMEOUT] ${this.timeoutConfig.duration}ms timeout started for session ${sessionId}`);
|
|
125
|
+
return timeoutId;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Clear timeout for a session
|
|
129
|
+
* @param sessionId - Session ID
|
|
130
|
+
*/
|
|
131
|
+
clearSessionTimeout(sessionId) {
|
|
132
|
+
const timeoutId = this.sessionTimeoutMap.get(sessionId);
|
|
133
|
+
if (timeoutId) {
|
|
134
|
+
clearTimeout(timeoutId);
|
|
135
|
+
this.sessionTimeoutMap.delete(sessionId);
|
|
136
|
+
console.log(`[TIMEOUT] Timeout cleared for session ${sessionId}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if timeout has been sent for a session
|
|
141
|
+
* @param sessionId - Session ID
|
|
142
|
+
*/
|
|
143
|
+
isSessionTimeout(sessionId) {
|
|
144
|
+
return this.sessionTimeoutSent.has(sessionId);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Mark session as completed (clear timeout and timeout flag)
|
|
148
|
+
* @param sessionId - Session ID
|
|
149
|
+
*/
|
|
150
|
+
markSessionCompleted(sessionId) {
|
|
151
|
+
this.clearSessionTimeout(sessionId);
|
|
152
|
+
this.sessionTimeoutSent.delete(sessionId);
|
|
153
|
+
console.log(`[TIMEOUT] Session ${sessionId} marked as completed`);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Clear all timeouts
|
|
157
|
+
*/
|
|
158
|
+
clearAllTimeouts() {
|
|
159
|
+
for (const [sessionId, timeoutId] of this.sessionTimeoutMap.entries()) {
|
|
160
|
+
clearTimeout(timeoutId);
|
|
161
|
+
}
|
|
162
|
+
this.sessionTimeoutMap.clear();
|
|
163
|
+
this.sessionTimeoutSent.clear();
|
|
164
|
+
console.log("[TIMEOUT] All timeouts cleared");
|
|
76
165
|
}
|
|
77
166
|
/**
|
|
78
167
|
* Get WebSocket manager
|