@ynhcj/xiaoyi-channel 0.0.170-beta → 0.0.171-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/file-upload.d.ts +5 -0
- package/dist/src/file-upload.js +101 -0
- package/dist/src/formatter.d.ts +29 -0
- package/dist/src/formatter.js +53 -0
- package/dist/src/parser.d.ts +1 -1
- package/dist/src/parser.js +2 -2
- package/dist/src/reply-dispatcher.js +16 -1
- package/dist/src/tools/check-plugin-privilege-tool.js +1 -1
- package/dist/src/tools/create-all-tools.js +2 -0
- package/dist/src/tools/send-html-card-tool.d.ts +7 -0
- package/dist/src/tools/send-html-card-tool.js +113 -0
- package/dist/src/tools/session-manager.d.ts +1 -1
- package/package.json +1 -1
|
@@ -17,6 +17,11 @@ export declare class XYFileUploadService {
|
|
|
17
17
|
* Uses completeAndQuery endpoint to get the file URL directly.
|
|
18
18
|
*/
|
|
19
19
|
uploadFileAndGetUrl(filePath: string, objectType?: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Upload a file and return a preview-able URL (needPreview=true).
|
|
22
|
+
* Same as uploadFileAndGetUrl but adds needPreview flag to get a directly viewable URL.
|
|
23
|
+
*/
|
|
24
|
+
uploadFileAndGetPreviewUrl(filePath: string, objectType?: string): Promise<string>;
|
|
20
25
|
/**
|
|
21
26
|
* Upload multiple files and return their file IDs.
|
|
22
27
|
*/
|
package/dist/src/file-upload.js
CHANGED
|
@@ -235,6 +235,107 @@ export class XYFileUploadService {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Upload a file and return a preview-able URL (needPreview=true).
|
|
240
|
+
* Same as uploadFileAndGetUrl but adds needPreview flag to get a directly viewable URL.
|
|
241
|
+
*/
|
|
242
|
+
async uploadFileAndGetPreviewUrl(filePath, objectType = "TEMPORARY_MATERIAL_DOC") {
|
|
243
|
+
let localFilePath = filePath;
|
|
244
|
+
let isTempFile = false;
|
|
245
|
+
try {
|
|
246
|
+
// Handle remote URLs by downloading first
|
|
247
|
+
if (isRemoteUrl(filePath)) {
|
|
248
|
+
localFilePath = await downloadToTempFile(filePath);
|
|
249
|
+
isTempFile = true;
|
|
250
|
+
}
|
|
251
|
+
// Read file
|
|
252
|
+
const fileBuffer = await fs.readFile(localFilePath);
|
|
253
|
+
const fileName = path.basename(localFilePath);
|
|
254
|
+
const fileSha256 = calculateSHA256(fileBuffer);
|
|
255
|
+
const fileSize = fileBuffer.length;
|
|
256
|
+
// Phase 1: Prepare
|
|
257
|
+
logger.log(`[XY File Upload] Phase 1 (preview): Prepare upload for ${fileName}`);
|
|
258
|
+
const prepareResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/prepare`, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers: {
|
|
261
|
+
"Content-Type": "application/json",
|
|
262
|
+
"x-uid": this.uid,
|
|
263
|
+
"x-api-key": this.apiKey,
|
|
264
|
+
"x-request-from": "openclaw",
|
|
265
|
+
},
|
|
266
|
+
body: JSON.stringify({
|
|
267
|
+
objectType,
|
|
268
|
+
fileName,
|
|
269
|
+
fileSha256,
|
|
270
|
+
fileSize,
|
|
271
|
+
fileOwnerInfo: {
|
|
272
|
+
uid: this.uid,
|
|
273
|
+
teamId: this.uid,
|
|
274
|
+
},
|
|
275
|
+
useEdge: false,
|
|
276
|
+
}),
|
|
277
|
+
});
|
|
278
|
+
if (!prepareResp.ok) {
|
|
279
|
+
throw new Error(`Prepare failed: HTTP ${prepareResp.status}`);
|
|
280
|
+
}
|
|
281
|
+
const prepareData = await prepareResp.json();
|
|
282
|
+
if (prepareData.code !== "0") {
|
|
283
|
+
throw new Error(`Prepare failed: ${prepareData.desc}`);
|
|
284
|
+
}
|
|
285
|
+
const { objectId, draftId, uploadInfos } = prepareData;
|
|
286
|
+
logger.log(`[XY File Upload] Prepare (preview) complete: objectId=${objectId}, draftId=${draftId}`);
|
|
287
|
+
// Phase 2: Upload
|
|
288
|
+
logger.log(`[XY File Upload] Phase 2 (preview): Upload file data`);
|
|
289
|
+
const uploadInfo = uploadInfos[0];
|
|
290
|
+
const uploadResp = await fetch(uploadInfo.url, {
|
|
291
|
+
method: uploadInfo.method,
|
|
292
|
+
headers: uploadInfo.headers,
|
|
293
|
+
body: fileBuffer,
|
|
294
|
+
});
|
|
295
|
+
if (!uploadResp.ok) {
|
|
296
|
+
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
297
|
+
}
|
|
298
|
+
logger.log(`[XY File Upload] Upload (preview) complete`);
|
|
299
|
+
// Phase 3: CompleteAndQuery with needPreview=true
|
|
300
|
+
logger.log(`[XY File Upload] Phase 3 (preview): CompleteAndQuery with needPreview=true`);
|
|
301
|
+
const completeResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/completeAndQuery`, {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: {
|
|
304
|
+
"Content-Type": "application/json",
|
|
305
|
+
"x-uid": this.uid,
|
|
306
|
+
"x-api-key": this.apiKey,
|
|
307
|
+
"x-request-from": "openclaw",
|
|
308
|
+
},
|
|
309
|
+
body: JSON.stringify({
|
|
310
|
+
objectId,
|
|
311
|
+
draftId,
|
|
312
|
+
needPreview: true,
|
|
313
|
+
}),
|
|
314
|
+
});
|
|
315
|
+
if (!completeResp.ok) {
|
|
316
|
+
throw new Error(`CompleteAndQuery (preview) failed: HTTP ${completeResp.status}`);
|
|
317
|
+
}
|
|
318
|
+
const completeData = await completeResp.json();
|
|
319
|
+
const fileUrl = completeData?.fileDetailInfo?.url || "";
|
|
320
|
+
if (!fileUrl) {
|
|
321
|
+
throw new Error("No file URL returned from completeAndQuery (preview)");
|
|
322
|
+
}
|
|
323
|
+
logger.log(`[XY File Upload] File upload with preview URL successful`);
|
|
324
|
+
return fileUrl;
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
logger.error(`[XY File Upload] File upload with preview URL failed for ${filePath}:`, error);
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
if (isTempFile) {
|
|
332
|
+
try {
|
|
333
|
+
await fs.unlink(localFilePath);
|
|
334
|
+
}
|
|
335
|
+
catch { }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
238
339
|
/**
|
|
239
340
|
* Upload multiple files and return their file IDs.
|
|
240
341
|
*/
|
package/dist/src/formatter.d.ts
CHANGED
|
@@ -82,6 +82,35 @@ export interface SendCommandParams {
|
|
|
82
82
|
* listening in the calling tool works unchanged.
|
|
83
83
|
*/
|
|
84
84
|
export declare function sendCommand(params: SendCommandParams): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Parameters for sending a card (e.g., HTML H5 card).
|
|
87
|
+
*/
|
|
88
|
+
export interface SendCardParams {
|
|
89
|
+
config: XYChannelConfig;
|
|
90
|
+
sessionId: string;
|
|
91
|
+
taskId: string;
|
|
92
|
+
messageId: string;
|
|
93
|
+
/** toolCallId from the tool's execute() — used for cron detection via hook-set Map. */
|
|
94
|
+
toolCallId?: string;
|
|
95
|
+
/** When true, the artifact-update is sent with final=true. Default: false. */
|
|
96
|
+
final?: boolean;
|
|
97
|
+
/** Array of card data objects to send. */
|
|
98
|
+
cardsInfo: CardDataObject[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Card data object for sending display cards.
|
|
102
|
+
*/
|
|
103
|
+
export interface CardDataObject {
|
|
104
|
+
cardName: string;
|
|
105
|
+
cardData: Record<string, any>;
|
|
106
|
+
displayType: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Send a card (e.g., HTML H5 card) as an artifact update (final=false).
|
|
110
|
+
*
|
|
111
|
+
* Cron-aware: same routing logic as sendCommand.
|
|
112
|
+
*/
|
|
113
|
+
export declare function sendCard(params: SendCardParams): Promise<void>;
|
|
85
114
|
/**
|
|
86
115
|
* Parameters for sending a clearContext response.
|
|
87
116
|
*/
|
package/dist/src/formatter.js
CHANGED
|
@@ -272,6 +272,59 @@ export async function sendCommand(params) {
|
|
|
272
272
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
273
273
|
log.log(`[A2A_COMMAND] Command sent successfully`);
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Send a card (e.g., HTML H5 card) as an artifact update (final=false).
|
|
277
|
+
*
|
|
278
|
+
* Cron-aware: same routing logic as sendCommand.
|
|
279
|
+
*/
|
|
280
|
+
export async function sendCard(params) {
|
|
281
|
+
const { config, sessionId, taskId, messageId, toolCallId } = params;
|
|
282
|
+
// ── Cron mode: route through push channel ──────────────────────
|
|
283
|
+
if (sessionId.startsWith("cron-") || isCronToolCall(toolCallId)) {
|
|
284
|
+
throw new Error("sendCard does not support cron mode");
|
|
285
|
+
}
|
|
286
|
+
// ── Normal mode: WebSocket ─────────────────────────────────────
|
|
287
|
+
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
288
|
+
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
289
|
+
const log = logger.withContext(sessionId, currentTaskId);
|
|
290
|
+
// Build artifact update with cardsInfo as data
|
|
291
|
+
const artifact = {
|
|
292
|
+
taskId: currentTaskId,
|
|
293
|
+
kind: "artifact-update",
|
|
294
|
+
append: false,
|
|
295
|
+
lastChunk: true,
|
|
296
|
+
final: params.final ?? false,
|
|
297
|
+
artifact: {
|
|
298
|
+
artifactId: uuidv4(),
|
|
299
|
+
parts: [
|
|
300
|
+
{
|
|
301
|
+
kind: "data",
|
|
302
|
+
data: {
|
|
303
|
+
cardsInfo: params.cardsInfo,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
// Build JSON-RPC response
|
|
310
|
+
const jsonRpcResponse = {
|
|
311
|
+
jsonrpc: "2.0",
|
|
312
|
+
id: currentMessageId,
|
|
313
|
+
result: artifact,
|
|
314
|
+
};
|
|
315
|
+
// Send via WebSocket
|
|
316
|
+
const wsManager = getXYWebSocketManager(config);
|
|
317
|
+
const outboundMessage = {
|
|
318
|
+
msgType: "agent_response",
|
|
319
|
+
agentId: config.agentId,
|
|
320
|
+
sessionId,
|
|
321
|
+
taskId: currentTaskId,
|
|
322
|
+
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
323
|
+
};
|
|
324
|
+
log.log(`[A2A_CARD] Sending card`);
|
|
325
|
+
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
326
|
+
log.log(`[A2A_CARD] Card sent successfully`);
|
|
327
|
+
}
|
|
275
328
|
/**
|
|
276
329
|
* Send a clearContext response.
|
|
277
330
|
*/
|
package/dist/src/parser.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ export declare function extractPushId(parts: A2AMessagePart[]): string | null;
|
|
|
52
52
|
export declare function extractDeviceType(parts: A2AMessagePart[]): string | null;
|
|
53
53
|
/**
|
|
54
54
|
* Extract modelName from message parts.
|
|
55
|
-
* Looks for modelName in data parts under variables.
|
|
55
|
+
* Looks for modelName in data parts under variables.clientVariables.modelName
|
|
56
56
|
* (same level as systemVariables).
|
|
57
57
|
*/
|
|
58
58
|
export declare function extractModelName(parts: A2AMessagePart[]): string | null;
|
package/dist/src/parser.js
CHANGED
|
@@ -145,13 +145,13 @@ export function extractDeviceType(parts) {
|
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
147
|
* Extract modelName from message parts.
|
|
148
|
-
* Looks for modelName in data parts under variables.
|
|
148
|
+
* Looks for modelName in data parts under variables.clientVariables.modelName
|
|
149
149
|
* (same level as systemVariables).
|
|
150
150
|
*/
|
|
151
151
|
export function extractModelName(parts) {
|
|
152
152
|
for (const part of parts) {
|
|
153
153
|
if (part.kind === "data" && part.data) {
|
|
154
|
-
const modelName = part.data.variables?.
|
|
154
|
+
const modelName = part.data.variables?.clientVariables?.modelName;
|
|
155
155
|
if (modelName && typeof modelName === "string" && modelName.trim() !== "" && modelName.toLowerCase() !== "none") {
|
|
156
156
|
return modelName;
|
|
157
157
|
}
|
|
@@ -116,6 +116,8 @@ export function createXYReplyDispatcher(params) {
|
|
|
116
116
|
let hasSentResponse = false;
|
|
117
117
|
let finalSent = false;
|
|
118
118
|
let accumulatedText = "";
|
|
119
|
+
let accumulatedReasoningHistory = "";
|
|
120
|
+
let lastReasoningText = "";
|
|
119
121
|
const initialRunCrossTaskContext = getCurrentSessionContext()?.runCrossTaskContext;
|
|
120
122
|
const getRunCrossTaskContext = () => {
|
|
121
123
|
return getCurrentSessionContext()?.runCrossTaskContext ?? initialRunCrossTaskContext;
|
|
@@ -404,13 +406,26 @@ export function createXYReplyDispatcher(params) {
|
|
|
404
406
|
}
|
|
405
407
|
try {
|
|
406
408
|
if (text.length > 0) {
|
|
409
|
+
// 🔑 检测是否是新一轮思考:当前text比上一次短,或不以上次内容开头
|
|
410
|
+
const isNewRound = lastReasoningText.length > 0 &&
|
|
411
|
+
(text.length < lastReasoningText.length || !text.startsWith(lastReasoningText));
|
|
412
|
+
if (isNewRound) {
|
|
413
|
+
// 将上一轮思考追加到历史
|
|
414
|
+
accumulatedReasoningHistory += (accumulatedReasoningHistory ? "\n\n" : "") + lastReasoningText;
|
|
415
|
+
}
|
|
416
|
+
// 更新当前轮最后一次text
|
|
417
|
+
lastReasoningText = text;
|
|
418
|
+
// 🔑 拼接历史 + 当前轮内容
|
|
419
|
+
const fullText = accumulatedReasoningHistory
|
|
420
|
+
? accumulatedReasoningHistory + "\n\n" + text
|
|
421
|
+
: text;
|
|
407
422
|
// 🔑 将模型真实的thinking/reasoning内容通过reasoningText转发
|
|
408
423
|
await sendReasoningTextUpdate({
|
|
409
424
|
config,
|
|
410
425
|
sessionId,
|
|
411
426
|
taskId: currentTaskId,
|
|
412
427
|
messageId: currentMessageId,
|
|
413
|
-
text,
|
|
428
|
+
text: fullText,
|
|
414
429
|
append: false,
|
|
415
430
|
});
|
|
416
431
|
}
|
|
@@ -107,7 +107,7 @@ export function createCheckPluginPrivilegeTool(ctx) {
|
|
|
107
107
|
executeMode: "background",
|
|
108
108
|
intentName: "CheckPlugInPrivilege",
|
|
109
109
|
intentParam: {
|
|
110
|
-
checkIntentName
|
|
110
|
+
checkIntentName,
|
|
111
111
|
permissionId,
|
|
112
112
|
},
|
|
113
113
|
needUnlock: false,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createLocationTool } from "./location-tool.js";
|
|
2
2
|
import { createXiaoyiGuiTool } from "./xiaoyi-gui-tool.js";
|
|
3
3
|
import { createSendFileToUserTool } from "./send-file-to-user-tool.js";
|
|
4
|
+
import { createSendHtmlCardTool } from "./send-html-card-tool.js";
|
|
4
5
|
import { viewPushResultTool } from "./view-push-result-tool.js";
|
|
5
6
|
import { createImageReadingTool } from "./image-reading-tool.js";
|
|
6
7
|
import { timestampToUtc8Tool } from "./timestamp-to-utc8-tool.js";
|
|
@@ -48,6 +49,7 @@ export function createAllTools(ctx) {
|
|
|
48
49
|
createGetAlarmToolSchemaTool(ctx),
|
|
49
50
|
createGetCollectionToolSchemaTool(ctx),
|
|
50
51
|
createSendFileToUserTool(ctx),
|
|
52
|
+
createSendHtmlCardTool(ctx),
|
|
51
53
|
// createGetEmailToolSchemaTool(ctx),
|
|
52
54
|
viewPushResultTool,
|
|
53
55
|
createImageReadingTool(ctx),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SessionContext } from "./session-manager.js";
|
|
2
|
+
/**
|
|
3
|
+
* XY send HTML card tool - sends HTML content as an H5 card to user's device.
|
|
4
|
+
* Prefer this tool over send_file_to_user when sending HTML files to users.
|
|
5
|
+
* Only use send_file_to_user for HTML files when the user explicitly requests the raw file.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createSendHtmlCardTool(ctx: SessionContext): any;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { XYFileUploadService } from "../file-upload.js";
|
|
2
|
+
import { sendCard } from "../formatter.js";
|
|
3
|
+
import { getCurrentTaskId } from "../task-manager.js";
|
|
4
|
+
import { logger } from "../utils/logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* XY send HTML card tool - sends HTML content as an H5 card to user's device.
|
|
7
|
+
* Prefer this tool over send_file_to_user when sending HTML files to users.
|
|
8
|
+
* Only use send_file_to_user for HTML files when the user explicitly requests the raw file.
|
|
9
|
+
*/
|
|
10
|
+
export function createSendHtmlCardTool(ctx) {
|
|
11
|
+
const { config, sessionId, taskId, messageId } = ctx;
|
|
12
|
+
return {
|
|
13
|
+
name: "send_html_card",
|
|
14
|
+
label: "Send HTML Card",
|
|
15
|
+
description: `工具能力描述:当需要把生成的html文件发送给用户时,优先使用这个工具。以H5卡片的形式展示HTML页面内容,用户可以直接在卡片中查看。如果用户要求原始文件,才使用send_file_to_user发送html文件,否则html的文件回传均使用此工具。
|
|
16
|
+
|
|
17
|
+
工具参数说明:
|
|
18
|
+
a. htmlUrl 和 htmlLocal 至少填写一个
|
|
19
|
+
b. htmlUrl 是在线链接,可以直接公网访问的HTML页面地址
|
|
20
|
+
c. htmlLocal 是本地HTML文件路径,会先上传获取预览链接再以卡片形式发送
|
|
21
|
+
|
|
22
|
+
注意事项:
|
|
23
|
+
a. 操作超时时间为2分钟(120秒),请勿重复调用此工具,如果超时或失败,最多重试一次
|
|
24
|
+
b. 最后要把最终的html的公网地址作为工具执行结果返回回去,要以markdown超链接的形式返回给用户`,
|
|
25
|
+
parameters: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
htmlUrl: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "在线HTML页面链接,可直接公网访问的URL地址",
|
|
31
|
+
},
|
|
32
|
+
htmlLocal: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "本地HTML文件路径",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
required: [],
|
|
38
|
+
},
|
|
39
|
+
async execute(toolCallId, params) {
|
|
40
|
+
// Validate at least one parameter is provided
|
|
41
|
+
if (!params.htmlUrl && !params.htmlLocal) {
|
|
42
|
+
throw new Error("htmlUrl 和 htmlLocal 至少需要填写一个");
|
|
43
|
+
}
|
|
44
|
+
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
45
|
+
// Set timeout for the entire operation (2 minutes)
|
|
46
|
+
const TOOL_TIMEOUT = 120000;
|
|
47
|
+
let timeoutHandle = null;
|
|
48
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
49
|
+
timeoutHandle = setTimeout(() => {
|
|
50
|
+
reject(new Error("操作超时(2分钟)"));
|
|
51
|
+
}, TOOL_TIMEOUT);
|
|
52
|
+
});
|
|
53
|
+
const executionPromise = (async () => {
|
|
54
|
+
let url = params.htmlUrl;
|
|
55
|
+
// If htmlLocal is provided, upload it to get a preview URL
|
|
56
|
+
if (params.htmlLocal) {
|
|
57
|
+
const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
|
|
58
|
+
logger.log(`[SEND-HTML-CARD] Uploading local HTML file: ${params.htmlLocal}`);
|
|
59
|
+
const previewUrl = await uploadService.uploadFileAndGetPreviewUrl(params.htmlLocal);
|
|
60
|
+
logger.log(`[SEND-HTML-CARD] Upload complete, preview URL obtained`);
|
|
61
|
+
url = previewUrl;
|
|
62
|
+
}
|
|
63
|
+
if (!url) {
|
|
64
|
+
throw new Error("未能获取HTML页面的URL");
|
|
65
|
+
}
|
|
66
|
+
// Build card data
|
|
67
|
+
const cardsInfo = [
|
|
68
|
+
{
|
|
69
|
+
cardName: "clawH5",
|
|
70
|
+
cardData: {
|
|
71
|
+
url,
|
|
72
|
+
},
|
|
73
|
+
displayType: "DisplayFaCard",
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
// Send card via sendCard
|
|
77
|
+
await sendCard({
|
|
78
|
+
config,
|
|
79
|
+
sessionId,
|
|
80
|
+
taskId: currentTaskId,
|
|
81
|
+
messageId,
|
|
82
|
+
toolCallId,
|
|
83
|
+
cardsInfo,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: JSON.stringify({
|
|
90
|
+
success: true,
|
|
91
|
+
url,
|
|
92
|
+
message: "HTML卡片发送成功",
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
})();
|
|
98
|
+
try {
|
|
99
|
+
const result = await Promise.race([executionPromise, timeoutPromise]);
|
|
100
|
+
if (timeoutHandle) {
|
|
101
|
+
clearTimeout(timeoutHandle);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (timeoutHandle) {
|
|
107
|
+
clearTimeout(timeoutHandle);
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -8,7 +8,7 @@ export interface SessionContext {
|
|
|
8
8
|
messageId: string;
|
|
9
9
|
agentId: string;
|
|
10
10
|
deviceType?: string;
|
|
11
|
-
/** Model name extracted from A2A user variables (variables.
|
|
11
|
+
/** Model name extracted from A2A user variables (variables.clientVariables.modelName).
|
|
12
12
|
* When set, provider.ts replaces model.id in the OpenAI request body. */
|
|
13
13
|
modelName?: string;
|
|
14
14
|
runCrossTaskContext?: RunCrossTaskContext;
|