@ynhcj/xiaoyi-channel 0.0.219-next → 0.0.221-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
CHANGED
|
@@ -5,7 +5,7 @@ import { xyPlugin } from "./src/channel.js";
|
|
|
5
5
|
import registerSentinelHook from "./src/cspl/sentinel_hook.js";
|
|
6
6
|
import registerSkillScopeHook from "./src/cspl/skill_scope_hook.js";
|
|
7
7
|
import { setXYRuntime } from "./src/runtime.js";
|
|
8
|
-
import { markCronToolCall, clearCronToolCall, getCurrentSessionContext } from "./src/tools/session-manager.js";
|
|
8
|
+
import { markCronToolCall, clearCronToolCall, getCurrentSessionContext, setCronToolRunInfo, clearCronToolRunInfo, isCronActive } from "./src/tools/session-manager.js";
|
|
9
9
|
import { configManager } from "./src/utils/config-manager.js";
|
|
10
10
|
import { setJobPushId } from "./src/utils/cron-push-map.js";
|
|
11
11
|
import { getAllPushIds } from "./src/utils/pushid-manager.js";
|
|
@@ -63,13 +63,20 @@ function registerSkillsDiagnosticHook(api) {
|
|
|
63
63
|
*/
|
|
64
64
|
function registerCronDetectionHook(api) {
|
|
65
65
|
api.on("before_tool_call", async (event, ctx) => {
|
|
66
|
-
|
|
66
|
+
// sessionKey 前缀依赖 openclaw 版本行为,不一定可靠;
|
|
67
|
+
// isCronActive() 由 provider.ts 根据消息内容 [cron:...] 设置,更可靠。
|
|
68
|
+
if ((ctx.sessionKey?.startsWith("cron:") || isCronActive()) && event.toolCallId) {
|
|
67
69
|
markCronToolCall(event.toolCallId);
|
|
70
|
+
// 存储 runId,供 call_device_tool 在 ALS 缺失时构造合成 SessionContext
|
|
71
|
+
if (event.runId) {
|
|
72
|
+
setCronToolRunInfo(event.toolCallId, event.runId);
|
|
73
|
+
}
|
|
68
74
|
}
|
|
69
75
|
});
|
|
70
76
|
api.on("after_tool_call", async (event, ctx) => {
|
|
71
77
|
if (event.toolCallId) {
|
|
72
78
|
clearCronToolCall(event.toolCallId);
|
|
79
|
+
clearCronToolRunInfo(event.toolCallId);
|
|
73
80
|
}
|
|
74
81
|
// 捕获对话创建的 cron job:agent 调 cron(add) 后,从 result 拿 jobId,
|
|
75
82
|
// 配合当前会话的 pushId,写入 jobId↔pushId 映射,供 fire 时反查设备。
|
package/dist/src/bot.js
CHANGED
|
@@ -322,8 +322,10 @@ export async function handleXYMessage(params) {
|
|
|
322
322
|
});
|
|
323
323
|
if (queued) {
|
|
324
324
|
log.log(`[BOT-STEER] Direct steer succeeded — message injected into active run`);
|
|
325
|
-
// Steer message taskId
|
|
326
|
-
|
|
325
|
+
// Steer message taskId stays in the binding so getActiveTaskId()
|
|
326
|
+
// resolves to the latest taskId for onPartialReply replies.
|
|
327
|
+
// Cleanup happens via the original dispatcher's onIdleComplete →
|
|
328
|
+
// decrementTaskIdRef(sessionId) which removes the entire binding.
|
|
327
329
|
return;
|
|
328
330
|
}
|
|
329
331
|
log.log(`[BOT-STEER] Direct steer failed (queued=false), falling through to dispatchReplyFromConfig`);
|
package/dist/src/provider.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// models.providers.xiaoyiprovider.models = [...]
|
|
10
10
|
import { createHash } from "crypto";
|
|
11
11
|
import { logger } from "./utils/logger.js";
|
|
12
|
-
import { getCurrentSessionContext, setCurrentCronJobId } from "./tools/session-manager.js";
|
|
12
|
+
import { getCurrentSessionContext, setCurrentCronJobId, markCronDetected } from "./tools/session-manager.js";
|
|
13
13
|
import { selfEvolutionManager } from "./utils/self-evolution-manager.js";
|
|
14
14
|
import { setCompactionConfig, setCompactionSessionSnapshot } from "./compaction-provider.js";
|
|
15
15
|
// ── Retry config ──────────────────────────────────────────────
|
|
@@ -560,6 +560,9 @@ export const xiaoyiProvider = {
|
|
|
560
560
|
// 4. UID-based fallback: sha256(uid).hex[:32]_timestamp
|
|
561
561
|
const isCron = isCronTriggered(context.messages);
|
|
562
562
|
if (isCron) {
|
|
563
|
+
// 通知 before_tool_call hook:当前请求是 cron 触发。
|
|
564
|
+
// 不依赖 openclaw 的 sessionKey 前缀(部分版本可能不设置)。
|
|
565
|
+
markCronDetected();
|
|
563
566
|
// fire 期 jobId 桥:把首条消息 `[cron:<jobId> ...]` 解析出的真实 jobId
|
|
564
567
|
// 绑定到本次 cron run 的合成 sessionId。sendCommand 凭同一 sessionId
|
|
565
568
|
// 反查 jobId → cron-push-map → 正确设备的 pushId(多设备路由)。
|
|
@@ -24,7 +24,7 @@ import { sendEmailTool } from "./send-email-tool.js";
|
|
|
24
24
|
import { searchEmailTool } from "./search-email-tool.js";
|
|
25
25
|
import { getCachedXYWebSocketManager } from "../client.js";
|
|
26
26
|
import { sendStatusUpdate } from "../formatter.js";
|
|
27
|
-
import { getCurrentSessionContext } from './session-manager.js';
|
|
27
|
+
import { getCurrentSessionContext, runWithSessionContext, isCronToolCall, getCronToolRunInfo } from './session-manager.js';
|
|
28
28
|
/**
|
|
29
29
|
* call_device_tool - 通用端工具调度器。
|
|
30
30
|
* LLM 必须先通过 get_xxx_tool_schema 获取具体工具 schema,再用本工具执行。
|
|
@@ -77,9 +77,26 @@ export const callDeviceTool = {
|
|
|
77
77
|
required: ["toolName", "arguments"],
|
|
78
78
|
},
|
|
79
79
|
async execute(toolCallId, params) {
|
|
80
|
-
|
|
80
|
+
let ctx = getCurrentSessionContext();
|
|
81
81
|
const wsManager = getCachedXYWebSocketManager();
|
|
82
82
|
const config = wsManager.config;
|
|
83
|
+
// Cron mode: ALS is not available (cron does not go through bot.ts →
|
|
84
|
+
// runWithSessionContext). Build a synthetic SessionContext so that
|
|
85
|
+
// sub-tools don't crash on null ALS and sendCommand() can route
|
|
86
|
+
// through sendCommandViaPush.
|
|
87
|
+
let isSyntheticCtx = false;
|
|
88
|
+
if (!ctx && isCronToolCall(toolCallId)) {
|
|
89
|
+
const runInfo = getCronToolRunInfo(toolCallId);
|
|
90
|
+
ctx = {
|
|
91
|
+
config,
|
|
92
|
+
sessionId: `cron-${(runInfo?.runId ?? toolCallId).replace(/-/g, "")}`,
|
|
93
|
+
taskId: runInfo?.runId ?? toolCallId,
|
|
94
|
+
messageId: "",
|
|
95
|
+
agentId: config.agentId,
|
|
96
|
+
isCron: true,
|
|
97
|
+
};
|
|
98
|
+
isSyntheticCtx = true;
|
|
99
|
+
}
|
|
83
100
|
const sessionId = ctx?.sessionId ?? "";
|
|
84
101
|
const taskId = ctx?.taskId ?? "";
|
|
85
102
|
const messageId = ctx?.messageId ?? "";
|
|
@@ -110,6 +127,11 @@ export const callDeviceTool = {
|
|
|
110
127
|
};
|
|
111
128
|
}
|
|
112
129
|
try {
|
|
130
|
+
// Wrap sub-tool with synthetic ALS context so getCurrentSessionContext()
|
|
131
|
+
// returns non-null inside the sub-tool's execute().
|
|
132
|
+
if (isSyntheticCtx) {
|
|
133
|
+
return await runWithSessionContext(ctx, () => tool.execute(toolCallId, toolArgs));
|
|
134
|
+
}
|
|
113
135
|
return await tool.execute(toolCallId, toolArgs);
|
|
114
136
|
}
|
|
115
137
|
catch (error) {
|
|
@@ -20,12 +20,24 @@ export interface SessionContext {
|
|
|
20
20
|
* Tools should use the push channel instead of WebSocket sendCommand. */
|
|
21
21
|
isCron?: boolean;
|
|
22
22
|
}
|
|
23
|
+
/** Mark that the current request is cron-triggered (called by provider). */
|
|
24
|
+
export declare function markCronDetected(): void;
|
|
25
|
+
/** Check whether a cron request was recently detected. */
|
|
26
|
+
export declare function isCronActive(ttlMs?: number): boolean;
|
|
23
27
|
/** Mark a toolCallId as originating from a cron trigger. */
|
|
24
28
|
export declare function markCronToolCall(toolCallId: string): void;
|
|
25
29
|
/** Check whether a toolCallId is from a cron trigger. */
|
|
26
30
|
export declare function isCronToolCall(toolCallId?: string): boolean;
|
|
27
31
|
/** Clean up a cron tool call marker after use. */
|
|
28
32
|
export declare function clearCronToolCall(toolCallId: string): void;
|
|
33
|
+
/** Store the openclaw runId for a cron-triggered tool call. */
|
|
34
|
+
export declare function setCronToolRunInfo(toolCallId: string, runId: string): void;
|
|
35
|
+
/** Get the stored runId for a cron-triggered tool call. */
|
|
36
|
+
export declare function getCronToolRunInfo(toolCallId: string): {
|
|
37
|
+
runId: string;
|
|
38
|
+
} | undefined;
|
|
39
|
+
/** Clean up cron tool run info after use. */
|
|
40
|
+
export declare function clearCronToolRunInfo(toolCallId: string): void;
|
|
29
41
|
/** 把 fire 期解析出的 jobId 绑定到当前 cron run 的合成 sessionId。 */
|
|
30
42
|
export declare function setCurrentCronJobId(cronSessionId: string, jobId: string): void;
|
|
31
43
|
/** 凭合成 cron sessionId 取本次 run 的 jobId(供 sendCommand 反查 pushId)。 */
|
|
@@ -14,6 +14,20 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
14
14
|
import { logger } from "../utils/logger.js";
|
|
15
15
|
import { getCurrentTaskId, getCurrentMessageId } from "../task-manager.js";
|
|
16
16
|
const _g = globalThis;
|
|
17
|
+
// ── Cron request detection ──────────────────────────────────────────
|
|
18
|
+
// The provider detects cron from [cron:...] in message text and sets
|
|
19
|
+
// this timestamp. The before_tool_call hook checks it as a fallback
|
|
20
|
+
// when openclaw's sessionKey does not have the "cron:" prefix.
|
|
21
|
+
// Timestamp-based so it self-expires and requires no explicit cleanup.
|
|
22
|
+
let __xyCronDetectedTs = 0;
|
|
23
|
+
/** Mark that the current request is cron-triggered (called by provider). */
|
|
24
|
+
export function markCronDetected() {
|
|
25
|
+
__xyCronDetectedTs = Date.now();
|
|
26
|
+
}
|
|
27
|
+
/** Check whether a cron request was recently detected. */
|
|
28
|
+
export function isCronActive(ttlMs = 120_000) {
|
|
29
|
+
return Date.now() - __xyCronDetectedTs < ttlMs;
|
|
30
|
+
}
|
|
17
31
|
// ── Cron tool-call tracking ─────────────────────────────────────────
|
|
18
32
|
// Global Map keyed by toolCallId to track whether a specific tool call
|
|
19
33
|
// originated from a cron/scheduled task. Populated by the
|
|
@@ -39,6 +53,29 @@ export function isCronToolCall(toolCallId) {
|
|
|
39
53
|
export function clearCronToolCall(toolCallId) {
|
|
40
54
|
cronToolCallMap.delete(toolCallId);
|
|
41
55
|
}
|
|
56
|
+
// ── Cron tool run info ─────────────────────────────────────────────
|
|
57
|
+
// When a cron-triggered tool executes, ALS is not available (cron does
|
|
58
|
+
// not go through bot.ts → runWithSessionContext). We store the runId
|
|
59
|
+
// from the before_tool_call event so call_device_tool can construct a
|
|
60
|
+
// synthetic SessionContext for sub-tool execution.
|
|
61
|
+
if (!_g.__xyCronToolRunInfo) {
|
|
62
|
+
_g.__xyCronToolRunInfo = new Map();
|
|
63
|
+
}
|
|
64
|
+
const cronToolRunInfoMap = _g.__xyCronToolRunInfo;
|
|
65
|
+
/** Store the openclaw runId for a cron-triggered tool call. */
|
|
66
|
+
export function setCronToolRunInfo(toolCallId, runId) {
|
|
67
|
+
if (toolCallId && runId) {
|
|
68
|
+
cronToolRunInfoMap.set(toolCallId, { runId });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Get the stored runId for a cron-triggered tool call. */
|
|
72
|
+
export function getCronToolRunInfo(toolCallId) {
|
|
73
|
+
return cronToolRunInfoMap.get(toolCallId);
|
|
74
|
+
}
|
|
75
|
+
/** Clean up cron tool run info after use. */
|
|
76
|
+
export function clearCronToolRunInfo(toolCallId) {
|
|
77
|
+
cronToolRunInfoMap.delete(toolCallId);
|
|
78
|
+
}
|
|
42
79
|
// ── Cron session ↔ jobId bridge ────────────────────────────────────
|
|
43
80
|
// fire 期 jobId 传递桥。provider.ts 在 isCron 分支从首条消息
|
|
44
81
|
// `[cron:<jobId> ...]` 解析出真实 jobId,写入合成 cron sessionId;
|