openclaw-multi-auto 1.4.5 → 1.4.7

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.
Files changed (100) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  3. package/dist/plugin-sdk/mattermost.js +3 -3
  4. package/dist/plugin-sdk/signal.js +2 -2
  5. package/docs/browser-architecture.md +602 -0
  6. package/extensions/googlechat/node_modules/.bin/openclaw +2 -2
  7. package/extensions/memory-core/node_modules/.bin/openclaw +2 -2
  8. package/extensions/memory-lancedb/node_modules/.bin/openai +2 -2
  9. package/extensions/page-action-cache/dist/actions-executor.d.ts +62 -0
  10. package/extensions/page-action-cache/dist/actions-executor.d.ts.map +1 -0
  11. package/extensions/page-action-cache/dist/actions-executor.js +339 -0
  12. package/extensions/page-action-cache/dist/actions-executor.js.map +1 -0
  13. package/extensions/page-action-cache/dist/cache-invalidator.d.ts +70 -0
  14. package/extensions/page-action-cache/dist/cache-invalidator.d.ts.map +1 -0
  15. package/extensions/page-action-cache/dist/cache-invalidator.js +212 -0
  16. package/extensions/page-action-cache/dist/cache-invalidator.js.map +1 -0
  17. package/extensions/page-action-cache/dist/cache-store.d.ts +80 -0
  18. package/extensions/page-action-cache/dist/cache-store.d.ts.map +1 -0
  19. package/extensions/page-action-cache/dist/cache-store.js +361 -0
  20. package/extensions/page-action-cache/dist/cache-store.js.map +1 -0
  21. package/extensions/page-action-cache/dist/cache-strategy.d.ts +65 -0
  22. package/extensions/page-action-cache/dist/cache-strategy.d.ts.map +1 -0
  23. package/extensions/page-action-cache/dist/cache-strategy.js +237 -0
  24. package/extensions/page-action-cache/dist/cache-strategy.js.map +1 -0
  25. package/extensions/page-action-cache/dist/hooks-entry.d.ts +18 -0
  26. package/extensions/page-action-cache/dist/hooks-entry.d.ts.map +1 -0
  27. package/extensions/page-action-cache/dist/hooks-entry.js +27 -0
  28. package/extensions/page-action-cache/dist/hooks-entry.js.map +1 -0
  29. package/extensions/page-action-cache/dist/hooks.d.ts +10 -0
  30. package/extensions/page-action-cache/dist/hooks.d.ts.map +1 -0
  31. package/extensions/page-action-cache/dist/hooks.js +277 -0
  32. package/extensions/page-action-cache/dist/hooks.js.map +1 -0
  33. package/extensions/page-action-cache/dist/index.d.ts +24 -0
  34. package/extensions/page-action-cache/dist/index.d.ts.map +1 -0
  35. package/extensions/page-action-cache/dist/index.js +34 -0
  36. package/extensions/page-action-cache/dist/index.js.map +1 -0
  37. package/extensions/page-action-cache/dist/scenario-recognizer.d.ts +45 -0
  38. package/extensions/page-action-cache/dist/scenario-recognizer.d.ts.map +1 -0
  39. package/extensions/page-action-cache/dist/scenario-recognizer.js +213 -0
  40. package/extensions/page-action-cache/dist/scenario-recognizer.js.map +1 -0
  41. package/extensions/page-action-cache/dist/security-policy.d.ts +62 -0
  42. package/extensions/page-action-cache/dist/security-policy.d.ts.map +1 -0
  43. package/extensions/page-action-cache/dist/security-policy.js +219 -0
  44. package/extensions/page-action-cache/dist/security-policy.js.map +1 -0
  45. package/extensions/page-action-cache/dist/tools.d.ts +209 -0
  46. package/extensions/page-action-cache/dist/tools.d.ts.map +1 -0
  47. package/extensions/page-action-cache/dist/tools.js +383 -0
  48. package/extensions/page-action-cache/dist/tools.js.map +1 -0
  49. package/extensions/page-action-cache/dist/types.d.ts +336 -0
  50. package/extensions/page-action-cache/dist/types.d.ts.map +1 -0
  51. package/extensions/page-action-cache/dist/types.js +8 -0
  52. package/extensions/page-action-cache/dist/types.js.map +1 -0
  53. package/extensions/page-action-cache/dist/ux-enhancer.d.ts +60 -0
  54. package/extensions/page-action-cache/dist/ux-enhancer.d.ts.map +1 -0
  55. package/extensions/page-action-cache/dist/ux-enhancer.js +218 -0
  56. package/extensions/page-action-cache/dist/ux-enhancer.js.map +1 -0
  57. package/extensions/page-action-cache/dist/variable-resolver.d.ts +28 -0
  58. package/extensions/page-action-cache/dist/variable-resolver.d.ts.map +1 -0
  59. package/extensions/page-action-cache/dist/variable-resolver.js +201 -0
  60. package/extensions/page-action-cache/dist/variable-resolver.js.map +1 -0
  61. package/extensions/page-action-cache/docs/API.md +555 -0
  62. package/extensions/page-action-cache/docs/IMPLEMENTATION.md +1792 -0
  63. package/extensions/page-action-cache/docs/INTEGRATION.md +387 -0
  64. package/extensions/page-action-cache/docs/README.md +183 -0
  65. package/extensions/page-action-cache/index.ts +118 -0
  66. package/extensions/page-action-cache/node_modules/.bin/nlc +21 -0
  67. package/extensions/page-action-cache/node_modules/.bin/node-llama-cpp +21 -0
  68. package/extensions/page-action-cache/node_modules/.bin/openclaw +21 -0
  69. package/extensions/page-action-cache/node_modules/.bin/tsc +21 -0
  70. package/extensions/page-action-cache/node_modules/.bin/tsserver +21 -0
  71. package/extensions/page-action-cache/node_modules/.bin/vitest +21 -0
  72. package/extensions/page-action-cache/openclaw.plugin.json +208 -0
  73. package/extensions/page-action-cache/package.json +76 -0
  74. package/extensions/page-action-cache/scripts/npm_publish.sh +80 -0
  75. package/extensions/page-action-cache/skills/page-action-cache/SKILL.md +216 -0
  76. package/extensions/page-action-cache/src/actions-executor.ts +441 -0
  77. package/extensions/page-action-cache/src/cache-invalidator.ts +271 -0
  78. package/extensions/page-action-cache/src/cache-store.ts +457 -0
  79. package/extensions/page-action-cache/src/cache-strategy.ts +327 -0
  80. package/extensions/page-action-cache/src/hooks-entry.ts +114 -0
  81. package/extensions/page-action-cache/src/hooks.ts +332 -0
  82. package/extensions/page-action-cache/src/index.ts +104 -0
  83. package/extensions/page-action-cache/src/scenario-recognizer.ts +259 -0
  84. package/extensions/page-action-cache/src/security-policy.ts +268 -0
  85. package/extensions/page-action-cache/src/tools.ts +437 -0
  86. package/extensions/page-action-cache/src/types.ts +482 -0
  87. package/extensions/page-action-cache/src/ux-enhancer.ts +266 -0
  88. package/extensions/page-action-cache/src/variable-resolver.ts +258 -0
  89. package/extensions/page-action-cache/tests/actions-executor.test.ts +424 -0
  90. package/extensions/page-action-cache/tests/cache-store.test.ts +267 -0
  91. package/extensions/page-action-cache/tests/integration-test.ts +62 -0
  92. package/extensions/page-action-cache/tests/scenario-recognizer.test.ts +140 -0
  93. package/extensions/page-action-cache/tests/variable-resolver.test.ts +187 -0
  94. package/extensions/page-action-cache/tsconfig.json +39 -0
  95. package/package.json +1 -1
  96. package/scripts/create-instance.sh +26 -8
  97. package/scripts/npm_publish.sh +59 -1
  98. package/scripts/publish-extension.sh +343 -0
  99. package/ui/node_modules/.bin/vite +2 -2
  100. package/ui/node_modules/.bin/vitest +2 -2
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Hooks Integration
3
+ * Hooks 集成 - 注册 OpenClaw Hooks 来拦截和增强浏览器操作
4
+ */
5
+
6
+ import type { CacheConfig, PageViewport } from "./types.js";
7
+ import { getCacheStore } from "./cache-store.js";
8
+ import { getScenarioRecognizer } from "./scenario-recognizer.js";
9
+ import { getVariableResolver } from "./variable-resolver.js";
10
+
11
+ // ============================================================================
12
+ // 默认配置
13
+ // ============================================================================
14
+
15
+ const DEFAULT_CONFIG: CacheConfig = {
16
+ // === 全局开关 ===
17
+ enabled: true,
18
+ autoUseCache: true,
19
+
20
+ // === 场景识别 ===
21
+ scenarioRecognitionEnabled: true,
22
+ llmClassificationThreshold: 70,
23
+
24
+ // === 缓存策略 ===
25
+ cacheLevelStrategy: "auto",
26
+ defaultCacheLevel: "L3",
27
+
28
+ // === 缓存失效 ===
29
+ pageChangeDetectionEnabled: true,
30
+ changeInvalidationThreshold: 80,
31
+ invalidationStrategy: "soft",
32
+ maxVersionsPerEntry: 3,
33
+
34
+ // === 变量系统 ===
35
+ variableExtractionEnabled: true,
36
+ allowUserConfirmVariables: false,
37
+
38
+ // === 安全 ===
39
+ encryptSensitiveCache: false,
40
+ accessControlEnabled: false,
41
+ allowedUserIds: [],
42
+ logSanitizationEnabled: true,
43
+
44
+ // === 用户体验 ===
45
+ showCacheStatusToUser: true,
46
+ enableUserCacheConfirmation: false,
47
+ enableUserForcedRefresh: true,
48
+ enableUserCacheErrorReport: true,
49
+
50
+ // === 统计 ===
51
+ trackExecutionStats: true,
52
+ statsUpdateInterval: 60,
53
+ };
54
+
55
+ // ============================================================================
56
+ // Hooks 注册
57
+ // ============================================================================
58
+
59
+ /**
60
+ * 注册页面操作缓存扩展的 Hooks
61
+ */
62
+ export function registerPageActionCacheHooks(api: any, config?: Partial<CacheConfig>): void {
63
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
64
+
65
+ if (!finalConfig.enabled) {
66
+ console.log("[PageActionCache] Extension disabled");
67
+ return;
68
+ }
69
+
70
+ const cacheStore = getCacheStore();
71
+ const scenarioRecognizer = getScenarioRecognizer();
72
+ const variableResolver = getVariableResolver();
73
+ // UX enhancer for future use
74
+ // const uxEnhancer = getUXEnhancer();
75
+
76
+ // -------------------------------------------------------------------------
77
+ // before_tool_call hook - 场景识别和缓存拦截
78
+ // -------------------------------------------------------------------------
79
+
80
+ api.registerHook({
81
+ name: "before_tool_call",
82
+ priority: 100,
83
+ description: "页面操作缓存 - 场景识别和缓存拦截",
84
+
85
+ async handler(event: any, ctx: any) {
86
+ if (event.tool.name !== "browser") {
87
+ return { block: false };
88
+ }
89
+
90
+ const params = event.params;
91
+ const url = params.url as string;
92
+ const action = params.action as string;
93
+
94
+ // 只缓存特定操作
95
+ const cacheActions = ["navigate", "click", "type", "press", "hover"];
96
+ if (!cacheActions.includes(action)) {
97
+ return { block: false };
98
+ }
99
+
100
+ // 提取用户输入
101
+ const userInput = extractUserInput(ctx);
102
+ const currentUrl = url || ctx.currentUrl || "";
103
+
104
+ // 场景识别
105
+ if (finalConfig.scenarioRecognitionEnabled) {
106
+ const match = await scenarioRecognizer.recognize(userInput, currentUrl);
107
+
108
+ if (match && match.confidence >= 80) {
109
+ // 高置信度匹配,返回 cacheInfo
110
+ return {
111
+ block: false,
112
+ result: {
113
+ cacheInfo: {
114
+ exists: true,
115
+ scenario: match.scenario,
116
+ confidence: match.confidence,
117
+ method: match.method,
118
+ matchedPattern: match.matchedPattern,
119
+ },
120
+ },
121
+ };
122
+ }
123
+ }
124
+
125
+ // 检查缓存
126
+ const viewport = extractViewport(ctx, params);
127
+ const entry = cacheStore.get(url, viewport);
128
+
129
+ if (entry) {
130
+ const stats = cacheStore.getStats();
131
+ // L3 缓存命中,返回 cacheInfo
132
+ return {
133
+ block: false,
134
+ result: {
135
+ cacheInfo: {
136
+ exists: true,
137
+ cacheLevel: entry.cacheLevel,
138
+ scenario: entry.scenario,
139
+ actions: entry.actions,
140
+ variables: entry.variables,
141
+ cacheKey: entry.key,
142
+ },
143
+ stats: {
144
+ hitRate: stats.hitRate,
145
+ savedTokens: stats.savedTokens,
146
+ },
147
+ },
148
+ };
149
+ }
150
+
151
+ return { block: false };
152
+ },
153
+ });
154
+
155
+ // -------------------------------------------------------------------------
156
+ // after_tool_call hook - 保存 LLM 分析结果到缓存
157
+ // -------------------------------------------------------------------------
158
+
159
+ api.registerHook({
160
+ name: "after_tool_call",
161
+ priority: 100,
162
+ description: "页面操作缓存 - 保存 LLM 分析结果",
163
+
164
+ async handler(event: any, ctx: any) {
165
+ if (event.tool.name !== "browser") {
166
+ return;
167
+ }
168
+
169
+ const params = event.params;
170
+ const url = params.url as string;
171
+ const action = params.action as string;
172
+
173
+ // 只缓存特定操作
174
+ const cacheActions = ["navigate", "click", "type", "press", "hover"];
175
+ if (!cacheActions.includes(action)) {
176
+ return;
177
+ }
178
+
179
+ // 检查 LLM 是否返回了场景类型
180
+ const result = event.result;
181
+ const llmScenario = result.scenario as string | undefined;
182
+
183
+ if (!llmScenario) {
184
+ // 没有 LLM 标记的场景,不保存
185
+ return;
186
+ }
187
+
188
+ // 识别场景并获取变量
189
+ const userInput = extractUserInput(ctx);
190
+ const scenarioMatch = await scenarioRecognizer.recognize(userInput, url);
191
+
192
+ // 决定缓存层级
193
+ let cacheLevel: "L3" | "L2" | "L1" = "L3";
194
+ if (finalConfig.cacheLevelStrategy === "auto" && scenarioMatch) {
195
+ cacheLevel =
196
+ scenarioMatch.confidence >= 85
197
+ ? "L3"
198
+ : scenarioMatch.confidence >= 70
199
+ ? "L2"
200
+ : "L1";
201
+ } else {
202
+ cacheLevel = finalConfig.defaultCacheLevel as "L3" | "L2" | "L1";
203
+ }
204
+
205
+ // 解析变量
206
+ let variables: Record<string, string> | undefined;
207
+ if (finalConfig.variableExtractionEnabled && scenarioMatch) {
208
+ variables = variableResolver.resolveVariables(
209
+ userInput,
210
+ scenarioMatch.scenario
211
+ );
212
+ }
213
+
214
+ // 保存到缓存
215
+ const viewport = extractViewport(ctx, params);
216
+ const actions = result.actions || [];
217
+
218
+ if (actions.length > 0) {
219
+ cacheStore.set(url, viewport, actions, {
220
+ scenario: scenarioMatch?.scenario || llmScenario,
221
+ cacheLevel,
222
+ variables,
223
+ description: result.description || `Cached ${llmScenario} actions`,
224
+ pageType: result.pageType || "mixed",
225
+ });
226
+
227
+ console.log(
228
+ `[PageActionCache] Saved cache entry: ${scenarioMatch?.scenario || llmScenario} (${cacheLevel})`
229
+ );
230
+ }
231
+ },
232
+ });
233
+
234
+ // -------------------------------------------------------------------------
235
+ // before_prompt_build hook - 注入缓存使用指南
236
+ // -------------------------------------------------------------------------
237
+
238
+ api.registerHook({
239
+ name: "before_prompt_build",
240
+ priority: 50,
241
+ description: "页面操作缓存 - 注入缓存使用指南",
242
+
243
+ async handler(_event: any, _ctx: any) {
244
+ if (!finalConfig.autoUseCache) {
245
+ return;
246
+ }
247
+
248
+ const stats = cacheStore.getStats();
249
+
250
+ // 只在命中率较高时注入
251
+ if (stats.hitRate < 30) {
252
+ return;
253
+ }
254
+
255
+ // 构建缓存使用指南
256
+ const guide = `
257
+ 【页面操作缓存系统】
258
+
259
+ 当前缓存统计:
260
+ - 总条目数:${stats.totalEntries}
261
+ - 总命中:${stats.totalHits}
262
+ - 命中率:${stats.hitRate.toFixed(1)}%
263
+
264
+ 已缓存的场景:
265
+ ${cacheStore.getTopScenarios(5)}
266
+
267
+ 使用方式:
268
+ 1. 如果工具返回 cacheInfo 信息,优先使用 execute_cached 工具
269
+ 2. 执行缓存操作可以大幅降低 token 消耗和操作延迟
270
+ 3. 如果页面结构变化,缓存会自动失效
271
+
272
+ 支持的场景:login, logout, search, checkout, settings, form_fill, navigate, screenshot
273
+
274
+ 缓存层级:
275
+ - L3: 场景级缓存(包含变量模板)- 高置信度时直接使用
276
+ - L2: 流程级缓存 - 中置信度时使用
277
+ - L1: 原子级缓存 - 低置信度或无场景时使用
278
+
279
+ 可用的缓存工具:
280
+ - execute_cached: 执行缓存的操作序列
281
+ - cache_stats: 查看缓存统计
282
+ - cache_list: 列出所有缓存
283
+ - cache_clear: 清空缓存
284
+ - scenario_list: 列出所有场景
285
+ - force_refresh: 强制刷新指定缓存
286
+ `;
287
+
288
+ return { prependContext: guide };
289
+ },
290
+ });
291
+
292
+ console.log("[PageActionCache] Hooks registered successfully");
293
+ }
294
+
295
+ // ============================================================================
296
+ // 辅助函数
297
+ // ============================================================================
298
+
299
+ /**
300
+ * 从上下文提取用户输入
301
+ */
302
+ function extractUserInput(ctx: any): string {
303
+ // 尝试从上下文中提取用户输入
304
+ if (ctx.message?.content) {
305
+ return ctx.message.content;
306
+ }
307
+ if (ctx.userInput) {
308
+ return ctx.userInput;
309
+ }
310
+ if (ctx.prompt) {
311
+ return ctx.prompt;
312
+ }
313
+ return "";
314
+ }
315
+
316
+ /**
317
+ * 从上下文和参数提取视口尺寸
318
+ */
319
+ function extractViewport(ctx: any, params: any): PageViewport {
320
+ // 优先从参数获取
321
+ if (params.viewport) {
322
+ return params.viewport;
323
+ }
324
+
325
+ // 其次从上下文获取
326
+ if (ctx.viewport) {
327
+ return ctx.viewport;
328
+ }
329
+
330
+ // 默认值
331
+ return { width: 1920, height: 1080 };
332
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Page Action Cache Extension - Main Exports
3
+ * 页面操作缓存扩展 - 主要导出
4
+ */
5
+
6
+ // ============================================================================
7
+ // 核心模块导出
8
+ // ============================================================================
9
+
10
+ export { getCacheStore } from "./cache-store.js";
11
+ export { getScenarioRecognizer } from "./scenario-recognizer.js";
12
+ export { getVariableResolver } from "./variable-resolver.js";
13
+ export { getActionsExecutor } from "./actions-executor.js";
14
+ export { getSecurityPolicy } from "./security-policy.js";
15
+ export { getCacheInvalidator } from "./cache-invalidator.js";
16
+ export { getUXEnhancer } from "./ux-enhancer.js";
17
+
18
+ // ============================================================================
19
+ // 类型导出
20
+ // ============================================================================
21
+
22
+ export type {
23
+ AtomicAction,
24
+ Action,
25
+ AtomicActionType,
26
+ NavigateAction,
27
+ ClickAction,
28
+ TypeAction,
29
+ PressAction,
30
+ HoverAction,
31
+ ScreenshotAction,
32
+ EvaluateAction,
33
+ CompositeAction,
34
+ PageViewport,
35
+ VariableMap,
36
+ CacheLevel,
37
+ PageType,
38
+ CacheSource,
39
+ DOMHashFeatures,
40
+ PageChangeDetection,
41
+ PageActionCacheEntry,
42
+ ScenarioRule,
43
+ ScenarioMatch,
44
+ CacheStats,
45
+ CacheConfig,
46
+ ExecutionResult,
47
+ PwAi,
48
+ } from "./types.js";
49
+
50
+ // ============================================================================
51
+ // Hooks 导出
52
+ // ============================================================================
53
+
54
+ export { registerPageActionCacheHooks } from "./hooks.js";
55
+ export { initializePageActionCache, pageActionCacheMeta } from "./hooks-entry.js";
56
+
57
+ // ============================================================================
58
+ // Tools 导出
59
+ // ============================================================================
60
+
61
+ export {
62
+ createExecuteCachedTool,
63
+ executeCached,
64
+ } from "./tools.js";
65
+
66
+ export {
67
+ createCacheListTool,
68
+ cacheList,
69
+ } from "./tools.js";
70
+
71
+ export {
72
+ createCacheStatsTool,
73
+ cacheStats,
74
+ } from "./tools.js";
75
+
76
+ export {
77
+ createCacheClearTool,
78
+ cacheClear,
79
+ } from "./tools.js";
80
+
81
+ export {
82
+ createScenarioListTool,
83
+ scenarioList,
84
+ } from "./tools.js";
85
+
86
+ export {
87
+ createForceRefreshTool,
88
+ forceRefresh,
89
+ } from "./tools.js";
90
+
91
+ export { CACHE_TOOLS } from "./tools.js";
92
+
93
+ // ============================================================================
94
+ // Cache Strategy 导出
95
+ // ============================================================================
96
+
97
+ export {
98
+ AutoCacheStrategy,
99
+ L3OnlyCacheStrategy,
100
+ L2OnlyCacheStrategy,
101
+ L1OnlyCacheStrategy,
102
+ } from "./cache-strategy.js";
103
+
104
+ export type { CacheStrategy } from "./cache-strategy.js";
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Scenario Recognizer
3
+ * 场景识别器 - 多层匹配策略
4
+ */
5
+
6
+ import type {
7
+ ScenarioRule,
8
+ ScenarioMatch,
9
+ } from "./types.js";
10
+ import { getCacheStore } from "./cache-store.js";
11
+
12
+ // ============================================================================
13
+ // 精确场景规则
14
+ // ============================================================================
15
+
16
+ const EXACT_SCENARIO_RULES: ScenarioRule[] = [
17
+ {
18
+ scenario: "login",
19
+ keywords: ["登录", "登陆", "注册", "进入", "login to", "sign in", "账号"],
20
+ urlPatterns: ["/login", "/signin", "/auth", "/account"],
21
+ priority: 100,
22
+ cacheLevel: "L3",
23
+ confidence: 90,
24
+ },
25
+ {
26
+ scenario: "logout",
27
+ keywords: ["退出", "登出", "注销", "sign out", "logout"],
28
+ urlPatterns: ["/logout", "/signout", "/signout"],
29
+ priority: 100,
30
+ cacheLevel: "L3",
31
+ confidence: 90,
32
+ },
33
+ {
34
+ scenario: "search",
35
+ keywords: ["搜索", "查找", "找", "search for", "查询"],
36
+ urlPatterns: ["/search", "/query", "/list"],
37
+ priority: 80,
38
+ cacheLevel: "L3",
39
+ confidence: 80,
40
+ },
41
+ {
42
+ scenario: "checkout",
43
+ keywords: ["结账", "结算", "支付", "收银", "买单", "checkout"],
44
+ urlPatterns: ["/checkout", "/cart", "/payment"],
45
+ priority: 90,
46
+ cacheLevel: "L3",
47
+ confidence: 85,
48
+ },
49
+ {
50
+ scenario: "settings",
51
+ keywords: ["设置", "配置", "修改", "编辑", "个人中心"],
52
+ urlPatterns: ["/settings", "/config", "/profile"],
53
+ priority: 75,
54
+ cacheLevel: "L3",
55
+ confidence: 75,
56
+ },
57
+ {
58
+ scenario: "form_fill",
59
+ keywords: ["填写", "填表单", "提交", "submit form"],
60
+ urlPatterns: [], // 不依赖 URL
61
+ priority: 70,
62
+ cacheLevel: "L2",
63
+ confidence: 70,
64
+ },
65
+ {
66
+ scenario: "navigate",
67
+ keywords: ["打开", "进入", "跳转", "go to", "open", "navigate"],
68
+ urlPatterns: [],
69
+ priority: 60,
70
+ cacheLevel: "L2",
71
+ confidence: 60,
72
+ },
73
+ {
74
+ scenario: "screenshot",
75
+ keywords: ["截图", "截图", "snapshot", "screenshot"],
76
+ urlPatterns: [],
77
+ priority: 50,
78
+ cacheLevel: "L1",
79
+ confidence: 50,
80
+ },
81
+ ];
82
+
83
+ // ============================================================================
84
+ // Scenario Recognizer 类
85
+ // ============================================================================
86
+
87
+ /**
88
+ * 场景识别器
89
+ */
90
+ export class ScenarioRecognizer {
91
+ private cacheStore = getCacheStore();
92
+
93
+ /**
94
+ * 多层匹配策略
95
+ */
96
+ async recognize(
97
+ userInput: string,
98
+ currentUrl: string
99
+ ): Promise<ScenarioMatch | null> {
100
+ // 1. 精确规则匹配(高优先级,低误判)
101
+ const exactMatch = this.matchExactRules(userInput, currentUrl);
102
+ if (exactMatch && exactMatch.confidence >= 90) {
103
+ return exactMatch;
104
+ }
105
+
106
+ // 2. 语义理解(使用 LLM)
107
+ const llmMatch = await this.matchWithLLM(userInput, currentUrl);
108
+ if (llmMatch && llmMatch.confidence >= 70) {
109
+ return llmMatch;
110
+ }
111
+
112
+ // 3. 历史学习(相似输入匹配)
113
+ const historyMatch = this.matchHistory(userInput);
114
+ if (historyMatch && historyMatch.confidence >= 60) {
115
+ return historyMatch;
116
+ }
117
+
118
+ return null; // 都不匹配,交给 LLM 分析
119
+ }
120
+
121
+ /**
122
+ * 精确规则匹配
123
+ */
124
+ private matchExactRules(
125
+ userInput: string,
126
+ url: string
127
+ ): ScenarioMatch | null {
128
+ const normalizedInput = userInput.toLowerCase();
129
+ const normalizedUrl = url.toLowerCase();
130
+
131
+ for (const rule of EXACT_SCENARIO_RULES) {
132
+ // 关键词匹配
133
+ for (const keyword of rule.keywords) {
134
+ if (normalizedInput.includes(keyword.toLowerCase())) {
135
+ return {
136
+ scenario: rule.scenario,
137
+ confidence: rule.priority as number,
138
+ method: "keyword",
139
+ matchedPattern: keyword,
140
+ } as ScenarioMatch;
141
+ }
142
+ }
143
+
144
+ // URL 模式匹配
145
+ for (const pattern of rule.urlPatterns) {
146
+ if (normalizedUrl.includes(pattern.toLowerCase())) {
147
+ return {
148
+ scenario: rule.scenario,
149
+ confidence: rule.priority - 10, // 略低于纯关键词
150
+ method: "url",
151
+ matchedPattern: pattern,
152
+ };
153
+ }
154
+ }
155
+ }
156
+
157
+ return null;
158
+ }
159
+
160
+ /**
161
+ * LLM 分类
162
+ */
163
+ private async matchWithLLM(
164
+ _userInput: string,
165
+ _url: string
166
+ ): Promise<ScenarioMatch | null> {
167
+ // TODO: 实现 LLM 分类
168
+ // 使用 OpenClaw 的 LLM 进行意图分类
169
+ // 返回场景类型和置信度
170
+
171
+ // 暂时返回 null,等待 LLM 集成
172
+ return null;
173
+ }
174
+
175
+ /**
176
+ * 历史匹配
177
+ */
178
+ private matchHistory(userInput: string): ScenarioMatch | null {
179
+ const stats = this.cacheStore.getStats();
180
+
181
+ // 如果命中率太低,不做历史匹配
182
+ if (stats.hitRate < 30) {
183
+ return null;
184
+ }
185
+
186
+ // 在历史记录中查找相似输入
187
+ // 计算 Jaccard 相似度
188
+ const scenarioMatch = this.cacheStore.getScenarioMatch("search"); // 示例
189
+
190
+ if (scenarioMatch) {
191
+ const similarity = this.calculateJaccardSimilarity(
192
+ userInput,
193
+ scenarioMatch.scenario
194
+ );
195
+
196
+ if (similarity >= 0.6) {
197
+ return {
198
+ scenario: scenarioMatch.scenario,
199
+ confidence: Math.round(similarity * 100),
200
+ method: "history",
201
+ };
202
+ }
203
+ }
204
+
205
+ return null;
206
+ }
207
+
208
+ /**
209
+ * 计算 Jaccard 相似度
210
+ */
211
+ private calculateJaccardSimilarity(str1: string, str2: string): number {
212
+ const set1 = new Set(str1.toLowerCase().split(/\s+/));
213
+ const set2 = new Set(str2.toLowerCase().split(/\s+/));
214
+
215
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
216
+ const union = new Set([...set1, ...set2]);
217
+
218
+ return intersection.size / union.size;
219
+ }
220
+
221
+ // -------------------------------------------------------------------------
222
+ // 辅助方法
223
+ // -------------------------------------------------------------------------
224
+
225
+ /**
226
+ * 获取所有规则
227
+ */
228
+ getRules(): ScenarioRule[] {
229
+ return [...EXACT_SCENARIO_RULES];
230
+ }
231
+
232
+ /**
233
+ * 添加自定义规则
234
+ */
235
+ addRule(rule: ScenarioRule): void {
236
+ EXACT_SCENARIO_RULES.push(rule);
237
+ }
238
+
239
+ /**
240
+ * 获取建议的缓存层级
241
+ */
242
+ getRecommendedCacheLevel(scenario: string): "L3" | "L2" | "L1" {
243
+ const rule = EXACT_SCENARIO_RULES.find((r) => r.scenario === scenario);
244
+ return rule?.cacheLevel || "L1";
245
+ }
246
+ }
247
+
248
+ // ============================================================================
249
+ // 单例
250
+ // ============================================================================
251
+
252
+ let scenarioRecognizerInstance: ScenarioRecognizer | null = null;
253
+
254
+ export function getScenarioRecognizer(): ScenarioRecognizer {
255
+ if (!scenarioRecognizerInstance) {
256
+ scenarioRecognizerInstance = new ScenarioRecognizer();
257
+ }
258
+ return scenarioRecognizerInstance;
259
+ }