@ynhcj/xiaoyi-channel 0.0.1 â 0.0.2-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 +42 -10
- package/dist/src/channel.js +6 -1
- package/dist/src/client.d.ts +15 -0
- package/dist/src/client.js +97 -2
- package/dist/src/file-upload.js +1 -1
- package/dist/src/formatter.js +43 -6
- package/dist/src/heartbeat.d.ts +2 -1
- package/dist/src/heartbeat.js +9 -1
- package/dist/src/monitor.d.ts +5 -0
- package/dist/src/monitor.js +97 -25
- package/dist/src/onboarding.js +7 -7
- package/dist/src/outbound.js +73 -8
- package/dist/src/parser.d.ts +5 -0
- package/dist/src/parser.js +15 -0
- package/dist/src/push.d.ts +7 -1
- package/dist/src/push.js +110 -19
- package/dist/src/reply-dispatcher.js +16 -18
- package/dist/src/tools/calendar-tool.d.ts +6 -0
- package/dist/src/tools/calendar-tool.js +167 -0
- package/dist/src/tools/location-tool.js +44 -5
- package/dist/src/tools/note-tool.d.ts +5 -0
- package/dist/src/tools/note-tool.js +130 -0
- package/dist/src/tools/search-note-tool.d.ts +5 -0
- package/dist/src/tools/search-note-tool.js +130 -0
- package/dist/src/tools/session-manager.js +43 -4
- package/dist/src/types.d.ts +0 -8
- package/dist/src/utils/config-manager.d.ts +26 -0
- package/dist/src/utils/config-manager.js +56 -0
- package/dist/src/websocket.d.ts +40 -0
- package/dist/src/websocket.js +190 -9
- package/package.json +7 -2
package/dist/src/bot.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { getXYRuntime } from "./runtime.js";
|
|
2
2
|
import { createXYReplyDispatcher } from "./reply-dispatcher.js";
|
|
3
|
-
import { parseA2AMessage, extractTextFromParts, extractFileParts } from "./parser.js";
|
|
3
|
+
import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId } from "./parser.js";
|
|
4
4
|
import { downloadFilesFromParts } from "./file-download.js";
|
|
5
5
|
import { resolveXYConfig } from "./config.js";
|
|
6
6
|
import { sendStatusUpdate, sendClearContextResponse, sendTasksCancelResponse } from "./formatter.js";
|
|
7
7
|
import { registerSession, unregisterSession } from "./tools/session-manager.js";
|
|
8
|
+
import { configManager } from "./utils/config-manager.js";
|
|
8
9
|
/**
|
|
9
10
|
* Handle an incoming A2A message.
|
|
10
11
|
* This is the main entry point for message processing.
|
|
@@ -19,7 +20,8 @@ export async function handleXYMessage(params) {
|
|
|
19
20
|
try {
|
|
20
21
|
// Check for special messages BEFORE parsing (these have different param structures)
|
|
21
22
|
const messageMethod = message.method;
|
|
22
|
-
log(`[
|
|
23
|
+
log(`[BOT-ENTRY] <<<<<<< Received message with method: ${messageMethod}, id: ${message.id} >>>>>>>`);
|
|
24
|
+
log(`[BOT-ENTRY] Stack trace for debugging:`, new Error().stack?.split('\n').slice(1, 4).join('\n'));
|
|
23
25
|
// Handle clearContext messages (params only has sessionId)
|
|
24
26
|
if (messageMethod === "clearContext" || messageMethod === "clear_context") {
|
|
25
27
|
const sessionId = message.params?.sessionId;
|
|
@@ -54,6 +56,18 @@ export async function handleXYMessage(params) {
|
|
|
54
56
|
}
|
|
55
57
|
// Parse the A2A message (for regular messages)
|
|
56
58
|
const parsed = parseA2AMessage(message);
|
|
59
|
+
// Extract and update push_id if present
|
|
60
|
+
const pushId = extractPushId(parsed.parts);
|
|
61
|
+
if (pushId) {
|
|
62
|
+
log(`[BOT] đ Extracted push_id from user message`);
|
|
63
|
+
log(`[BOT] - Session ID: ${parsed.sessionId}`);
|
|
64
|
+
log(`[BOT] - Push ID preview: ${pushId.substring(0, 20)}...`);
|
|
65
|
+
log(`[BOT] - Full push_id: ${pushId}`);
|
|
66
|
+
configManager.updatePushId(parsed.sessionId, pushId);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
log(`[BOT] âšī¸ No push_id found in message, will use config default`);
|
|
70
|
+
}
|
|
57
71
|
// Resolve configuration (needed for status updates)
|
|
58
72
|
const config = resolveXYConfig(cfg);
|
|
59
73
|
// â
Resolve agent route (following feishu pattern)
|
|
@@ -61,7 +75,7 @@ export async function handleXYMessage(params) {
|
|
|
61
75
|
// Use sessionId as peer.id to ensure all messages in the same session share context
|
|
62
76
|
let route = core.channel.routing.resolveAgentRoute({
|
|
63
77
|
cfg,
|
|
64
|
-
channel: "
|
|
78
|
+
channel: "xiaoyi-channel",
|
|
65
79
|
accountId, // "default"
|
|
66
80
|
peer: {
|
|
67
81
|
kind: "direct",
|
|
@@ -70,6 +84,10 @@ export async function handleXYMessage(params) {
|
|
|
70
84
|
});
|
|
71
85
|
log(`xy: resolved route accountId=${route.accountId}, sessionKey=${route.sessionKey}`);
|
|
72
86
|
// Register session context for tools
|
|
87
|
+
log(`[BOT] đ About to register session for tools...`);
|
|
88
|
+
log(`[BOT] - sessionKey: ${route.sessionKey}`);
|
|
89
|
+
log(`[BOT] - sessionId: ${parsed.sessionId}`);
|
|
90
|
+
log(`[BOT] - taskId: ${parsed.taskId}`);
|
|
73
91
|
registerSession(route.sessionKey, {
|
|
74
92
|
config,
|
|
75
93
|
sessionId: parsed.sessionId,
|
|
@@ -77,6 +95,7 @@ export async function handleXYMessage(params) {
|
|
|
77
95
|
messageId: parsed.messageId,
|
|
78
96
|
agentId: route.accountId,
|
|
79
97
|
});
|
|
98
|
+
log(`[BOT] â
Session registered for tools`);
|
|
80
99
|
// Extract text and files from parts
|
|
81
100
|
const text = extractTextFromParts(parsed.parts);
|
|
82
101
|
const fileParts = extractFileParts(parsed.parts);
|
|
@@ -93,7 +112,7 @@ export async function handleXYMessage(params) {
|
|
|
93
112
|
messageBody = `${speaker}: ${messageBody}`;
|
|
94
113
|
// Format agent envelope (following feishu pattern)
|
|
95
114
|
const body = core.channel.reply.formatAgentEnvelope({
|
|
96
|
-
channel: "
|
|
115
|
+
channel: "xiaoyi-channel",
|
|
97
116
|
from: speaker,
|
|
98
117
|
timestamp: new Date(),
|
|
99
118
|
envelope: envelopeOptions,
|
|
@@ -113,13 +132,13 @@ export async function handleXYMessage(params) {
|
|
|
113
132
|
GroupSubject: undefined,
|
|
114
133
|
SenderName: parsed.sessionId,
|
|
115
134
|
SenderId: parsed.sessionId,
|
|
116
|
-
Provider: "
|
|
117
|
-
Surface: "
|
|
135
|
+
Provider: "xiaoyi-channel",
|
|
136
|
+
Surface: "xiaoyi-channel",
|
|
118
137
|
MessageSid: parsed.messageId,
|
|
119
138
|
Timestamp: Date.now(),
|
|
120
139
|
WasMentioned: false,
|
|
121
140
|
CommandAuthorized: true,
|
|
122
|
-
OriginatingChannel: "
|
|
141
|
+
OriginatingChannel: "xiaoyi-channel",
|
|
123
142
|
OriginatingTo: parsed.sessionId, // Original message target
|
|
124
143
|
ReplyToBody: undefined, // A2A protocol doesn't support reply/quote
|
|
125
144
|
...mediaPayload,
|
|
@@ -137,6 +156,7 @@ export async function handleXYMessage(params) {
|
|
|
137
156
|
error(`Failed to send initial status update:`, err);
|
|
138
157
|
});
|
|
139
158
|
// Create reply dispatcher (following feishu pattern)
|
|
159
|
+
log(`[BOT-DISPATCHER] đ¯ Creating reply dispatcher for session=${parsed.sessionId}, taskId=${parsed.taskId}, messageId=${parsed.messageId}`);
|
|
140
160
|
const { dispatcher, replyOptions, markDispatchIdle, startStatusInterval } = createXYReplyDispatcher({
|
|
141
161
|
cfg,
|
|
142
162
|
runtime,
|
|
@@ -145,17 +165,22 @@ export async function handleXYMessage(params) {
|
|
|
145
165
|
messageId: parsed.messageId,
|
|
146
166
|
accountId: route.accountId, // â
Use route.accountId
|
|
147
167
|
});
|
|
168
|
+
log(`[BOT-DISPATCHER] â
Reply dispatcher created successfully`);
|
|
148
169
|
// Start status update interval (will send updates every 60 seconds)
|
|
149
170
|
// Interval will be automatically stopped when onIdle/onCleanup is triggered
|
|
150
171
|
startStatusInterval();
|
|
151
172
|
log(`xy: dispatching to agent (session=${parsed.sessionId})`);
|
|
152
173
|
// Dispatch to OpenClaw core using correct API (following feishu pattern)
|
|
174
|
+
log(`[BOT] đ Starting dispatcher with session: ${route.sessionKey}`);
|
|
153
175
|
await core.channel.reply.withReplyDispatcher({
|
|
154
176
|
dispatcher,
|
|
155
177
|
onSettled: () => {
|
|
178
|
+
log(`[BOT] đ onSettled called for session: ${route.sessionKey}`);
|
|
179
|
+
log(`[BOT] - About to unregister session...`);
|
|
156
180
|
markDispatchIdle();
|
|
157
181
|
// Unregister session context when done
|
|
158
182
|
unregisterSession(route.sessionKey);
|
|
183
|
+
log(`[BOT] â
Session unregistered in onSettled`);
|
|
159
184
|
},
|
|
160
185
|
run: () => core.channel.reply.dispatchReplyFromConfig({
|
|
161
186
|
ctx: ctxPayload,
|
|
@@ -164,33 +189,40 @@ export async function handleXYMessage(params) {
|
|
|
164
189
|
replyOptions,
|
|
165
190
|
}),
|
|
166
191
|
});
|
|
192
|
+
log(`[BOT] â
Dispatcher completed for session: ${parsed.sessionId}`);
|
|
167
193
|
log(`xy: dispatch complete (session=${parsed.sessionId})`);
|
|
168
194
|
}
|
|
169
195
|
catch (err) {
|
|
196
|
+
// â
Only log error, don't re-throw to prevent gateway restart
|
|
170
197
|
error("Failed to handle XY message:", err);
|
|
171
198
|
runtime.error?.(`xy: Failed to handle message: ${String(err)}`);
|
|
199
|
+
log(`[BOT] â Error occurred, attempting cleanup...`);
|
|
172
200
|
// Try to unregister session on error (if route was established)
|
|
173
201
|
try {
|
|
174
202
|
const core = getXYRuntime();
|
|
175
203
|
const params = message.params;
|
|
176
204
|
const sessionId = params?.sessionId;
|
|
177
205
|
if (sessionId) {
|
|
206
|
+
log(`[BOT] đ§š Cleaning up session after error: ${sessionId}`);
|
|
178
207
|
const route = core.channel.routing.resolveAgentRoute({
|
|
179
208
|
cfg,
|
|
180
|
-
channel: "
|
|
209
|
+
channel: "xiaoyi-channel",
|
|
181
210
|
accountId,
|
|
182
211
|
peer: {
|
|
183
212
|
kind: "direct",
|
|
184
213
|
id: sessionId, // â
Use sessionId for cleanup consistency
|
|
185
214
|
},
|
|
186
215
|
});
|
|
216
|
+
log(`[BOT] - Unregistering session: ${route.sessionKey}`);
|
|
187
217
|
unregisterSession(route.sessionKey);
|
|
218
|
+
log(`[BOT] â
Session unregistered after error`);
|
|
188
219
|
}
|
|
189
220
|
}
|
|
190
|
-
catch {
|
|
221
|
+
catch (cleanupErr) {
|
|
222
|
+
log(`[BOT] â ī¸ Cleanup failed:`, cleanupErr);
|
|
191
223
|
// Ignore cleanup errors
|
|
192
224
|
}
|
|
193
|
-
throw
|
|
225
|
+
// â Don't re-throw: message processing error should not affect gateway stability
|
|
194
226
|
}
|
|
195
227
|
}
|
|
196
228
|
/**
|
package/dist/src/channel.js
CHANGED
|
@@ -3,6 +3,9 @@ import { xyConfigSchema } from "./config-schema.js";
|
|
|
3
3
|
import { xyOutbound } from "./outbound.js";
|
|
4
4
|
import { xyOnboardingAdapter } from "./onboarding.js";
|
|
5
5
|
import { locationTool } from "./tools/location-tool.js";
|
|
6
|
+
import { noteTool } from "./tools/note-tool.js";
|
|
7
|
+
import { searchNoteTool } from "./tools/search-note-tool.js";
|
|
8
|
+
import { calendarTool } from "./tools/calendar-tool.js";
|
|
6
9
|
/**
|
|
7
10
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
8
11
|
* Implements Xiaoyi A2A protocol with dual WebSocket connections.
|
|
@@ -20,6 +23,7 @@ export const xyPlugin = {
|
|
|
20
23
|
agentPrompt: {
|
|
21
24
|
messageToolHints: () => [
|
|
22
25
|
"- xiaoyi targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `default`",
|
|
26
|
+
"- sendMedia requires a text reply"
|
|
23
27
|
],
|
|
24
28
|
},
|
|
25
29
|
capabilities: {
|
|
@@ -41,7 +45,7 @@ export const xyPlugin = {
|
|
|
41
45
|
},
|
|
42
46
|
outbound: xyOutbound,
|
|
43
47
|
onboarding: xyOnboardingAdapter,
|
|
44
|
-
agentTools: [locationTool],
|
|
48
|
+
agentTools: [locationTool, noteTool, searchNoteTool, calendarTool],
|
|
45
49
|
messaging: {
|
|
46
50
|
normalizeTarget: (raw) => {
|
|
47
51
|
const trimmed = raw.trim();
|
|
@@ -77,6 +81,7 @@ export const xyPlugin = {
|
|
|
77
81
|
runtime: context.runtime,
|
|
78
82
|
abortSignal: context.abortSignal,
|
|
79
83
|
accountId: context.accountId,
|
|
84
|
+
setStatus: context.setStatus,
|
|
80
85
|
});
|
|
81
86
|
},
|
|
82
87
|
},
|
package/dist/src/client.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ export declare function setClientRuntime(rt: RuntimeEnv | undefined): void;
|
|
|
10
10
|
* Reuses existing managers if config matches.
|
|
11
11
|
*/
|
|
12
12
|
export declare function getXYWebSocketManager(config: XYChannelConfig): XYWebSocketManager;
|
|
13
|
+
/**
|
|
14
|
+
* Remove a specific WebSocket manager from cache.
|
|
15
|
+
* Disconnects the manager and removes it from the cache.
|
|
16
|
+
*/
|
|
17
|
+
export declare function removeXYWebSocketManager(config: XYChannelConfig): void;
|
|
13
18
|
/**
|
|
14
19
|
* Clear all cached WebSocket managers.
|
|
15
20
|
*/
|
|
@@ -18,3 +23,13 @@ export declare function clearXYWebSocketManagers(): void;
|
|
|
18
23
|
* Get the number of cached managers.
|
|
19
24
|
*/
|
|
20
25
|
export declare function getCachedManagerCount(): number;
|
|
26
|
+
/**
|
|
27
|
+
* Diagnose all cached WebSocket managers.
|
|
28
|
+
* Helps identify connection issues and orphan connections.
|
|
29
|
+
*/
|
|
30
|
+
export declare function diagnoseAllManagers(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Clean up orphan connections across all managers.
|
|
33
|
+
* Returns the number of managers that had orphan connections.
|
|
34
|
+
*/
|
|
35
|
+
export declare function cleanupOrphanConnections(): number;
|
package/dist/src/client.js
CHANGED
|
@@ -23,16 +23,34 @@ export function getXYWebSocketManager(config) {
|
|
|
23
23
|
let cached = wsManagerCache.get(cacheKey);
|
|
24
24
|
if (cached && cached.isConfigMatch(config)) {
|
|
25
25
|
const log = runtime?.log ?? console.log;
|
|
26
|
-
log(`[
|
|
26
|
+
log(`[WS-MANAGER-CACHE] â
Reusing cached WebSocket manager: ${cacheKey}, total managers: ${wsManagerCache.size}`);
|
|
27
27
|
return cached;
|
|
28
28
|
}
|
|
29
29
|
// Create new manager
|
|
30
30
|
const log = runtime?.log ?? console.log;
|
|
31
|
-
log(`Creating new WebSocket manager: ${cacheKey}`);
|
|
31
|
+
log(`[WS-MANAGER-CACHE] đ Creating new WebSocket manager: ${cacheKey}, total managers before: ${wsManagerCache.size}`);
|
|
32
32
|
cached = new XYWebSocketManager(config, runtime);
|
|
33
33
|
wsManagerCache.set(cacheKey, cached);
|
|
34
|
+
log(`[WS-MANAGER-CACHE] đ Total managers after creation: ${wsManagerCache.size}`);
|
|
34
35
|
return cached;
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Remove a specific WebSocket manager from cache.
|
|
39
|
+
* Disconnects the manager and removes it from the cache.
|
|
40
|
+
*/
|
|
41
|
+
export function removeXYWebSocketManager(config) {
|
|
42
|
+
const cacheKey = `${config.apiKey}-${config.agentId}`;
|
|
43
|
+
const manager = wsManagerCache.get(cacheKey);
|
|
44
|
+
if (manager) {
|
|
45
|
+
console.log(`đī¸ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
|
|
46
|
+
manager.disconnect();
|
|
47
|
+
wsManagerCache.delete(cacheKey);
|
|
48
|
+
console.log(`đī¸ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.log(`â ī¸ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
36
54
|
/**
|
|
37
55
|
* Clear all cached WebSocket managers.
|
|
38
56
|
*/
|
|
@@ -50,3 +68,80 @@ export function clearXYWebSocketManagers() {
|
|
|
50
68
|
export function getCachedManagerCount() {
|
|
51
69
|
return wsManagerCache.size;
|
|
52
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Diagnose all cached WebSocket managers.
|
|
73
|
+
* Helps identify connection issues and orphan connections.
|
|
74
|
+
*/
|
|
75
|
+
export function diagnoseAllManagers() {
|
|
76
|
+
console.log("========================================");
|
|
77
|
+
console.log("đ WebSocket Manager Global Diagnostics");
|
|
78
|
+
console.log("========================================");
|
|
79
|
+
console.log(`Total cached managers: ${wsManagerCache.size}`);
|
|
80
|
+
console.log("");
|
|
81
|
+
if (wsManagerCache.size === 0) {
|
|
82
|
+
console.log("âšī¸ No managers in cache");
|
|
83
|
+
console.log("========================================");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let orphanCount = 0;
|
|
87
|
+
wsManagerCache.forEach((manager, key) => {
|
|
88
|
+
const diag = manager.getConnectionDiagnostics();
|
|
89
|
+
console.log(`đ Manager: ${key}`);
|
|
90
|
+
console.log(` Shutting down: ${diag.isShuttingDown}`);
|
|
91
|
+
console.log(` Total event listeners on manager: ${diag.totalEventListeners}`);
|
|
92
|
+
// Server 1
|
|
93
|
+
console.log(` đ Server1:`);
|
|
94
|
+
console.log(` - Exists: ${diag.server1.exists}`);
|
|
95
|
+
console.log(` - ReadyState: ${diag.server1.readyState}`);
|
|
96
|
+
console.log(` - State connected/ready: ${diag.server1.stateConnected}/${diag.server1.stateReady}`);
|
|
97
|
+
console.log(` - Reconnect attempts: ${diag.server1.reconnectAttempts}`);
|
|
98
|
+
console.log(` - Listeners on WebSocket: ${diag.server1.listenerCount}`);
|
|
99
|
+
console.log(` - Heartbeat active: ${diag.server1.heartbeatActive}`);
|
|
100
|
+
console.log(` - Has reconnect timer: ${diag.server1.hasReconnectTimer}`);
|
|
101
|
+
if (diag.server1.isOrphan) {
|
|
102
|
+
console.log(` â ī¸ ORPHAN CONNECTION DETECTED!`);
|
|
103
|
+
orphanCount++;
|
|
104
|
+
}
|
|
105
|
+
// Server 2
|
|
106
|
+
console.log(` đ Server2:`);
|
|
107
|
+
console.log(` - Exists: ${diag.server2.exists}`);
|
|
108
|
+
console.log(` - ReadyState: ${diag.server2.readyState}`);
|
|
109
|
+
console.log(` - State connected/ready: ${diag.server2.stateConnected}/${diag.server2.stateReady}`);
|
|
110
|
+
console.log(` - Reconnect attempts: ${diag.server2.reconnectAttempts}`);
|
|
111
|
+
console.log(` - Listeners on WebSocket: ${diag.server2.listenerCount}`);
|
|
112
|
+
console.log(` - Heartbeat active: ${diag.server2.heartbeatActive}`);
|
|
113
|
+
console.log(` - Has reconnect timer: ${diag.server2.hasReconnectTimer}`);
|
|
114
|
+
if (diag.server2.isOrphan) {
|
|
115
|
+
console.log(` â ī¸ ORPHAN CONNECTION DETECTED!`);
|
|
116
|
+
orphanCount++;
|
|
117
|
+
}
|
|
118
|
+
console.log("");
|
|
119
|
+
});
|
|
120
|
+
if (orphanCount > 0) {
|
|
121
|
+
console.log(`â ī¸ Total orphan connections found: ${orphanCount}`);
|
|
122
|
+
console.log(`đĄ Suggestion: These connections should be cleaned up`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(`â
No orphan connections found`);
|
|
126
|
+
}
|
|
127
|
+
console.log("========================================");
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clean up orphan connections across all managers.
|
|
131
|
+
* Returns the number of managers that had orphan connections.
|
|
132
|
+
*/
|
|
133
|
+
export function cleanupOrphanConnections() {
|
|
134
|
+
let cleanedCount = 0;
|
|
135
|
+
wsManagerCache.forEach((manager, key) => {
|
|
136
|
+
const diag = manager.getConnectionDiagnostics();
|
|
137
|
+
if (diag.server1.isOrphan || diag.server2.isOrphan) {
|
|
138
|
+
console.log(`đ§š Cleaning up orphan connections in manager: ${key}`);
|
|
139
|
+
manager.disconnect();
|
|
140
|
+
cleanedCount++;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
if (cleanedCount > 0) {
|
|
144
|
+
console.log(`đ§š Cleaned up ${cleanedCount} manager(s) with orphan connections`);
|
|
145
|
+
}
|
|
146
|
+
return cleanedCount;
|
|
147
|
+
}
|
package/dist/src/file-upload.js
CHANGED
package/dist/src/formatter.js
CHANGED
|
@@ -51,8 +51,21 @@ export async function sendA2AResponse(params) {
|
|
|
51
51
|
taskId,
|
|
52
52
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
53
53
|
};
|
|
54
|
+
// đ Log complete response body
|
|
55
|
+
log(`[A2A_RESPONSE] đ¤ Sending A2A artifact-update response:`);
|
|
56
|
+
log(`[A2A_RESPONSE] - sessionId: ${sessionId}`);
|
|
57
|
+
log(`[A2A_RESPONSE] - taskId: ${taskId}`);
|
|
58
|
+
log(`[A2A_RESPONSE] - messageId: ${messageId}`);
|
|
59
|
+
log(`[A2A_RESPONSE] - append: ${append}`);
|
|
60
|
+
log(`[A2A_RESPONSE] - final: ${final}`);
|
|
61
|
+
log(`[A2A_RESPONSE] - text length: ${text?.length ?? 0}`);
|
|
62
|
+
log(`[A2A_RESPONSE] - files count: ${files?.length ?? 0}`);
|
|
63
|
+
log(`[A2A_RESPONSE] đĻ Complete outbound message:`);
|
|
64
|
+
log(JSON.stringify(outboundMessage, null, 2));
|
|
65
|
+
log(`[A2A_RESPONSE] đĻ JSON-RPC response body:`);
|
|
66
|
+
log(JSON.stringify(jsonRpcResponse, null, 2));
|
|
54
67
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
55
|
-
log(`
|
|
68
|
+
log(`[A2A_RESPONSE] â
Message sent successfully`);
|
|
56
69
|
}
|
|
57
70
|
/**
|
|
58
71
|
* Send an A2A task status update.
|
|
@@ -96,8 +109,19 @@ export async function sendStatusUpdate(params) {
|
|
|
96
109
|
taskId,
|
|
97
110
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
98
111
|
};
|
|
112
|
+
// đ Log complete response body
|
|
113
|
+
log(`[A2A_STATUS] đ¤ Sending A2A status-update:`);
|
|
114
|
+
log(`[A2A_STATUS] - sessionId: ${sessionId}`);
|
|
115
|
+
log(`[A2A_STATUS] - taskId: ${taskId}`);
|
|
116
|
+
log(`[A2A_STATUS] - messageId: ${messageId}`);
|
|
117
|
+
log(`[A2A_STATUS] - state: ${state}`);
|
|
118
|
+
log(`[A2A_STATUS] - text: "${text}"`);
|
|
119
|
+
log(`[A2A_STATUS] đĻ Complete outbound message:`);
|
|
120
|
+
log(JSON.stringify(outboundMessage, null, 2));
|
|
121
|
+
log(`[A2A_STATUS] đĻ JSON-RPC response body:`);
|
|
122
|
+
log(JSON.stringify(jsonRpcResponse, null, 2));
|
|
99
123
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
100
|
-
log(`
|
|
124
|
+
log(`[A2A_STATUS] â
Status update sent successfully`);
|
|
101
125
|
}
|
|
102
126
|
/**
|
|
103
127
|
* Send a command as an artifact update (final=false).
|
|
@@ -107,7 +131,8 @@ export async function sendCommand(params) {
|
|
|
107
131
|
const runtime = getXYRuntime();
|
|
108
132
|
const log = runtime?.log ?? console.log;
|
|
109
133
|
const error = runtime?.error ?? console.error;
|
|
110
|
-
// Build artifact update with command
|
|
134
|
+
// Build artifact update with command as data
|
|
135
|
+
// Wrap command in commands array as per protocol requirement
|
|
111
136
|
const artifact = {
|
|
112
137
|
taskId,
|
|
113
138
|
kind: "artifact-update",
|
|
@@ -118,8 +143,10 @@ export async function sendCommand(params) {
|
|
|
118
143
|
artifactId: uuidv4(),
|
|
119
144
|
parts: [
|
|
120
145
|
{
|
|
121
|
-
kind: "
|
|
122
|
-
|
|
146
|
+
kind: "data",
|
|
147
|
+
data: {
|
|
148
|
+
commands: [command],
|
|
149
|
+
},
|
|
123
150
|
},
|
|
124
151
|
],
|
|
125
152
|
},
|
|
@@ -139,8 +166,18 @@ export async function sendCommand(params) {
|
|
|
139
166
|
taskId,
|
|
140
167
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
141
168
|
};
|
|
169
|
+
// đ Log complete response body
|
|
170
|
+
log(`[A2A_COMMAND] đ¤ Sending A2A command:`);
|
|
171
|
+
log(`[A2A_COMMAND] - sessionId: ${sessionId}`);
|
|
172
|
+
log(`[A2A_COMMAND] - taskId: ${taskId}`);
|
|
173
|
+
log(`[A2A_COMMAND] - messageId: ${messageId}`);
|
|
174
|
+
log(`[A2A_COMMAND] - command: ${command.header.namespace}::${command.header.name}`);
|
|
175
|
+
log(`[A2A_COMMAND] đĻ Complete outbound message:`);
|
|
176
|
+
log(JSON.stringify(outboundMessage, null, 2));
|
|
177
|
+
log(`[A2A_COMMAND] đĻ JSON-RPC response body:`);
|
|
178
|
+
log(JSON.stringify(jsonRpcResponse, null, 2));
|
|
142
179
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
143
|
-
log(`
|
|
180
|
+
log(`[A2A_COMMAND] â
Command sent successfully`);
|
|
144
181
|
}
|
|
145
182
|
/**
|
|
146
183
|
* Send a clearContext response.
|
package/dist/src/heartbeat.d.ts
CHANGED
|
@@ -13,12 +13,13 @@ export declare class HeartbeatManager {
|
|
|
13
13
|
private config;
|
|
14
14
|
private onTimeout;
|
|
15
15
|
private serverName;
|
|
16
|
+
private onHeartbeatSuccess?;
|
|
16
17
|
private intervalTimer;
|
|
17
18
|
private timeoutTimer;
|
|
18
19
|
private lastPongTime;
|
|
19
20
|
private log;
|
|
20
21
|
private error;
|
|
21
|
-
constructor(ws: WebSocket, config: HeartbeatConfig, onTimeout: () => void, serverName?: string, logFn?: (msg: string, ...args: any[]) => void, errorFn?: (msg: string, ...args: any[]) => void);
|
|
22
|
+
constructor(ws: WebSocket, config: HeartbeatConfig, onTimeout: () => void, serverName?: string, logFn?: (msg: string, ...args: any[]) => void, errorFn?: (msg: string, ...args: any[]) => void, onHeartbeatSuccess?: () => void);
|
|
22
23
|
/**
|
|
23
24
|
* Start heartbeat monitoring.
|
|
24
25
|
*/
|
package/dist/src/heartbeat.js
CHANGED
|
@@ -9,17 +9,20 @@ export class HeartbeatManager {
|
|
|
9
9
|
config;
|
|
10
10
|
onTimeout;
|
|
11
11
|
serverName;
|
|
12
|
+
onHeartbeatSuccess;
|
|
12
13
|
intervalTimer = null;
|
|
13
14
|
timeoutTimer = null;
|
|
14
15
|
lastPongTime = 0;
|
|
15
16
|
// Logging functions following feishu pattern
|
|
16
17
|
log;
|
|
17
18
|
error;
|
|
18
|
-
constructor(ws, config, onTimeout, serverName = "unknown", logFn, errorFn
|
|
19
|
+
constructor(ws, config, onTimeout, serverName = "unknown", logFn, errorFn, onHeartbeatSuccess // â
æ°åĸīŧåŋ莺æååč°
|
|
20
|
+
) {
|
|
19
21
|
this.ws = ws;
|
|
20
22
|
this.config = config;
|
|
21
23
|
this.onTimeout = onTimeout;
|
|
22
24
|
this.serverName = serverName;
|
|
25
|
+
this.onHeartbeatSuccess = onHeartbeatSuccess;
|
|
23
26
|
this.log = logFn ?? console.log;
|
|
24
27
|
this.error = errorFn ?? console.error;
|
|
25
28
|
}
|
|
@@ -36,6 +39,8 @@ export class HeartbeatManager {
|
|
|
36
39
|
clearTimeout(this.timeoutTimer);
|
|
37
40
|
this.timeoutTimer = null;
|
|
38
41
|
}
|
|
42
|
+
// â
Report health: heartbeat successful
|
|
43
|
+
this.onHeartbeatSuccess?.();
|
|
39
44
|
});
|
|
40
45
|
// Start interval timer
|
|
41
46
|
this.intervalTimer = setInterval(() => {
|
|
@@ -67,9 +72,12 @@ export class HeartbeatManager {
|
|
|
67
72
|
}
|
|
68
73
|
try {
|
|
69
74
|
// Send application-level heartbeat message
|
|
75
|
+
console.log(`[WS-${this.serverName}-SEND] Sending heartbeat frame:`, this.config.message);
|
|
70
76
|
this.ws.send(this.config.message);
|
|
77
|
+
console.log(`[WS-${this.serverName}-SEND] Heartbeat message sent, size: ${this.config.message.length} bytes`);
|
|
71
78
|
// Send protocol-level ping
|
|
72
79
|
this.ws.ping();
|
|
80
|
+
console.log(`[WS-${this.serverName}-SEND] Protocol-level ping sent`);
|
|
73
81
|
// Setup timeout timer
|
|
74
82
|
this.timeoutTimer = setTimeout(() => {
|
|
75
83
|
this.error(`Heartbeat timeout for ${this.serverName}`);
|
package/dist/src/monitor.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export type MonitorXYOpts = {
|
|
|
4
4
|
runtime?: RuntimeEnv;
|
|
5
5
|
abortSignal?: AbortSignal;
|
|
6
6
|
accountId?: string;
|
|
7
|
+
setStatus?: (status: {
|
|
8
|
+
lastEventAt?: number;
|
|
9
|
+
lastInboundAt?: number;
|
|
10
|
+
connected?: boolean;
|
|
11
|
+
}) => void;
|
|
7
12
|
};
|
|
8
13
|
/**
|
|
9
14
|
* Monitor XY channel WebSocket connections.
|