@ynhcj/xiaoyi-channel 0.0.166-next → 0.0.167-next
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/index.js +3 -3
- package/dist/src/bot.js +7 -27
- package/dist/src/channel.js +9 -9
- package/dist/src/cspl/sentinel_hook.js +2 -2
- package/dist/src/formatter.js +12 -23
- package/dist/src/monitor.js +2 -9
- package/dist/src/provider.js +33 -62
- package/dist/src/reply-dispatcher.js +30 -53
- package/dist/src/task-manager.d.ts +0 -12
- package/dist/src/task-manager.js +0 -21
- package/dist/src/tools/agent-as-skill-tool.js +1 -3
- package/dist/src/tools/calendar-tool.js +1 -3
- package/dist/src/tools/call-device-tool.js +2 -5
- package/dist/src/tools/call-phone-tool.js +1 -3
- package/dist/src/tools/check-plugin-privilege-tool.js +1 -3
- package/dist/src/tools/create-alarm-tool.js +1 -3
- package/dist/src/tools/delete-alarm-tool.js +1 -3
- package/dist/src/tools/discover-cross-devices-tool.js +4 -7
- package/dist/src/tools/display-a2ui-card-tool.js +2 -5
- package/dist/src/tools/location-tool.js +1 -3
- package/dist/src/tools/login-token-tool.js +3 -6
- package/dist/src/tools/modify-alarm-tool.js +1 -3
- package/dist/src/tools/modify-note-tool.js +1 -3
- package/dist/src/tools/note-tool.js +1 -3
- package/dist/src/tools/query-app-message-tool.js +1 -3
- package/dist/src/tools/query-memory-data-tool.js +1 -3
- package/dist/src/tools/query-todo-task-tool.js +1 -3
- package/dist/src/tools/save-file-to-phone-tool.js +1 -3
- package/dist/src/tools/save-media-to-gallery-tool.js +1 -3
- package/dist/src/tools/search-alarm-tool.js +1 -3
- package/dist/src/tools/search-calendar-tool.js +1 -3
- package/dist/src/tools/search-contact-tool.js +1 -3
- package/dist/src/tools/search-email-tool.js +1 -3
- package/dist/src/tools/search-file-tool.js +1 -3
- package/dist/src/tools/search-message-tool.js +1 -3
- package/dist/src/tools/search-note-tool.js +1 -3
- package/dist/src/tools/search-photo-gallery-tool.js +1 -3
- package/dist/src/tools/send-cross-device-task-tool.js +9 -14
- package/dist/src/tools/send-email-tool.js +1 -3
- package/dist/src/tools/send-file-to-user-tool.js +3 -6
- package/dist/src/tools/send-html-card-tool.js +1 -3
- package/dist/src/tools/send-message-tool.js +1 -3
- package/dist/src/tools/session-manager.d.ts +7 -50
- package/dist/src/tools/session-manager.js +34 -233
- package/dist/src/tools/upload-file-tool.js +1 -3
- package/dist/src/tools/upload-photo-tool.js +1 -3
- package/dist/src/tools/xiaoyi-add-collection-tool.js +1 -3
- package/dist/src/tools/xiaoyi-collection-tool.js +1 -3
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +1 -3
- package/dist/src/tools/xiaoyi-gui-tool.js +2 -4
- package/dist/src/utils/logger.js +3 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { xiaoyiProvider } from "./src/provider.js";
|
|
|
3
3
|
import { xyPlugin } from "./src/channel.js";
|
|
4
4
|
import registerSentinelHook from "./src/cspl/sentinel_hook.js";
|
|
5
5
|
import { setXYRuntime } from "./src/runtime.js";
|
|
6
|
-
import { markCronToolCall, clearCronToolCall,
|
|
6
|
+
import { markCronToolCall, clearCronToolCall, getCurrentSessionContext } from "./src/tools/session-manager.js";
|
|
7
7
|
import { configManager } from "./src/utils/config-manager.js";
|
|
8
8
|
import { setJobPushId } from "./src/utils/cron-push-map.js";
|
|
9
9
|
import { getAllPushIds } from "./src/utils/pushid-manager.js";
|
|
@@ -54,10 +54,10 @@ async function captureCronAddMapping(event, ctx) {
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
console.log(`[CRONMAP] extracted jobId=${jobId}`);
|
|
57
|
-
const sessionCtx =
|
|
57
|
+
const sessionCtx = getCurrentSessionContext();
|
|
58
58
|
const sessionId = sessionCtx?.sessionId;
|
|
59
59
|
if (!sessionId) {
|
|
60
|
-
console.log(`[CRONMAP] skip: no sessionId (
|
|
60
|
+
console.log(`[CRONMAP] skip: no sessionId in ALS scope (ctxFound=${!!sessionCtx})`);
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
const pushId = await resolvePushId(sessionId);
|
package/dist/src/bot.js
CHANGED
|
@@ -6,7 +6,7 @@ import { downloadFilesFromParts } from "./file-download.js";
|
|
|
6
6
|
import { resolveXYConfig } from "./config.js";
|
|
7
7
|
import { sendStatusUpdate, sendClearContextResponse, sendTasksCancelResponse, sendA2AResponse } from "./formatter.js";
|
|
8
8
|
import { appendSelfEvolutionKeywordNudge, shouldNudgeForSelfEvolutionKeyword, } from "./self-evolution-keyword.js";
|
|
9
|
-
import {
|
|
9
|
+
import { runWithSessionContext } from "./tools/session-manager.js";
|
|
10
10
|
import { configManager } from "./utils/config-manager.js";
|
|
11
11
|
import { addPushId } from "./utils/pushid-manager.js";
|
|
12
12
|
import { getPushDataById } from "./utils/pushdata-manager.js";
|
|
@@ -160,19 +160,10 @@ export async function handleXYMessage(params) {
|
|
|
160
160
|
},
|
|
161
161
|
});
|
|
162
162
|
log.log(`[BOT] Resolved route, sessionKey=${route.sessionKey}`);
|
|
163
|
-
//
|
|
163
|
+
// ALS only: no registerSession. The sessionContext built below is handed
|
|
164
|
+
// to runWithSessionContext() inside withReplyDispatcher.run, which is the
|
|
165
|
+
// single wrap point for the whole agent turn.
|
|
164
166
|
if (!skipReg) {
|
|
165
|
-
registerSession(route.sessionKey, {
|
|
166
|
-
config,
|
|
167
|
-
sessionId: parsed.sessionId,
|
|
168
|
-
distributionSessionId,
|
|
169
|
-
taskId: parsed.taskId,
|
|
170
|
-
messageId: parsed.messageId,
|
|
171
|
-
agentId: route.accountId,
|
|
172
|
-
deviceType,
|
|
173
|
-
modelName,
|
|
174
|
-
runCrossTaskContext: runCrossTaskContext ?? undefined,
|
|
175
|
-
});
|
|
176
167
|
// 🔑 Sync A2A modelName to OpenClaw session store so that session_status
|
|
177
168
|
// reports the correct model. Without this, session_status returns the
|
|
178
169
|
// configured default model instead of the A2A-specified one.
|
|
@@ -380,7 +371,6 @@ export async function handleXYMessage(params) {
|
|
|
380
371
|
}
|
|
381
372
|
streamingSignals.delete(parsed.sessionId);
|
|
382
373
|
decrementTaskIdRef(parsed.sessionId);
|
|
383
|
-
unregisterSession(route.sessionKey);
|
|
384
374
|
log.log(`[BOT] Cleanup completed`);
|
|
385
375
|
},
|
|
386
376
|
run: () => {
|
|
@@ -390,6 +380,7 @@ export async function handleXYMessage(params) {
|
|
|
390
380
|
// signal init complete to release the global dispatch gate
|
|
391
381
|
// for the next session.
|
|
392
382
|
const dispatchPromise = runWithSessionContext(sessionContext, async () => {
|
|
383
|
+
log.log(`[ALS-PROOF] bot entered dispatch scope sessionId=${sessionContext.sessionId} taskId=${sessionContext.taskId} isSteer=false`);
|
|
393
384
|
log.log(`[BOT-DISPATCH] dispatchReplyFromConfig starting, body.length=${ctxPayload.Body?.length ?? 0}`);
|
|
394
385
|
try {
|
|
395
386
|
const result = await core.channel.reply.dispatchReplyFromConfig({
|
|
@@ -422,7 +413,7 @@ export async function handleXYMessage(params) {
|
|
|
422
413
|
errLog.error("Failed to handle XY message:", err);
|
|
423
414
|
runtime.error?.(`xy: Failed to handle message: ${String(err)}`);
|
|
424
415
|
errLog.log(`[BOT] Error occurred, attempting cleanup`);
|
|
425
|
-
// 🔑 错误时也要清理taskId
|
|
416
|
+
// 🔑 错误时也要清理taskId(session 走 ALS,作用域退出自动清理)
|
|
426
417
|
try {
|
|
427
418
|
const params = message.params;
|
|
428
419
|
const sessionId = params?.sessionId;
|
|
@@ -430,18 +421,6 @@ export async function handleXYMessage(params) {
|
|
|
430
421
|
errLog.log(`[BOT] Cleaning up after error`);
|
|
431
422
|
// 清理 taskId
|
|
432
423
|
decrementTaskIdRef(sessionId);
|
|
433
|
-
// 清理 session
|
|
434
|
-
const core = getXYRuntime();
|
|
435
|
-
const route = core.channel.routing.resolveAgentRoute({
|
|
436
|
-
cfg,
|
|
437
|
-
channel: "xiaoyi-channel",
|
|
438
|
-
accountId,
|
|
439
|
-
peer: {
|
|
440
|
-
kind: "direct",
|
|
441
|
-
id: sessionId,
|
|
442
|
-
},
|
|
443
|
-
});
|
|
444
|
-
unregisterSession(route.sessionKey);
|
|
445
424
|
errLog.log(`[BOT] Cleanup completed after error`);
|
|
446
425
|
}
|
|
447
426
|
}
|
|
@@ -626,6 +605,7 @@ async function dispatchSteerWhenReady(params) {
|
|
|
626
605
|
},
|
|
627
606
|
run: () => {
|
|
628
607
|
return runWithSessionContext(sessionContext, async () => {
|
|
608
|
+
log.log(`[ALS-PROOF] bot entered steer dispatch scope sessionId=${sessionContext.sessionId} taskId=${sessionContext.taskId} isSteer=true`);
|
|
629
609
|
const result = await core.channel.reply.dispatchReplyFromConfig({
|
|
630
610
|
ctx: ctxPayload,
|
|
631
611
|
cfg: params.cfg,
|
package/dist/src/channel.js
CHANGED
|
@@ -2,7 +2,7 @@ import { resolveXYConfig, listXYAccountIds, getDefaultXYAccountId } from "./conf
|
|
|
2
2
|
import { xyConfigSchema } from "./config-schema.js";
|
|
3
3
|
import { xyOutbound } from "./outbound.js";
|
|
4
4
|
import { filterToolsByDevice } from "./tools/device-tool-map.js";
|
|
5
|
-
import { getCurrentSessionContext
|
|
5
|
+
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
6
6
|
import { createAllTools } from "./tools/create-all-tools.js";
|
|
7
7
|
import { logger } from "./utils/logger.js";
|
|
8
8
|
/**
|
|
@@ -69,12 +69,10 @@ export const xyPlugin = {
|
|
|
69
69
|
agentTools: (params) => {
|
|
70
70
|
let ctx = getCurrentSessionContext();
|
|
71
71
|
// ── Cron / non-session fallback ──────────────────────────────
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
// push channel. sendCommand() detects the "cron-" sessionId
|
|
77
|
-
// prefix and routes commands through push instead of WebSocket.
|
|
72
|
+
// cron 路径不进 ALS: openclaw 的 cron runner 同步调 agentTools({cfg})
|
|
73
|
+
// 返回工具后才在别处跑 turn, xy_channel 没有 wrap 整个 turn 的点。
|
|
74
|
+
// 这里同步构造合成 ctx 给工具闭包捕获, 工具调用走 sendCommand/push,
|
|
75
|
+
// 不依赖 getCurrentSessionContext。所以不注册任何全局状态。
|
|
78
76
|
if (!ctx && params?.cfg) {
|
|
79
77
|
try {
|
|
80
78
|
const config = resolveXYConfig(params.cfg);
|
|
@@ -87,14 +85,16 @@ export const xyPlugin = {
|
|
|
87
85
|
agentId: "default",
|
|
88
86
|
isCron: true,
|
|
89
87
|
};
|
|
90
|
-
|
|
91
|
-
registerSession(`__cron__${cronId}`, ctx);
|
|
88
|
+
logger.log(`[ALS-PROOF] agentTools ctx from ALS miss, using synthetic cron ctx sessionId=${cronId} isCron=true`);
|
|
92
89
|
logger.log(`[CRON-TOOLS] Created cron session context: ${cronId}`);
|
|
93
90
|
}
|
|
94
91
|
catch (err) {
|
|
95
92
|
logger.error("[CRON-TOOLS] Failed to create cron context:", err);
|
|
96
93
|
}
|
|
97
94
|
}
|
|
95
|
+
else {
|
|
96
|
+
logger.log(`[ALS-PROOF] agentTools ctx from ALS sessionId=${ctx?.sessionId} taskId=${ctx?.taskId} isCron=${ctx?.isCron === true}`);
|
|
97
|
+
}
|
|
98
98
|
if (!ctx) {
|
|
99
99
|
logger.log("[CREATE-ALL-TOOLS] no session context, returning empty tools list");
|
|
100
100
|
return [];
|
|
@@ -6,7 +6,7 @@ import { callApi } from './call_api.js';
|
|
|
6
6
|
import { processText, extractResultText, validateAndTruncateText, parseSecurityResult, handleExecToolInput, handleMessageToolInput, handleOtherToolInput } from './utils.js';
|
|
7
7
|
import { ALLOWED_TOOLS, MAX_TEXT_LENGTH, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, STEER_ABORT_MESSAGE, TOOL_OUTPUT_ACTION } from './constants.js';
|
|
8
8
|
import { logger } from '../utils/logger.js';
|
|
9
|
-
import {
|
|
9
|
+
import { getCurrentSessionContext } from '../tools/session-manager.js';
|
|
10
10
|
import { tryInjectSteer } from './steer-context.js';
|
|
11
11
|
// 主入口模块
|
|
12
12
|
export default function register(api) {
|
|
@@ -78,7 +78,7 @@ export default function register(api) {
|
|
|
78
78
|
logger.log(`[SENTINEL HOOK] TOOL_OUTPUT response: status=${result.status}.`);
|
|
79
79
|
if (result.status === 'REJECT') {
|
|
80
80
|
logger.warn('[SENTINEL HOOK] REJECT detected, attempting steer injection');
|
|
81
|
-
const sessionCtx =
|
|
81
|
+
const sessionCtx = getCurrentSessionContext();
|
|
82
82
|
if (sessionCtx?.sessionId && sessionCtx?.taskId) {
|
|
83
83
|
await tryInjectSteer({
|
|
84
84
|
sessionId: sessionCtx.sessionId,
|
package/dist/src/formatter.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
3
|
import { getXYWebSocketManager } from "./client.js";
|
|
4
4
|
import { logger } from "./utils/logger.js";
|
|
5
|
-
import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
|
|
6
5
|
import { redactSensitiveText, containsSensitiveInfo } from "./sensitive-redactor.js";
|
|
7
6
|
import { rewriteOutboundApprovalText } from "./approval-bridge.js";
|
|
8
7
|
import { isCronToolCall, getCurrentCronJobId } from "./tools/session-manager.js";
|
|
@@ -158,11 +157,7 @@ export async function sendReasoningTextUpdate(params) {
|
|
|
158
157
|
*/
|
|
159
158
|
export async function sendStatusUpdate(params) {
|
|
160
159
|
const { config, sessionId, taskId, messageId, text, state } = params;
|
|
161
|
-
|
|
162
|
-
// fall back to closure-captured values
|
|
163
|
-
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
164
|
-
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
165
|
-
const log = logger.withContext(sessionId, currentTaskId);
|
|
160
|
+
const log = logger.withContext(sessionId, taskId);
|
|
166
161
|
// 审批桥接和脱敏
|
|
167
162
|
const bridgedText = rewriteOutboundApprovalText(sessionId, text);
|
|
168
163
|
const redactedText = redactSensitiveText(bridgedText);
|
|
@@ -177,7 +172,7 @@ export async function sendStatusUpdate(params) {
|
|
|
177
172
|
],
|
|
178
173
|
});
|
|
179
174
|
const statusUpdate = {
|
|
180
|
-
taskId
|
|
175
|
+
taskId,
|
|
181
176
|
kind: "status-update",
|
|
182
177
|
final: false, // Status updates should not end the stream
|
|
183
178
|
status: {
|
|
@@ -188,7 +183,7 @@ export async function sendStatusUpdate(params) {
|
|
|
188
183
|
// Build JSON-RPC response
|
|
189
184
|
const jsonRpcResponse = {
|
|
190
185
|
jsonrpc: "2.0",
|
|
191
|
-
id:
|
|
186
|
+
id: messageId,
|
|
192
187
|
result: statusUpdate,
|
|
193
188
|
};
|
|
194
189
|
// Send via WebSocket
|
|
@@ -197,7 +192,7 @@ export async function sendStatusUpdate(params) {
|
|
|
197
192
|
msgType: "agent_response",
|
|
198
193
|
agentId: config.agentId,
|
|
199
194
|
sessionId,
|
|
200
|
-
taskId
|
|
195
|
+
taskId,
|
|
201
196
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
202
197
|
};
|
|
203
198
|
// Log complete response body
|
|
@@ -270,15 +265,11 @@ export async function sendCommand(params) {
|
|
|
270
265
|
return sendCommandViaPush({ config, command: commands[0], pushId });
|
|
271
266
|
}
|
|
272
267
|
// ── Normal mode: WebSocket ─────────────────────────────────────
|
|
273
|
-
|
|
274
|
-
// fall back to closure-captured values
|
|
275
|
-
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
276
|
-
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
277
|
-
const log = logger.withContext(sessionId, currentTaskId);
|
|
268
|
+
const log = logger.withContext(sessionId, taskId);
|
|
278
269
|
// Build artifact update with command as data
|
|
279
270
|
// Wrap command in commands array as per protocol requirement
|
|
280
271
|
const artifact = {
|
|
281
|
-
taskId
|
|
272
|
+
taskId,
|
|
282
273
|
kind: "artifact-update",
|
|
283
274
|
append: false,
|
|
284
275
|
lastChunk: true,
|
|
@@ -300,7 +291,7 @@ export async function sendCommand(params) {
|
|
|
300
291
|
// Build JSON-RPC response
|
|
301
292
|
const jsonRpcResponse = {
|
|
302
293
|
jsonrpc: "2.0",
|
|
303
|
-
id:
|
|
294
|
+
id: messageId,
|
|
304
295
|
result: artifact,
|
|
305
296
|
};
|
|
306
297
|
// Send via WebSocket
|
|
@@ -309,7 +300,7 @@ export async function sendCommand(params) {
|
|
|
309
300
|
msgType: "agent_response",
|
|
310
301
|
agentId: config.agentId,
|
|
311
302
|
sessionId,
|
|
312
|
-
taskId
|
|
303
|
+
taskId,
|
|
313
304
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
314
305
|
};
|
|
315
306
|
// Log complete response body
|
|
@@ -329,12 +320,10 @@ export async function sendCard(params) {
|
|
|
329
320
|
throw new Error("sendCard does not support cron mode");
|
|
330
321
|
}
|
|
331
322
|
// ── Normal mode: WebSocket ─────────────────────────────────────
|
|
332
|
-
const
|
|
333
|
-
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
334
|
-
const log = logger.withContext(sessionId, currentTaskId);
|
|
323
|
+
const log = logger.withContext(sessionId, taskId);
|
|
335
324
|
// Build artifact update with cardsInfo as data
|
|
336
325
|
const artifact = {
|
|
337
|
-
taskId
|
|
326
|
+
taskId,
|
|
338
327
|
kind: "artifact-update",
|
|
339
328
|
append: false,
|
|
340
329
|
lastChunk: true,
|
|
@@ -354,7 +343,7 @@ export async function sendCard(params) {
|
|
|
354
343
|
// Build JSON-RPC response
|
|
355
344
|
const jsonRpcResponse = {
|
|
356
345
|
jsonrpc: "2.0",
|
|
357
|
-
id:
|
|
346
|
+
id: messageId,
|
|
358
347
|
result: artifact,
|
|
359
348
|
};
|
|
360
349
|
// Send via WebSocket
|
|
@@ -363,7 +352,7 @@ export async function sendCard(params) {
|
|
|
363
352
|
msgType: "agent_response",
|
|
364
353
|
agentId: config.agentId,
|
|
365
354
|
sessionId,
|
|
366
|
-
taskId
|
|
355
|
+
taskId,
|
|
367
356
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
368
357
|
};
|
|
369
358
|
log.log(`[A2A_CARD] Sending card`);
|
package/dist/src/monitor.js
CHANGED
|
@@ -9,7 +9,6 @@ import { handleSelfEvolutionEvent, handleSelfEvolutionStateGetEvent } from "./se
|
|
|
9
9
|
import { handleLoginTokenEvent } from "./login-token-handler.js";
|
|
10
10
|
import { handleCronQueryEvent } from "./cron-query-handler.js";
|
|
11
11
|
import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
|
|
12
|
-
import { cleanupStaleSessions, getActiveSessionCount, cleanupAllSessions } from "./tools/session-manager.js";
|
|
13
12
|
import { logger } from "./utils/logger.js";
|
|
14
13
|
/**
|
|
15
14
|
* Per-session serial queue that ensures messages from the same session are processed
|
|
@@ -231,8 +230,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
231
230
|
wsManager.disconnect();
|
|
232
231
|
// ✅ Remove manager from cache to prevent reusing dirty state
|
|
233
232
|
removeXYWebSocketManager(account);
|
|
234
|
-
//
|
|
235
|
-
cleanupAllSessions();
|
|
233
|
+
// Session context is ALS-scoped now — nothing global to clean up.
|
|
236
234
|
loggedServers.clear();
|
|
237
235
|
activeMessages.clear();
|
|
238
236
|
logger.log(`[MONITOR-HANDLER] Cleanup complete, cleared active messages and sessions`);
|
|
@@ -300,12 +298,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
300
298
|
if (cleaned > 0) {
|
|
301
299
|
logger.log(`[HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
|
|
302
300
|
}
|
|
303
|
-
//
|
|
304
|
-
const cleanedSessions = cleanupStaleSessions();
|
|
305
|
-
const remainingSessions = getActiveSessionCount();
|
|
306
|
-
if (cleanedSessions > 0 || remainingSessions > 0) {
|
|
307
|
-
logger.log(`[HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
|
|
308
|
-
}
|
|
301
|
+
// Session context is ALS-scoped — no global session cleanup needed.
|
|
309
302
|
// Cleanup stale temp files (older than 24 hours)
|
|
310
303
|
void cleanupStaleTempFiles();
|
|
311
304
|
}, 6 * 60 * 60 * 1000); // 6 hours
|
package/dist/src/provider.js
CHANGED
|
@@ -393,24 +393,6 @@ function trimUserMetadata(text) {
|
|
|
393
393
|
text = text.replace(/\n*Sender \(untrusted metadata\):\n```json\n[\s\S]*?\n```\n*/, "\n");
|
|
394
394
|
return text.replace(/\n{3,}/g, "\n\n");
|
|
395
395
|
}
|
|
396
|
-
/**
|
|
397
|
-
* Extract A2A taskId and deviceType from Conversation info JSON.
|
|
398
|
-
* bot.ts stores them as MessageSid = "xiaoyi_taskId_deviceType".
|
|
399
|
-
* The "xiaoyi_" prefix ensures extraction only happens for messages
|
|
400
|
-
* routed through xiaoyi-channel, not other channels sharing the provider.
|
|
401
|
-
*/
|
|
402
|
-
function extractA2AFromConversationInfo(text) {
|
|
403
|
-
const match = text.match(/Conversation info \(untrusted metadata\):\n```json\n([\s\S]*?)\n```/);
|
|
404
|
-
if (!match)
|
|
405
|
-
return null;
|
|
406
|
-
const msgIdMatch = match[1].match(/"message_id"\s*:\s*"([^"]+)"/);
|
|
407
|
-
if (!msgIdMatch)
|
|
408
|
-
return null;
|
|
409
|
-
const parts = msgIdMatch[1].split("_");
|
|
410
|
-
if (parts.length < 3 || parts[0] !== "xiaoyi")
|
|
411
|
-
return null;
|
|
412
|
-
return { taskId: parts[1], deviceType: parts[2] };
|
|
413
|
-
}
|
|
414
396
|
export const xiaoyiProvider = {
|
|
415
397
|
id: "xiaoyiprovider",
|
|
416
398
|
label: "Xiaoyi Provider",
|
|
@@ -424,7 +406,12 @@ export const xiaoyiProvider = {
|
|
|
424
406
|
* xiaoyiprovider as long as the provider has a configured baseUrl.
|
|
425
407
|
*/
|
|
426
408
|
resolveDynamicModel: (ctx) => {
|
|
427
|
-
|
|
409
|
+
// providerConfig from models.providers.xiaoyiprovider is preferred;
|
|
410
|
+
// fall back to zai baseUrl when config-driven provider setup is unavailable.
|
|
411
|
+
let baseUrl = ctx.providerConfig?.baseUrl;
|
|
412
|
+
if (!baseUrl || typeof baseUrl !== "string") {
|
|
413
|
+
baseUrl = ctx.config?.models?.providers?.zai?.baseUrl;
|
|
414
|
+
}
|
|
428
415
|
if (!baseUrl || typeof baseUrl !== "string")
|
|
429
416
|
return null;
|
|
430
417
|
return {
|
|
@@ -474,35 +461,10 @@ export const xiaoyiProvider = {
|
|
|
474
461
|
return underlying;
|
|
475
462
|
return async (model, context, options) => {
|
|
476
463
|
const dynamicHeaders = {};
|
|
477
|
-
// ── Extract A2A taskId/deviceType from Conversation info ──
|
|
478
|
-
// bot.ts stores taskId_deviceType as MessageSid, which the framework
|
|
479
|
-
// renders as message_id in the Conversation info JSON block.
|
|
480
|
-
let extractedTaskId = null;
|
|
481
|
-
let extractedDeviceType = null;
|
|
482
|
-
if (context.messages) {
|
|
483
|
-
for (let i = context.messages.length - 1; i >= 0; i--) {
|
|
484
|
-
const msg = context.messages[i];
|
|
485
|
-
if (msg.role !== "user")
|
|
486
|
-
continue;
|
|
487
|
-
const text = typeof msg.content === "string"
|
|
488
|
-
? msg.content
|
|
489
|
-
: Array.isArray(msg.content)
|
|
490
|
-
? msg.content.find((b) => b.type === "text")?.text ?? ""
|
|
491
|
-
: "";
|
|
492
|
-
if (!text)
|
|
493
|
-
continue;
|
|
494
|
-
const extracted = extractA2AFromConversationInfo(text);
|
|
495
|
-
if (extracted) {
|
|
496
|
-
extractedTaskId = extracted.taskId;
|
|
497
|
-
extractedDeviceType = extracted.deviceType;
|
|
498
|
-
break;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
464
|
// ── Build dynamic headers ────────────────────────────
|
|
503
465
|
// Priority:
|
|
504
466
|
// 1. Cron-triggered: uid → cronUuid, with cron-specific headers
|
|
505
|
-
// 2. Xiaoyi A2A:
|
|
467
|
+
// 2. Xiaoyi A2A: rawTaskId from AsyncLocalStorage, split on "&"
|
|
506
468
|
// 3. UID-based fallback: sha256(uid).hex[:32]_timestamp
|
|
507
469
|
const isCron = isCronTriggered(context.messages);
|
|
508
470
|
if (isCron) {
|
|
@@ -533,21 +495,32 @@ export const xiaoyiProvider = {
|
|
|
533
495
|
dynamicHeaders["x-cron-title"] = encodeURIComponent(cronTitle);
|
|
534
496
|
if (context.messages?.length === 1)
|
|
535
497
|
dynamicHeaders["x-cron-flag"] = "begin";
|
|
536
|
-
|
|
537
|
-
else if (extractedTaskId) {
|
|
538
|
-
const sessionId = extractedTaskId.split("&")[0];
|
|
539
|
-
const interactionId = extractedTaskId.split("&")[1] ?? "";
|
|
540
|
-
dynamicHeaders[HEADER_TRACE_ID] = extractedTaskId;
|
|
541
|
-
dynamicHeaders[HEADER_SESSION_ID] = sessionId;
|
|
542
|
-
dynamicHeaders[HEADER_INTERACTION_ID] = interactionId;
|
|
498
|
+
logger.log(`[ALS-PROOF] provider headers source=cron`);
|
|
543
499
|
}
|
|
544
500
|
else {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
501
|
+
// ALS path: rawTaskId comes from the per-turn AsyncLocalStorage scope
|
|
502
|
+
// set by bot.ts runWithSessionContext. Real upstream params.id is
|
|
503
|
+
// `realSession&realInteraction`; tester UUIDs degenerate to the whole
|
|
504
|
+
// id as sessionId with empty interactionId.
|
|
505
|
+
const als = getCurrentSessionContext();
|
|
506
|
+
const rawTaskId = als?.taskId;
|
|
507
|
+
if (rawTaskId) {
|
|
508
|
+
const sessionId = rawTaskId.split("&")[0];
|
|
509
|
+
const interactionId = rawTaskId.split("&")[1] ?? "";
|
|
510
|
+
dynamicHeaders[HEADER_TRACE_ID] = rawTaskId;
|
|
511
|
+
dynamicHeaders[HEADER_SESSION_ID] = sessionId;
|
|
512
|
+
dynamicHeaders[HEADER_INTERACTION_ID] = interactionId;
|
|
513
|
+
logger.log(`[ALS-PROOF] provider headers source=ALS traceId=${rawTaskId} sessionId=${sessionId} interactionId=${interactionId}`);
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
const fallbackPrefix = ctx.extraParams?.[FALLBACK_PREFIX_KEY];
|
|
517
|
+
if (typeof fallbackPrefix === "string") {
|
|
518
|
+
const fallbackValue = `${fallbackPrefix}_${Date.now()}`;
|
|
519
|
+
dynamicHeaders[HEADER_TRACE_ID] = fallbackValue;
|
|
520
|
+
dynamicHeaders[HEADER_SESSION_ID] = fallbackValue;
|
|
521
|
+
dynamicHeaders[HEADER_INTERACTION_ID] = fallbackValue;
|
|
522
|
+
}
|
|
523
|
+
logger.log(`[ALS-PROOF] provider headers source=uid-fallback (ALS miss)`);
|
|
551
524
|
}
|
|
552
525
|
}
|
|
553
526
|
// 记录输入
|
|
@@ -560,10 +533,8 @@ export const xiaoyiProvider = {
|
|
|
560
533
|
if (context.systemPrompt) {
|
|
561
534
|
logger.log(`[xiaoyiprovider] system prompt length: ${context.systemPrompt.length}`);
|
|
562
535
|
}
|
|
563
|
-
// deviceType:
|
|
564
|
-
|
|
565
|
-
const deviceType = extractedDeviceType
|
|
566
|
-
?? getCurrentSessionContext()?.deviceType;
|
|
536
|
+
// deviceType: read from ALS (no more text-extraction).
|
|
537
|
+
const deviceType = getCurrentSessionContext()?.deviceType;
|
|
567
538
|
// 在发送给模型前,优化 systemPrompt 结构
|
|
568
539
|
if (context.systemPrompt) {
|
|
569
540
|
let sp = context.systemPrompt;
|