@ynhcj/xiaoyi-channel 0.0.196-beta → 0.0.196-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.d.ts +3 -8
- package/dist/index.js +8 -4
- package/dist/src/acp-session-binding.d.ts +37 -0
- package/dist/src/acp-session-binding.js +237 -0
- package/dist/src/bot.js +72 -115
- package/dist/src/channel.js +81 -64
- package/dist/src/client.d.ts +5 -0
- package/dist/src/client.js +10 -0
- package/dist/src/cspl/call_api.d.ts +1 -1
- package/dist/src/cspl/call_api.js +11 -12
- package/dist/src/cspl/config.d.ts +0 -1
- package/dist/src/cspl/config.js +68 -59
- package/dist/src/cspl/constants.d.ts +36 -1
- package/dist/src/cspl/constants.js +9 -0
- package/dist/src/cspl/sentinel_hook.js +29 -28
- package/dist/src/cspl/upload_file.js +5 -6
- package/dist/src/cspl/utils.d.ts +19 -9
- package/dist/src/cspl/utils.js +112 -75
- package/dist/src/formatter.js +21 -31
- package/dist/src/log-reporter/config-loader.d.ts +11 -0
- package/dist/src/log-reporter/config-loader.js +68 -0
- package/dist/src/log-reporter/cursor-store.d.ts +5 -0
- package/dist/src/log-reporter/cursor-store.js +26 -0
- package/dist/src/log-reporter/index.d.ts +10 -0
- package/dist/src/log-reporter/index.js +201 -0
- package/dist/src/log-reporter/openclaw-parser.d.ts +18 -0
- package/dist/src/log-reporter/openclaw-parser.js +94 -0
- package/dist/src/log-reporter/path-resolver.d.ts +5 -0
- package/dist/src/log-reporter/path-resolver.js +50 -0
- package/dist/src/log-reporter/reporter.d.ts +6 -0
- package/dist/src/log-reporter/reporter.js +56 -0
- package/dist/src/log-reporter/scanner.d.ts +10 -0
- package/dist/src/log-reporter/scanner.js +78 -0
- package/dist/src/log-reporter/types.d.ts +58 -0
- package/dist/src/log-reporter/types.js +2 -0
- package/dist/src/log-reporter/uploader.d.ts +7 -0
- package/dist/src/log-reporter/uploader.js +55 -0
- package/dist/src/memory-query-handler.js +3 -2
- package/dist/src/monitor.js +19 -9
- package/dist/src/outbound.js +5 -0
- package/dist/src/parser.d.ts +3 -3
- package/dist/src/parser.js +6 -6
- package/dist/src/provider.js +55 -20
- package/dist/src/reply-dispatcher.d.ts +3 -0
- package/dist/src/reply-dispatcher.js +158 -159
- package/dist/src/self-evolution-handler.js +2 -1
- package/dist/src/skill-retriever/config.js +2 -0
- package/dist/src/skill-retriever/hooks.js +1 -0
- package/dist/src/skill-retriever/tool-search.d.ts +3 -0
- package/dist/src/skill-retriever/tool-search.js +27 -11
- package/dist/src/skill-retriever/types.d.ts +1 -0
- package/dist/src/subagent-wait-state.d.ts +66 -0
- package/dist/src/subagent-wait-state.js +226 -0
- package/dist/src/task-manager.d.ts +5 -6
- package/dist/src/task-manager.js +5 -9
- package/dist/src/tools/agent-as-skill-tool.d.ts +41 -2
- package/dist/src/tools/agent-as-skill-tool.js +144 -151
- package/dist/src/tools/calendar-tool.d.ts +24 -2
- package/dist/src/tools/calendar-tool.js +115 -117
- package/dist/src/tools/call-device-tool.d.ts +20 -6
- package/dist/src/tools/call-device-tool.js +116 -138
- package/dist/src/tools/call-phone-tool.d.ts +21 -2
- package/dist/src/tools/call-phone-tool.js +112 -114
- package/dist/src/tools/check-plugin-privilege-tool.d.ts +16 -2
- package/dist/src/tools/check-plugin-privilege-tool.js +141 -143
- package/dist/src/tools/create-alarm-tool.d.ts +39 -2
- package/dist/src/tools/create-alarm-tool.js +229 -231
- package/dist/src/tools/create-all-tools.js +2 -2
- package/dist/src/tools/delete-alarm-tool.d.ts +15 -2
- package/dist/src/tools/delete-alarm-tool.js +134 -136
- package/dist/src/tools/device-tool-map.js +1 -7
- package/dist/src/tools/discover-cross-devices-tool.d.ts +16 -2
- package/dist/src/tools/discover-cross-devices-tool.js +121 -124
- package/dist/src/tools/display-a2ui-card-bypath-tool.d.ts +21 -0
- package/dist/src/tools/display-a2ui-card-bypath-tool.js +65 -0
- package/dist/src/tools/get-alarm-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-alarm-tool-schema.js +10 -16
- package/dist/src/tools/get-calendar-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-calendar-tool-schema.js +8 -12
- package/dist/src/tools/get-collection-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-collection-tool-schema.js +9 -11
- package/dist/src/tools/get-contact-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-contact-tool-schema.js +10 -16
- package/dist/src/tools/get-device-file-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-device-file-tool-schema.js +9 -13
- package/dist/src/tools/get-email-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-email-tool-schema.js +8 -11
- package/dist/src/tools/get-note-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-note-tool-schema.js +9 -14
- package/dist/src/tools/get-photo-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-photo-tool-schema.js +9 -12
- package/dist/src/tools/hmos-cli.d.ts +103 -0
- package/dist/src/tools/hmos-cli.js +561 -0
- package/dist/src/tools/image-reading-tool.d.ts +28 -2
- package/dist/src/tools/image-reading-tool.js +76 -76
- package/dist/src/tools/invoke.d.ts +48 -0
- package/dist/src/tools/invoke.js +1201 -0
- package/dist/src/tools/location-tool.d.ts +11 -2
- package/dist/src/tools/location-tool.js +93 -92
- package/dist/src/tools/login-token-tool.d.ts +20 -2
- package/dist/src/tools/login-token-tool.js +123 -125
- package/dist/src/tools/modify-alarm-tool.d.ts +47 -2
- package/dist/src/tools/modify-alarm-tool.js +250 -252
- package/dist/src/tools/modify-note-tool.d.ts +20 -2
- package/dist/src/tools/modify-note-tool.js +107 -109
- package/dist/src/tools/note-tool.d.ts +20 -2
- package/dist/src/tools/note-tool.js +106 -108
- package/dist/src/tools/query-app-message-tool.d.ts +28 -2
- package/dist/src/tools/query-app-message-tool.js +111 -113
- package/dist/src/tools/query-memory-data-tool.d.ts +28 -2
- package/dist/src/tools/query-memory-data-tool.js +112 -114
- package/dist/src/tools/query-todo-task-tool.d.ts +24 -2
- package/dist/src/tools/query-todo-task-tool.js +106 -108
- package/dist/src/tools/save-file-to-phone-tool.d.ts +24 -2
- package/dist/src/tools/save-file-to-phone-tool.js +130 -132
- package/dist/src/tools/save-media-to-gallery-tool.d.ts +24 -2
- package/dist/src/tools/save-media-to-gallery-tool.js +137 -139
- package/dist/src/tools/save-self-evolution-skill-tool.d.ts +54 -2
- package/dist/src/tools/save-self-evolution-skill-tool.js +194 -194
- package/dist/src/tools/search-alarm-tool.d.ts +34 -2
- package/dist/src/tools/search-alarm-tool.js +174 -176
- package/dist/src/tools/search-calendar-tool.d.ts +24 -2
- package/dist/src/tools/search-calendar-tool.js +148 -150
- package/dist/src/tools/search-contact-tool.d.ts +16 -2
- package/dist/src/tools/search-contact-tool.js +101 -103
- package/dist/src/tools/search-email-tool.d.ts +21 -2
- package/dist/src/tools/search-email-tool.js +110 -112
- package/dist/src/tools/search-file-tool.d.ts +16 -2
- package/dist/src/tools/search-file-tool.js +104 -106
- package/dist/src/tools/search-message-tool.d.ts +16 -2
- package/dist/src/tools/search-message-tool.js +103 -105
- package/dist/src/tools/search-note-tool.d.ts +16 -2
- package/dist/src/tools/search-note-tool.js +98 -100
- package/dist/src/tools/search-photo-gallery-tool.d.ts +21 -2
- package/dist/src/tools/search-photo-gallery-tool.js +35 -37
- package/dist/src/tools/send-cross-device-task-tool.d.ts +35 -2
- package/dist/src/tools/send-cross-device-task-tool.js +140 -143
- package/dist/src/tools/send-email-tool.d.ts +24 -2
- package/dist/src/tools/send-email-tool.js +108 -110
- package/dist/src/tools/send-file-to-user-tool.d.ts +20 -2
- package/dist/src/tools/send-file-to-user-tool.js +174 -176
- package/dist/src/tools/send-html-card-tool.d.ts +20 -2
- package/dist/src/tools/send-html-card-tool.js +85 -87
- package/dist/src/tools/send-message-tool.d.ts +20 -2
- package/dist/src/tools/send-message-tool.js +122 -124
- package/dist/src/tools/session-manager.d.ts +9 -52
- package/dist/src/tools/session-manager.js +40 -231
- package/dist/src/tools/upload-file-tool.d.ts +20 -2
- package/dist/src/tools/upload-file-tool.js +80 -82
- package/dist/src/tools/upload-photo-tool.d.ts +20 -2
- package/dist/src/tools/upload-photo-tool.js +68 -70
- package/dist/src/tools/xiaoyi-add-collection-tool.d.ts +32 -2
- package/dist/src/tools/xiaoyi-add-collection-tool.js +146 -148
- package/dist/src/tools/xiaoyi-collection-tool.d.ts +20 -2
- package/dist/src/tools/xiaoyi-collection-tool.js +114 -116
- package/dist/src/tools/xiaoyi-delete-collection-tool.d.ts +15 -2
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +127 -129
- package/dist/src/tools/xiaoyi-gui-tool.d.ts +16 -2
- package/dist/src/tools/xiaoyi-gui-tool.js +92 -95
- package/dist/src/utils/logger.js +3 -14
- package/dist/src/websocket.d.ts +1 -1
- package/dist/src/websocket.js +10 -1
- package/package.json +2 -2
package/dist/src/channel.js
CHANGED
|
@@ -2,15 +2,55 @@ 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
|
|
6
|
-
import { createAllTools } from "./tools/create-all-tools.js";
|
|
5
|
+
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
7
6
|
import { logger } from "./utils/logger.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
// Static tool imports (3.24 pattern)
|
|
8
|
+
import { locationTool } from "./tools/location-tool.js";
|
|
9
|
+
import { xiaoyiGuiTool } from "./tools/xiaoyi-gui-tool.js";
|
|
10
|
+
import { sendFileToUserTool } from "./tools/send-file-to-user-tool.js";
|
|
11
|
+
import { sendHtmlCardTool } from "./tools/send-html-card-tool.js";
|
|
12
|
+
import { viewPushResultTool } from "./tools/view-push-result-tool.js";
|
|
13
|
+
import { imageReadingTool } from "./tools/image-reading-tool.js";
|
|
14
|
+
import { timestampToUtc8Tool } from "./tools/timestamp-to-utc8-tool.js";
|
|
15
|
+
import { saveSelfEvolutionSkillTool } from "./tools/save-self-evolution-skill-tool.js";
|
|
16
|
+
import { callDeviceTool } from "./tools/call-device-tool.js";
|
|
17
|
+
import { getNoteToolSchemaTool } from "./tools/get-note-tool-schema.js";
|
|
18
|
+
import { getCalendarToolSchemaTool } from "./tools/get-calendar-tool-schema.js";
|
|
19
|
+
import { getContactToolSchemaTool } from "./tools/get-contact-tool-schema.js";
|
|
20
|
+
import { getPhotoToolSchemaTool } from "./tools/get-photo-tool-schema.js";
|
|
21
|
+
import { getDeviceFileToolSchemaTool } from "./tools/get-device-file-tool-schema.js";
|
|
22
|
+
import { getAlarmToolSchemaTool } from "./tools/get-alarm-tool-schema.js";
|
|
23
|
+
import { getCollectionToolSchemaTool } from "./tools/get-collection-tool-schema.js";
|
|
24
|
+
import { loginTokenTool } from "./tools/login-token-tool.js";
|
|
25
|
+
import { agentAsSkillTool } from "./tools/agent-as-skill-tool.js";
|
|
26
|
+
import { discoverCrossDevicesTool } from "./tools/discover-cross-devices-tool.js";
|
|
27
|
+
import { sendCrossDeviceTaskTool } from "./tools/send-cross-device-task-tool.js";
|
|
28
|
+
import { displayA2UICardByPathTool } from "./tools/display-a2ui-card-bypath-tool.js";
|
|
29
|
+
import { checkPluginPrivilegeTool } from "./tools/check-plugin-privilege-tool.js";
|
|
30
|
+
const ALL_TOOLS = [
|
|
31
|
+
locationTool,
|
|
32
|
+
discoverCrossDevicesTool,
|
|
33
|
+
sendCrossDeviceTaskTool,
|
|
34
|
+
displayA2UICardByPathTool,
|
|
35
|
+
callDeviceTool,
|
|
36
|
+
getNoteToolSchemaTool,
|
|
37
|
+
getCalendarToolSchemaTool,
|
|
38
|
+
getContactToolSchemaTool,
|
|
39
|
+
getPhotoToolSchemaTool,
|
|
40
|
+
xiaoyiGuiTool,
|
|
41
|
+
getDeviceFileToolSchemaTool,
|
|
42
|
+
getAlarmToolSchemaTool,
|
|
43
|
+
getCollectionToolSchemaTool,
|
|
44
|
+
sendFileToUserTool,
|
|
45
|
+
sendHtmlCardTool,
|
|
46
|
+
viewPushResultTool,
|
|
47
|
+
imageReadingTool,
|
|
48
|
+
timestampToUtc8Tool,
|
|
49
|
+
saveSelfEvolutionSkillTool,
|
|
50
|
+
loginTokenTool,
|
|
51
|
+
agentAsSkillTool,
|
|
52
|
+
checkPluginPrivilegeTool,
|
|
53
|
+
];
|
|
14
54
|
/**
|
|
15
55
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
16
56
|
* Implements Xiaoyi A2A protocol with dual WebSocket connections.
|
|
@@ -32,7 +72,7 @@ export const xyPlugin = {
|
|
|
32
72
|
],
|
|
33
73
|
},
|
|
34
74
|
capabilities: {
|
|
35
|
-
chatTypes: ["direct"],
|
|
75
|
+
chatTypes: ["direct"],
|
|
36
76
|
polls: false,
|
|
37
77
|
threads: false,
|
|
38
78
|
media: true,
|
|
@@ -49,59 +89,11 @@ export const xyPlugin = {
|
|
|
49
89
|
schema: xyConfigSchema,
|
|
50
90
|
},
|
|
51
91
|
outbound: xyOutbound,
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* 1. **Normal (WebSocket) session** – `getCurrentSessionContext()` returns
|
|
58
|
-
* a context that was registered by bot.ts during message processing.
|
|
59
|
-
* Tools send commands through the WebSocket and listen for responses.
|
|
60
|
-
*
|
|
61
|
-
* 2. **Cron / scheduled-task session** – openclaw's cron runner calls
|
|
62
|
-
* `agentTools({ cfg })` without an active WebSocket session. When no
|
|
63
|
-
* session context exists but `cfg` is provided, we create a synthetic
|
|
64
|
-
* "cron session" with `isCron: true` and a `cron-`-prefixed sessionId.
|
|
65
|
-
* `sendCommand()` detects this prefix and routes commands through the
|
|
66
|
-
* push channel. Response listening (WebSocket events) works unchanged
|
|
67
|
-
* because the gateway WebSocket connection is always active.
|
|
68
|
-
*/
|
|
69
|
-
agentTools: (params) => {
|
|
70
|
-
let ctx = getCurrentSessionContext();
|
|
71
|
-
// ── Cron / non-session fallback ──────────────────────────────
|
|
72
|
-
// When no active xy WebSocket session exists but the openclaw cfg
|
|
73
|
-
// is provided (framework calls agentTools({ cfg })), create a
|
|
74
|
-
// synthetic "cron session". This enables cron-triggered agent
|
|
75
|
-
// turns and cross-channel tool calls to use xiaoyi tools via the
|
|
76
|
-
// push channel. sendCommand() detects the "cron-" sessionId
|
|
77
|
-
// prefix and routes commands through push instead of WebSocket.
|
|
78
|
-
if (!ctx && params?.cfg) {
|
|
79
|
-
try {
|
|
80
|
-
const config = resolveXYConfig(params.cfg);
|
|
81
|
-
const cronId = `${CRON_SESSION_PREFIX}${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
82
|
-
ctx = {
|
|
83
|
-
config,
|
|
84
|
-
sessionId: cronId,
|
|
85
|
-
taskId: cronId,
|
|
86
|
-
messageId: cronId,
|
|
87
|
-
agentId: "default",
|
|
88
|
-
isCron: true,
|
|
89
|
-
};
|
|
90
|
-
// Register so getCurrentSessionContext() fallback can find it
|
|
91
|
-
registerSession(`__cron__${cronId}`, ctx);
|
|
92
|
-
logger.log(`[CRON-TOOLS] Created cron session context: ${cronId}`);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
logger.error("[CRON-TOOLS] Failed to create cron context:", err);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (!ctx) {
|
|
99
|
-
logger.log("[CREATE-ALL-TOOLS] no session context, returning empty tools list");
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
const allTools = createAllTools(ctx);
|
|
103
|
-
const filtered = filterToolsByDevice(allTools, ctx.deviceType);
|
|
104
|
-
logger.log(`[DEVICE-FILTER] deviceType=${ctx.deviceType ?? "(none)"}, tools: ${allTools.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
92
|
+
/** Static tool list (3.24 pattern). Tools read SessionContext at execute time via ALS. */
|
|
93
|
+
agentTools: () => {
|
|
94
|
+
const ctx = getCurrentSessionContext();
|
|
95
|
+
const filtered = filterToolsByDevice(ALL_TOOLS, ctx?.deviceType);
|
|
96
|
+
logger.log(`[DEVICE-FILTER] deviceType=${ctx?.deviceType ?? "(none)"}, tools: ${ALL_TOOLS.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
105
97
|
return filtered;
|
|
106
98
|
},
|
|
107
99
|
messaging: {
|
|
@@ -113,7 +105,6 @@ export const xyPlugin = {
|
|
|
113
105
|
},
|
|
114
106
|
targetResolver: {
|
|
115
107
|
looksLikeId: (raw) => {
|
|
116
|
-
// 信任所有非空字符串作为有效的 sessionId
|
|
117
108
|
const trimmed = raw.trim();
|
|
118
109
|
return trimmed.length > 0;
|
|
119
110
|
},
|
|
@@ -135,20 +126,46 @@ export const xyPlugin = {
|
|
|
135
126
|
? { conversationId, matchPriority: 2 }
|
|
136
127
|
: null;
|
|
137
128
|
},
|
|
129
|
+
resolveCommandConversation: ({ accountId, sessionKey }) => {
|
|
130
|
+
// xiaoyi-channel: the A2A sessionId IS the conversationId.
|
|
131
|
+
// 1. Prefer the current ALS session context (works when the command
|
|
132
|
+
// is processed inside an active A2A message-handling turn).
|
|
133
|
+
const ctx = getCurrentSessionContext();
|
|
134
|
+
const alsSessionId = ctx?.sessionId?.trim();
|
|
135
|
+
if (alsSessionId)
|
|
136
|
+
return { conversationId: alsSessionId };
|
|
137
|
+
// 2. Fall back to parsing the session key. For xiaoyi-channel the
|
|
138
|
+
// session key has the form:
|
|
139
|
+
// agent:<agentId>:xiaoyi-channel:<accountId>:<sessionId>
|
|
140
|
+
// The last `:`-delimited segment is the A2A sessionId.
|
|
141
|
+
if (sessionKey) {
|
|
142
|
+
const lastColon = sessionKey.lastIndexOf(":");
|
|
143
|
+
if (lastColon >= 0) {
|
|
144
|
+
const sessionId = sessionKey.slice(lastColon + 1).trim();
|
|
145
|
+
if (sessionId)
|
|
146
|
+
return { conversationId: sessionId };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
},
|
|
138
151
|
},
|
|
139
152
|
reload: {
|
|
140
153
|
configPrefixes: ["channels.xiaoyi-channel"],
|
|
141
154
|
},
|
|
142
|
-
// Gateway adapter for receiving messages
|
|
143
155
|
gateway: {
|
|
144
156
|
async startAccount(context) {
|
|
145
157
|
const { monitorXYProvider } = await import("./monitor.js");
|
|
158
|
+
const { createXyAcpBindingManager } = await import("./acp-session-binding.js");
|
|
146
159
|
const account = resolveXYConfig(context.cfg);
|
|
147
160
|
context.setStatus?.({
|
|
148
161
|
accountId: context.accountId,
|
|
149
162
|
wsUrl: account.wsUrl,
|
|
150
163
|
});
|
|
151
164
|
context.log?.info(`[${context.accountId}] starting xiaoyi channel (wsUrl: ${account.wsUrl})`);
|
|
165
|
+
// Register ACP session binding adapter for this account.
|
|
166
|
+
// Enables sessions_spawn(runtime="acp") to bind subagent sessions
|
|
167
|
+
// to the current A2A conversation.
|
|
168
|
+
createXyAcpBindingManager({ accountId: context.accountId, cfg: context.cfg });
|
|
152
169
|
return monitorXYProvider({
|
|
153
170
|
config: context.cfg,
|
|
154
171
|
runtime: context.runtime,
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { XYWebSocketManager } from "./websocket.js";
|
|
2
2
|
import type { XYChannelConfig } from "./types.js";
|
|
3
3
|
import type { RuntimeEnv } from "openclaw/plugin-sdk";
|
|
4
|
+
/**
|
|
5
|
+
* Get a cached WebSocket manager without requiring config.
|
|
6
|
+
* Returns the first available manager. Use when ALS has no SessionContext.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getCachedXYWebSocketManager(): XYWebSocketManager;
|
|
4
9
|
/**
|
|
5
10
|
* Get or create a WebSocket manager for the given configuration.
|
|
6
11
|
* Reuses existing managers if config matches.
|
package/dist/src/client.js
CHANGED
|
@@ -13,6 +13,16 @@ if (!_g.__xyWsManagerCache) {
|
|
|
13
13
|
_g.__xyWsManagerCache = new Map();
|
|
14
14
|
}
|
|
15
15
|
const wsManagerCache = _g.__xyWsManagerCache;
|
|
16
|
+
/**
|
|
17
|
+
* Get a cached WebSocket manager without requiring config.
|
|
18
|
+
* Returns the first available manager. Use when ALS has no SessionContext.
|
|
19
|
+
*/
|
|
20
|
+
export function getCachedXYWebSocketManager() {
|
|
21
|
+
if (wsManagerCache.size === 0) {
|
|
22
|
+
throw new Error("No WebSocket manager available in cache");
|
|
23
|
+
}
|
|
24
|
+
return wsManagerCache.values().next().value;
|
|
25
|
+
}
|
|
16
26
|
/**
|
|
17
27
|
* Get or create a WebSocket manager for the given configuration.
|
|
18
28
|
* Reuses existing managers if config matches.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ApiResponse } from './constants.js';
|
|
2
|
-
export declare function callApi(
|
|
2
|
+
export declare function callApi(payload: object, api: any, sessionId: string): Promise<ApiResponse>;
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import https from 'https';
|
|
5
5
|
import { URL } from 'url';
|
|
6
6
|
import { getConfig } from './config.js';
|
|
7
|
-
import {
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT, HTTP_STATUS_BAD_REQUEST } from './constants.js';
|
|
8
9
|
function buildHeadersForCelia(config, sessionId) {
|
|
9
10
|
if (!config.uid || !config.apiKey || !config.skillId || !config.requestFrom) {
|
|
10
11
|
throw new Error('[SENTINEL HOOK] Missing required configuration: uid, apiKey, skillId, or requestFrom is not defined');
|
|
@@ -22,7 +23,7 @@ function buildRequestOptions(url, headers, timeout) {
|
|
|
22
23
|
const urlObj = new URL(url);
|
|
23
24
|
return {
|
|
24
25
|
hostname: urlObj.hostname,
|
|
25
|
-
port: urlObj.port || DEFAULT_HTTPS_PORT,
|
|
26
|
+
port: urlObj.port || (urlObj.protocol === 'https:' ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT),
|
|
26
27
|
path: urlObj.pathname,
|
|
27
28
|
method: "POST",
|
|
28
29
|
headers: headers,
|
|
@@ -69,6 +70,7 @@ function handleResponse(res, resolve, reject) {
|
|
|
69
70
|
data += chunk;
|
|
70
71
|
});
|
|
71
72
|
res.on('end', () => {
|
|
73
|
+
logger.log(`[SENTINEL HOOK] callApi response body: ${data}`);
|
|
72
74
|
try {
|
|
73
75
|
const result = parseResponseData(data);
|
|
74
76
|
resolve(result);
|
|
@@ -78,19 +80,16 @@ function handleResponse(res, resolve, reject) {
|
|
|
78
80
|
}
|
|
79
81
|
});
|
|
80
82
|
}
|
|
81
|
-
export async function callApi(
|
|
83
|
+
export async function callApi(payload, api, sessionId) {
|
|
82
84
|
const config = getConfig(api);
|
|
83
85
|
const headersForCelia = buildHeadersForCelia(config, sessionId);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
};
|
|
90
|
-
const httpBody = JSON.stringify(payload);
|
|
91
|
-
const apiUrl = `${config.api.url}${API_URL_SUFFIX}`;
|
|
86
|
+
// 确保 uid 存在于消息体中(从 config 注入)
|
|
87
|
+
const payloadWithUid = { ...payload, uid: config.uid };
|
|
88
|
+
const httpBody = JSON.stringify(payloadWithUid);
|
|
89
|
+
logger.log(`[SENTINEL HOOK] callApi request URL: ${config.api.url}`);
|
|
90
|
+
logger.log(`[SENTINEL HOOK] callApi request body: ${httpBody}`);
|
|
92
91
|
return new Promise((resolve, reject) => {
|
|
93
|
-
const options = buildRequestOptions(
|
|
92
|
+
const options = buildRequestOptions(config.api.url, headersForCelia, config.api.timeout);
|
|
94
93
|
const req = https.request(options, (res) => {
|
|
95
94
|
handleResponse(res, resolve, reject);
|
|
96
95
|
});
|
package/dist/src/cspl/config.js
CHANGED
|
@@ -3,48 +3,50 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
6
9
|
import { CONFIG_FILE_NAME, ENV_FILE_PATH, REQUIRED_ENV_VARS } from './constants.js';
|
|
7
10
|
import { logger } from '../utils/logger.js';
|
|
8
11
|
let cachedConfig = null;
|
|
9
|
-
function
|
|
10
|
-
if (!fs.existsSync(ENV_FILE_PATH)) {
|
|
11
|
-
throw new Error(`Environment file not found.`);
|
|
12
|
-
}
|
|
13
|
-
let envData;
|
|
12
|
+
function loadEnvContent() {
|
|
14
13
|
try {
|
|
15
|
-
|
|
14
|
+
return fs.readFileSync(ENV_FILE_PATH, 'utf-8');
|
|
16
15
|
}
|
|
17
16
|
catch (error) {
|
|
18
17
|
const err = error;
|
|
19
18
|
throw new Error(`Failed to read environment file. Error: ${err.message}`);
|
|
20
19
|
}
|
|
21
|
-
|
|
20
|
+
}
|
|
21
|
+
function parseEnvLine(line) {
|
|
22
|
+
const trimmedLine = line.trim();
|
|
23
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const firstEqualIndex = trimmedLine.indexOf('=');
|
|
27
|
+
if (firstEqualIndex === -1) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const key = trimmedLine.substring(0, firstEqualIndex).trim();
|
|
31
|
+
const value = trimmedLine.substring(firstEqualIndex + 1).trim();
|
|
32
|
+
return { key, value };
|
|
33
|
+
}
|
|
34
|
+
function readEnvFile() {
|
|
35
|
+
if (!fs.existsSync(ENV_FILE_PATH)) {
|
|
36
|
+
throw new Error(`Environment file not found.`);
|
|
37
|
+
}
|
|
38
|
+
const envData = loadEnvContent();
|
|
39
|
+
const envVars = {};
|
|
22
40
|
const lines = envData.split('\n');
|
|
23
41
|
for (const line of lines) {
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
const firstEqualIndex = trimmedLine.indexOf('=');
|
|
29
|
-
if (firstEqualIndex === -1) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
const key = trimmedLine.substring(0, firstEqualIndex).trim();
|
|
33
|
-
const value = trimmedLine.substring(firstEqualIndex + 1).trim();
|
|
34
|
-
if (key && REQUIRED_ENV_VARS.includes(key)) {
|
|
35
|
-
env[key] = value;
|
|
42
|
+
const parsed = parseEnvLine(line);
|
|
43
|
+
if (parsed && parsed.key && REQUIRED_ENV_VARS.includes(parsed.key)) {
|
|
44
|
+
envVars[parsed.key] = parsed.value;
|
|
36
45
|
}
|
|
37
46
|
}
|
|
38
|
-
return
|
|
47
|
+
return envVars;
|
|
39
48
|
}
|
|
40
|
-
|
|
41
|
-
if (cachedConfig) {
|
|
42
|
-
return cachedConfig;
|
|
43
|
-
}
|
|
44
|
-
const configPath = path.join(__dirname, CONFIG_FILE_NAME);
|
|
45
|
-
if (!fs.existsSync(configPath)) {
|
|
46
|
-
throw new Error(`Config file not found: ${CONFIG_FILE_NAME}`);
|
|
47
|
-
}
|
|
49
|
+
function loadConfigFile(configPath) {
|
|
48
50
|
let configData;
|
|
49
51
|
try {
|
|
50
52
|
configData = fs.readFileSync(configPath, 'utf-8');
|
|
@@ -52,58 +54,65 @@ export function getConfig(api) {
|
|
|
52
54
|
catch (error) {
|
|
53
55
|
throw new Error(`Failed to read config file: ${CONFIG_FILE_NAME}.`);
|
|
54
56
|
}
|
|
55
|
-
let parsedConfig;
|
|
56
57
|
try {
|
|
57
|
-
|
|
58
|
+
return JSON.parse(configData);
|
|
58
59
|
}
|
|
59
60
|
catch (error) {
|
|
60
61
|
throw new Error(`Failed to parse config file: ${CONFIG_FILE_NAME}.`);
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!config.requestFrom || typeof config.requestFrom !== 'string') {
|
|
76
|
-
throw new Error(`Invalid config: missing or invalid 'requestFrom' in ${CONFIG_FILE_NAME}`);
|
|
77
|
-
}
|
|
78
|
-
if (!config.textSource || typeof config.textSource !== 'string') {
|
|
79
|
-
throw new Error(`Invalid config: missing or invalid 'textSource' in ${CONFIG_FILE_NAME}`);
|
|
80
|
-
}
|
|
81
|
-
if (!config.action || typeof config.action !== 'string') {
|
|
82
|
-
throw new Error(`Invalid config: missing or invalid 'action' in ${CONFIG_FILE_NAME}`);
|
|
63
|
+
}
|
|
64
|
+
function validateConfigStructure(config) {
|
|
65
|
+
const validators = [
|
|
66
|
+
{ field: 'api', check: () => !config.api || typeof config.api !== 'object' },
|
|
67
|
+
{ field: 'api.timeout', check: () => !config.api?.timeout || typeof config.api.timeout !== 'number' },
|
|
68
|
+
{ field: 'skillId', check: () => !config.skillId || typeof config.skillId !== 'string' },
|
|
69
|
+
{ field: 'requestFrom', check: () => !config.requestFrom || typeof config.requestFrom !== 'string' },
|
|
70
|
+
{ field: 'textSource', check: () => !config.textSource || typeof config.textSource !== 'string' },
|
|
71
|
+
];
|
|
72
|
+
for (const { field, check } of validators) {
|
|
73
|
+
if (check()) {
|
|
74
|
+
throw new Error(`Invalid config: missing or invalid '${field}' in ${CONFIG_FILE_NAME}`);
|
|
75
|
+
}
|
|
83
76
|
}
|
|
84
|
-
|
|
77
|
+
}
|
|
78
|
+
function validateEnvVars() {
|
|
79
|
+
let envVars;
|
|
85
80
|
try {
|
|
86
|
-
|
|
81
|
+
envVars = readEnvFile();
|
|
87
82
|
}
|
|
88
83
|
catch (error) {
|
|
89
84
|
const err = error;
|
|
90
85
|
throw new Error(`Failed to load environment variables from env files: ${err.message}`);
|
|
91
86
|
}
|
|
92
|
-
const personalApiKey =
|
|
87
|
+
const personalApiKey = envVars['PERSONAL-API-KEY'];
|
|
93
88
|
if (!personalApiKey || typeof personalApiKey !== 'string' || personalApiKey.trim() === '') {
|
|
94
89
|
throw new Error(`Missing or empty 'PERSONAL-API-KEY' in env files`);
|
|
95
90
|
}
|
|
96
|
-
const personalUid =
|
|
91
|
+
const personalUid = envVars['PERSONAL-UID'];
|
|
97
92
|
if (!personalUid || typeof personalUid !== 'string' || personalUid.trim() === '') {
|
|
98
93
|
throw new Error(`Missing or empty 'PERSONAL-UID' in env files`);
|
|
99
94
|
}
|
|
100
|
-
const serviceUrl =
|
|
95
|
+
const serviceUrl = envVars.SERVICE_URL;
|
|
101
96
|
if (!serviceUrl || typeof serviceUrl !== 'string' || serviceUrl.trim() === '') {
|
|
102
97
|
throw new Error(`Missing or empty 'SERVICE_URL' in env files`);
|
|
103
98
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
return envVars;
|
|
100
|
+
}
|
|
101
|
+
export function getConfig(api) {
|
|
102
|
+
if (cachedConfig) {
|
|
103
|
+
return cachedConfig;
|
|
104
|
+
}
|
|
105
|
+
const configPath = path.join(__dirname, CONFIG_FILE_NAME);
|
|
106
|
+
if (!fs.existsSync(configPath)) {
|
|
107
|
+
throw new Error(`Config file not found: ${CONFIG_FILE_NAME}`);
|
|
108
|
+
}
|
|
109
|
+
const parsedConfig = loadConfigFile(configPath);
|
|
110
|
+
const config = parsedConfig;
|
|
111
|
+
validateConfigStructure(config);
|
|
112
|
+
const envVars = validateEnvVars();
|
|
113
|
+
config.apiKey = envVars['PERSONAL-API-KEY'].trim();
|
|
114
|
+
config.uid = envVars['PERSONAL-UID'].trim();
|
|
115
|
+
config.api.url = envVars.SERVICE_URL.trim();
|
|
107
116
|
cachedConfig = config;
|
|
108
117
|
logger.log(`[SENTINEL HOOK] Config loaded successfully`);
|
|
109
118
|
return cachedConfig;
|
|
@@ -11,7 +11,42 @@ export interface ApiPayload {
|
|
|
11
11
|
questionText: string;
|
|
12
12
|
textSource: string;
|
|
13
13
|
action: string;
|
|
14
|
-
|
|
14
|
+
}
|
|
15
|
+
export declare const RESULT_CODE_MAP: Record<number, string>;
|
|
16
|
+
export interface NewRequestPayload {
|
|
17
|
+
taskID: string;
|
|
18
|
+
sessionID: string;
|
|
19
|
+
uid: string;
|
|
20
|
+
businessID: string;
|
|
21
|
+
sceneID: string;
|
|
22
|
+
checkPoint: number;
|
|
23
|
+
interActionID: number;
|
|
24
|
+
loginType?: string;
|
|
25
|
+
reqTime?: string;
|
|
26
|
+
message: object;
|
|
27
|
+
}
|
|
28
|
+
export interface NewApiResponse {
|
|
29
|
+
data: {
|
|
30
|
+
taskID: string;
|
|
31
|
+
resultCode: number;
|
|
32
|
+
resultMessage?: string;
|
|
33
|
+
securityResult: string;
|
|
34
|
+
riskLabels?: string[];
|
|
35
|
+
riskDegree?: Array<{
|
|
36
|
+
riskLabel: string;
|
|
37
|
+
score: number;
|
|
38
|
+
}>;
|
|
39
|
+
riskLabelCount?: Array<{
|
|
40
|
+
riskLabel: string;
|
|
41
|
+
count: number;
|
|
42
|
+
}>;
|
|
43
|
+
actionRiskResult?: {
|
|
44
|
+
riskScore: number;
|
|
45
|
+
riskTag: string[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
retCode?: string;
|
|
49
|
+
retMsg?: string;
|
|
15
50
|
}
|
|
16
51
|
export interface ApiResponse {
|
|
17
52
|
[key: string]: any;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* 版权所有 (c) 华为技术有限公司 2026-2026
|
|
3
3
|
*/
|
|
4
|
+
// resultCode 错误码映射
|
|
5
|
+
export const RESULT_CODE_MAP = {
|
|
6
|
+
0: 'Success',
|
|
7
|
+
1: 'Parameter is invalid',
|
|
8
|
+
2: "Parameter's format is invalid",
|
|
9
|
+
3: 'The request frequency exceeds the limit',
|
|
10
|
+
4: "Parameter's size is invalid",
|
|
11
|
+
5: 'The text detection is abnormal',
|
|
12
|
+
};
|
|
4
13
|
// 常量配置
|
|
5
14
|
export const MIN_TEXT_LENGTH = 0;
|
|
6
15
|
export const MAX_TEXT_LENGTH = 4096;
|
|
@@ -2,32 +2,34 @@
|
|
|
2
2
|
* 版权所有 (c) 华为技术有限公司 2026-2026
|
|
3
3
|
*/
|
|
4
4
|
import crypto from 'crypto';
|
|
5
|
-
import { callApi } from './call_api.js';
|
|
6
|
-
import { processText, extractResultText, validateAndTruncateText, parseSecurityResult, handleExecToolInput, handleMessageToolInput, handleOtherToolInput } from './utils.js';
|
|
7
|
-
import { ALLOWED_TOOLS, MAX_TEXT_LENGTH, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, STEER_ABORT_MESSAGE, TOOL_OUTPUT_ACTION } from './constants.js';
|
|
8
5
|
import { logger } from '../utils/logger.js';
|
|
9
|
-
import {
|
|
6
|
+
import { callApi } from './call_api.js';
|
|
7
|
+
import { processText, extractResultText, validateAndTruncateText, parseSecurityResult, handleExecToolInput, handleMessageToolInput, handleOtherToolInput, buildToolOutputPayload, extractInterActionId } from './utils.js';
|
|
8
|
+
import { ALLOWED_TOOLS, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, MAX_TEXT_LENGTH, STEER_ABORT_MESSAGE } from './constants.js';
|
|
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) {
|
|
13
13
|
api.on("before_tool_call", async (event, ctx) => {
|
|
14
14
|
logger.log(`[SENTINEL HOOK] before_tool_call_event toolName: ${event.toolName}`);
|
|
15
|
-
//
|
|
16
|
-
const
|
|
17
|
-
|
|
15
|
+
// 获取真实sessionID:优先使用ALS中的A2A sessionId,降级到OpenClaw runId或随机值
|
|
16
|
+
const sessionCtx = getCurrentSessionContext();
|
|
17
|
+
const sessionId = sessionCtx?.sessionId || (event.runId?.replace(/-/g, '') || crypto.randomBytes(16).toString('hex'));
|
|
18
|
+
const taskId = sessionCtx?.taskId || event.runId;
|
|
19
|
+
logger.log(`[SENTINEL HOOK] Session ID: ${sessionId}, Task ID: ${taskId} (fromALS: ${!!sessionCtx?.sessionId})`);
|
|
18
20
|
// 处理 TOOL_INPUT 数据采集、发送数据,根据扫描结果决定是否阻塞
|
|
19
21
|
try {
|
|
20
22
|
let scanResult = null;
|
|
21
23
|
if (event.toolName === 'exec') {
|
|
22
|
-
scanResult = await handleExecToolInput(event, api, sessionId);
|
|
24
|
+
scanResult = await handleExecToolInput(event, api, sessionId, taskId);
|
|
23
25
|
}
|
|
24
26
|
else if (event.toolName === 'message') {
|
|
25
|
-
scanResult = await handleMessageToolInput(event, api, sessionId);
|
|
27
|
+
scanResult = await handleMessageToolInput(event, api, sessionId, taskId);
|
|
26
28
|
}
|
|
27
29
|
else {
|
|
28
|
-
scanResult = await handleOtherToolInput(event, api, sessionId);
|
|
30
|
+
scanResult = await handleOtherToolInput(event, api, sessionId, taskId);
|
|
29
31
|
}
|
|
30
|
-
if (scanResult?.status === '
|
|
32
|
+
if (scanResult?.status === 'reject') {
|
|
31
33
|
logger.warn(`[SENTINEL HOOK] TOOL_INPUT REJECT, blocking tool call: ${event.toolName}`);
|
|
32
34
|
return { block: true, blockReason: `安全扫描检测到风险,已阻止工具调用: ${event.toolName}` };
|
|
33
35
|
}
|
|
@@ -43,10 +45,12 @@ export default function register(api) {
|
|
|
43
45
|
}
|
|
44
46
|
try {
|
|
45
47
|
logger.log(`[SENTINEL HOOK] after_tool_call_event toolName: ${event.toolName}`);
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
// 获取真实sessionID:优先使用ALS中的A2A sessionId,降级到OpenClaw runId或随机值
|
|
49
|
+
const sessionCtx = getCurrentSessionContext();
|
|
50
|
+
const sessionId = sessionCtx?.sessionId || (event.runId?.replace(/-/g, '') || crypto.randomBytes(16).toString('hex'));
|
|
51
|
+
const taskId = sessionCtx?.taskId || event.runId;
|
|
52
|
+
logger.log(`[SENTINEL HOOK] Session ID: ${sessionId}, Task ID: ${taskId} (fromALS: ${!!sessionCtx?.sessionId})`);
|
|
53
|
+
// 处理TOOL_OUTPUT数据采集
|
|
50
54
|
const resultText = extractResultText(event, event.toolName);
|
|
51
55
|
const resultTextLength = resultText.length;
|
|
52
56
|
if (resultTextLength > MAX_TOTAL_LENGTH) {
|
|
@@ -58,27 +62,24 @@ export default function register(api) {
|
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
60
64
|
// 处理和验证文本
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (finalText.length > MAX_TEXT_LENGTH) {
|
|
66
|
-
const diff_length = finalText.length - MAX_TEXT_LENGTH;
|
|
67
|
-
const { text: filterText, truncated } = validateAndTruncateText(originText, MAX_TEXT_LENGTH - diff_length);
|
|
65
|
+
const originText = processText(resultText);
|
|
66
|
+
let content = originText;
|
|
67
|
+
if (originText.length > MAX_TEXT_LENGTH) {
|
|
68
|
+
const { text: filterText, truncated } = validateAndTruncateText(originText, MAX_TEXT_LENGTH);
|
|
68
69
|
if (truncated) {
|
|
69
|
-
|
|
70
|
+
content = filterText;
|
|
70
71
|
logger.warn(`[SENTINEL HOOK] postText exceeds ${MAX_TEXT_LENGTH}.`);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
const
|
|
74
|
-
|
|
74
|
+
const interActionID = extractInterActionId(taskId);
|
|
75
|
+
const outputPayload = buildToolOutputPayload(taskId, sessionId, event.toolName, content, event.toolCallId, interActionID);
|
|
76
|
+
logger.log(`[SENTINEL HOOK] Content extracted successfully. Length: ${JSON.stringify(outputPayload).length}`);
|
|
75
77
|
try {
|
|
76
|
-
const response = await callApi(
|
|
78
|
+
const response = await callApi(outputPayload, api, sessionId);
|
|
77
79
|
const result = parseSecurityResult(response);
|
|
78
80
|
logger.log(`[SENTINEL HOOK] TOOL_OUTPUT response: status=${result.status}.`);
|
|
79
|
-
if (result.status === '
|
|
81
|
+
if (result.status === 'reject') {
|
|
80
82
|
logger.warn('[SENTINEL HOOK] REJECT detected, attempting steer injection');
|
|
81
|
-
const sessionCtx = ctx.sessionKey ? getSessionContext(ctx.sessionKey) : null;
|
|
82
83
|
if (sessionCtx?.sessionId && sessionCtx?.taskId) {
|
|
83
84
|
await tryInjectSteer({
|
|
84
85
|
sessionId: sessionCtx.sessionId,
|