@ynhcj/xiaoyi-channel 0.0.218-next → 0.0.220-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 } 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";
|
|
@@ -65,11 +65,16 @@ function registerCronDetectionHook(api) {
|
|
|
65
65
|
api.on("before_tool_call", async (event, ctx) => {
|
|
66
66
|
if (ctx.sessionKey?.startsWith("cron:") && event.toolCallId) {
|
|
67
67
|
markCronToolCall(event.toolCallId);
|
|
68
|
+
// 存储 runId,供 call_device_tool 在 ALS 缺失时构造合成 SessionContext
|
|
69
|
+
if (event.runId) {
|
|
70
|
+
setCronToolRunInfo(event.toolCallId, event.runId);
|
|
71
|
+
}
|
|
68
72
|
}
|
|
69
73
|
});
|
|
70
74
|
api.on("after_tool_call", async (event, ctx) => {
|
|
71
75
|
if (event.toolCallId) {
|
|
72
76
|
clearCronToolCall(event.toolCallId);
|
|
77
|
+
clearCronToolRunInfo(event.toolCallId);
|
|
73
78
|
}
|
|
74
79
|
// 捕获对话创建的 cron job:agent 调 cron(add) 后,从 result 拿 jobId,
|
|
75
80
|
// 配合当前会话的 pushId,写入 jobId↔pushId 映射,供 fire 时反查设备。
|
package/dist/src/bot.js
CHANGED
|
@@ -323,7 +323,10 @@ export async function handleXYMessage(params) {
|
|
|
323
323
|
if (queued) {
|
|
324
324
|
log.log(`[BOT-STEER] Direct steer succeeded — message injected into active run`);
|
|
325
325
|
// Steer message taskId refCount is no longer needed since we skip the dispatcher.
|
|
326
|
-
|
|
326
|
+
// Use expectedTaskId to only remove the steer's own task — passing no
|
|
327
|
+
// expectedTaskId would wipe the entire session binding and cause the next
|
|
328
|
+
// steer to be misdetected as a new message (isUpdate=false).
|
|
329
|
+
decrementTaskIdRef(parsed.sessionId, parsed.taskId);
|
|
327
330
|
return;
|
|
328
331
|
}
|
|
329
332
|
log.log(`[BOT-STEER] Direct steer failed (queued=false), falling through to dispatchReplyFromConfig`);
|
|
@@ -142,7 +142,10 @@ export function createXYReplyDispatcher(params) {
|
|
|
142
142
|
// ── append: false streaming state ─────────────────────────────
|
|
143
143
|
// Instead of computing deltas, we always send the full text with
|
|
144
144
|
// append: false. Model-call boundaries are detected via startsWith.
|
|
145
|
-
|
|
145
|
+
// crossTurnPrefix: 仅在 steer 时继承上一轮内容,新消息从空开始。
|
|
146
|
+
let crossTurnPrefix = steerState.steered
|
|
147
|
+
? (sessionFullTextMap.get(sessionId) ?? "")
|
|
148
|
+
: "";
|
|
146
149
|
let prevModelText = ""; // completed model calls within this turn
|
|
147
150
|
let currentModelText = ""; // current model call's full accumulated text
|
|
148
151
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -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) {
|
|
@@ -26,6 +26,14 @@ export declare function markCronToolCall(toolCallId: string): void;
|
|
|
26
26
|
export declare function isCronToolCall(toolCallId?: string): boolean;
|
|
27
27
|
/** Clean up a cron tool call marker after use. */
|
|
28
28
|
export declare function clearCronToolCall(toolCallId: string): void;
|
|
29
|
+
/** Store the openclaw runId for a cron-triggered tool call. */
|
|
30
|
+
export declare function setCronToolRunInfo(toolCallId: string, runId: string): void;
|
|
31
|
+
/** Get the stored runId for a cron-triggered tool call. */
|
|
32
|
+
export declare function getCronToolRunInfo(toolCallId: string): {
|
|
33
|
+
runId: string;
|
|
34
|
+
} | undefined;
|
|
35
|
+
/** Clean up cron tool run info after use. */
|
|
36
|
+
export declare function clearCronToolRunInfo(toolCallId: string): void;
|
|
29
37
|
/** 把 fire 期解析出的 jobId 绑定到当前 cron run 的合成 sessionId。 */
|
|
30
38
|
export declare function setCurrentCronJobId(cronSessionId: string, jobId: string): void;
|
|
31
39
|
/** 凭合成 cron sessionId 取本次 run 的 jobId(供 sendCommand 反查 pushId)。 */
|
|
@@ -39,6 +39,29 @@ export function isCronToolCall(toolCallId) {
|
|
|
39
39
|
export function clearCronToolCall(toolCallId) {
|
|
40
40
|
cronToolCallMap.delete(toolCallId);
|
|
41
41
|
}
|
|
42
|
+
// ── Cron tool run info ─────────────────────────────────────────────
|
|
43
|
+
// When a cron-triggered tool executes, ALS is not available (cron does
|
|
44
|
+
// not go through bot.ts → runWithSessionContext). We store the runId
|
|
45
|
+
// from the before_tool_call event so call_device_tool can construct a
|
|
46
|
+
// synthetic SessionContext for sub-tool execution.
|
|
47
|
+
if (!_g.__xyCronToolRunInfo) {
|
|
48
|
+
_g.__xyCronToolRunInfo = new Map();
|
|
49
|
+
}
|
|
50
|
+
const cronToolRunInfoMap = _g.__xyCronToolRunInfo;
|
|
51
|
+
/** Store the openclaw runId for a cron-triggered tool call. */
|
|
52
|
+
export function setCronToolRunInfo(toolCallId, runId) {
|
|
53
|
+
if (toolCallId && runId) {
|
|
54
|
+
cronToolRunInfoMap.set(toolCallId, { runId });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Get the stored runId for a cron-triggered tool call. */
|
|
58
|
+
export function getCronToolRunInfo(toolCallId) {
|
|
59
|
+
return cronToolRunInfoMap.get(toolCallId);
|
|
60
|
+
}
|
|
61
|
+
/** Clean up cron tool run info after use. */
|
|
62
|
+
export function clearCronToolRunInfo(toolCallId) {
|
|
63
|
+
cronToolRunInfoMap.delete(toolCallId);
|
|
64
|
+
}
|
|
42
65
|
// ── Cron session ↔ jobId bridge ────────────────────────────────────
|
|
43
66
|
// fire 期 jobId 传递桥。provider.ts 在 isCron 分支从首条消息
|
|
44
67
|
// `[cron:<jobId> ...]` 解析出真实 jobId,写入合成 cron sessionId;
|