agentdev 0.1.7 → 0.1.9

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 (188) hide show
  1. package/dist/BasicAgent-UWXLSZP2.js +13 -0
  2. package/dist/ExplorerAgent-LCM3JQS4.js +13 -0
  3. package/dist/{chunk-LQTEETML.js → chunk-5T4C2XRT.js} +12 -7
  4. package/dist/chunk-5T4C2XRT.js.map +1 -0
  5. package/dist/{chunk-OBOU27DM.js → chunk-A354ZCZF.js} +6735 -1822
  6. package/dist/chunk-A354ZCZF.js.map +1 -0
  7. package/dist/{chunk-TSASFMRF.js → chunk-BAP2GCYH.js} +1 -1
  8. package/dist/chunk-BAP2GCYH.js.map +1 -0
  9. package/dist/{chunk-3BPSNNK3.js → chunk-EECW6PYP.js} +11 -9
  10. package/dist/chunk-EECW6PYP.js.map +1 -0
  11. package/dist/chunk-G5ECPY4K.js +551 -0
  12. package/dist/chunk-G5ECPY4K.js.map +1 -0
  13. package/dist/{chunk-LLV3W326.js → chunk-NORTAQIL.js} +67 -20
  14. package/dist/chunk-NORTAQIL.js.map +1 -0
  15. package/dist/{chunk-F3PR7UTL.js → chunk-QFHPUAUQ.js} +5 -3
  16. package/dist/{chunk-F3PR7UTL.js.map → chunk-QFHPUAUQ.js.map} +1 -1
  17. package/dist/cli/server.js +2 -2
  18. package/dist/cli/viewer.js +2 -2
  19. package/dist/create-feature-cli.cmd +2 -0
  20. package/dist/create-feature-cli.js +292 -0
  21. package/dist/features/shell/templates/bash.render.d.ts +1 -1
  22. package/dist/features/websearch/templates/web-fetch.render.d.ts +1 -1
  23. package/dist/index.d.ts +1733 -500
  24. package/dist/index.js +30 -8
  25. package/dist/index.js.map +1 -1
  26. package/dist/{notification-3VEAP7YF.js → notification-NWVOS2WR.js} +3 -3
  27. package/dist/tools-LDR3LIJP.js +14 -0
  28. package/dist/tools-LDR3LIJP.js.map +1 -0
  29. package/dist/{types-DUKIIntb.d.ts → types-CF5UsxD9.d.ts} +3 -0
  30. package/node_modules/@sliverp/qqbot/LICENSE +21 -0
  31. package/node_modules/@sliverp/qqbot/README.md +427 -0
  32. package/node_modules/@sliverp/qqbot/README.standalone.zh.md +77 -0
  33. package/node_modules/@sliverp/qqbot/README.zh.md +423 -0
  34. package/node_modules/@sliverp/qqbot/bin/qqbot-cli.js +227 -0
  35. package/node_modules/@sliverp/qqbot/clawdbot.plugin.json +16 -0
  36. package/node_modules/@sliverp/qqbot/dist/index.d.ts +20 -0
  37. package/node_modules/@sliverp/qqbot/dist/index.js +25 -0
  38. package/node_modules/@sliverp/qqbot/dist/src/agent.d.ts +76 -0
  39. package/node_modules/@sliverp/qqbot/dist/src/agent.js +36 -0
  40. package/node_modules/@sliverp/qqbot/dist/src/api.d.ts +138 -0
  41. package/node_modules/@sliverp/qqbot/dist/src/api.js +523 -0
  42. package/node_modules/@sliverp/qqbot/dist/src/channel.d.ts +3 -0
  43. package/node_modules/@sliverp/qqbot/dist/src/channel.js +349 -0
  44. package/node_modules/@sliverp/qqbot/dist/src/config.d.ts +25 -0
  45. package/node_modules/@sliverp/qqbot/dist/src/config.js +156 -0
  46. package/node_modules/@sliverp/qqbot/dist/src/demo-standalone.d.ts +1 -0
  47. package/node_modules/@sliverp/qqbot/dist/src/demo-standalone.js +125 -0
  48. package/node_modules/@sliverp/qqbot/dist/src/gateway.d.ts +20 -0
  49. package/node_modules/@sliverp/qqbot/dist/src/gateway.js +2156 -0
  50. package/node_modules/@sliverp/qqbot/dist/src/image-server.d.ts +62 -0
  51. package/node_modules/@sliverp/qqbot/dist/src/image-server.js +401 -0
  52. package/node_modules/@sliverp/qqbot/dist/src/known-users.d.ts +100 -0
  53. package/node_modules/@sliverp/qqbot/dist/src/known-users.js +263 -0
  54. package/node_modules/@sliverp/qqbot/dist/src/onboarding.d.ts +10 -0
  55. package/node_modules/@sliverp/qqbot/dist/src/onboarding.js +203 -0
  56. package/node_modules/@sliverp/qqbot/dist/src/openclaw-agent-adapter.d.ts +2 -0
  57. package/node_modules/@sliverp/qqbot/dist/src/openclaw-agent-adapter.js +155 -0
  58. package/node_modules/@sliverp/qqbot/dist/src/outbound.d.ts +150 -0
  59. package/node_modules/@sliverp/qqbot/dist/src/outbound.js +1175 -0
  60. package/node_modules/@sliverp/qqbot/dist/src/proactive.d.ts +170 -0
  61. package/node_modules/@sliverp/qqbot/dist/src/proactive.js +399 -0
  62. package/node_modules/@sliverp/qqbot/dist/src/runtime.d.ts +5 -0
  63. package/node_modules/@sliverp/qqbot/dist/src/runtime.js +16 -0
  64. package/node_modules/@sliverp/qqbot/dist/src/session-store.d.ts +52 -0
  65. package/node_modules/@sliverp/qqbot/dist/src/session-store.js +254 -0
  66. package/node_modules/@sliverp/qqbot/dist/src/types.d.ts +145 -0
  67. package/node_modules/@sliverp/qqbot/dist/src/types.js +1 -0
  68. package/node_modules/@sliverp/qqbot/dist/src/utils/audio-convert.d.ts +73 -0
  69. package/node_modules/@sliverp/qqbot/dist/src/utils/audio-convert.js +645 -0
  70. package/node_modules/@sliverp/qqbot/dist/src/utils/file-utils.d.ts +46 -0
  71. package/node_modules/@sliverp/qqbot/dist/src/utils/file-utils.js +107 -0
  72. package/node_modules/@sliverp/qqbot/dist/src/utils/image-size.d.ts +51 -0
  73. package/node_modules/@sliverp/qqbot/dist/src/utils/image-size.js +234 -0
  74. package/node_modules/@sliverp/qqbot/dist/src/utils/media-tags.d.ts +14 -0
  75. package/node_modules/@sliverp/qqbot/dist/src/utils/media-tags.js +120 -0
  76. package/node_modules/@sliverp/qqbot/dist/src/utils/payload.d.ts +112 -0
  77. package/node_modules/@sliverp/qqbot/dist/src/utils/payload.js +186 -0
  78. package/node_modules/@sliverp/qqbot/dist/src/utils/platform.d.ts +126 -0
  79. package/node_modules/@sliverp/qqbot/dist/src/utils/platform.js +358 -0
  80. package/node_modules/@sliverp/qqbot/dist/src/utils/upload-cache.d.ts +34 -0
  81. package/node_modules/@sliverp/qqbot/dist/src/utils/upload-cache.js +93 -0
  82. package/node_modules/@sliverp/qqbot/dist/standalone.d.ts +6 -0
  83. package/node_modules/@sliverp/qqbot/dist/standalone.js +6 -0
  84. package/node_modules/@sliverp/qqbot/index.ts +30 -0
  85. package/node_modules/@sliverp/qqbot/moltbot.plugin.json +16 -0
  86. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/LICENSE +201 -0
  87. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/README.md +134 -0
  88. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/browser.js +17 -0
  89. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
  90. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
  91. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/index.d.ts +4 -0
  92. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/node.js +223 -0
  93. package/node_modules/@sliverp/qqbot/node_modules/@eshaz/web-worker/package.json +54 -0
  94. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/index.js +5 -0
  95. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/package.json +36 -0
  96. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
  97. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
  98. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
  99. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
  100. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
  101. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
  102. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
  103. package/node_modules/@sliverp/qqbot/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
  104. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/README.md +265 -0
  105. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
  106. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
  107. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/index.js +8 -0
  108. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/package.json +58 -0
  109. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
  110. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
  111. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
  112. package/node_modules/@sliverp/qqbot/node_modules/mpg123-decoder/types.d.ts +30 -0
  113. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/LICENSE +21 -0
  114. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/README.md +85 -0
  115. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/lib/index.cjs +16 -0
  116. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/lib/index.d.ts +70 -0
  117. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/lib/index.mjs +16 -0
  118. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/lib/silk.wasm +0 -0
  119. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/lib/utils.d.ts +4 -0
  120. package/node_modules/@sliverp/qqbot/node_modules/silk-wasm/package.json +39 -0
  121. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
  122. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/.prettierignore +1 -0
  123. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/LICENSE +7 -0
  124. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/README.md +163 -0
  125. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/dist/esm.js +1 -0
  126. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/dist/index.js +1 -0
  127. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/package.json +50 -0
  128. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/rollup.config.js +27 -0
  129. package/node_modules/@sliverp/qqbot/node_modules/simple-yenc/src/simple-yenc.js +302 -0
  130. package/node_modules/@sliverp/qqbot/node_modules/ws/LICENSE +20 -0
  131. package/node_modules/@sliverp/qqbot/node_modules/ws/README.md +548 -0
  132. package/node_modules/@sliverp/qqbot/node_modules/ws/browser.js +8 -0
  133. package/node_modules/@sliverp/qqbot/node_modules/ws/index.js +13 -0
  134. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/buffer-util.js +131 -0
  135. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/constants.js +19 -0
  136. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/event-target.js +292 -0
  137. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/extension.js +203 -0
  138. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/limiter.js +55 -0
  139. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/permessage-deflate.js +528 -0
  140. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/receiver.js +706 -0
  141. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/sender.js +602 -0
  142. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/stream.js +161 -0
  143. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/subprotocol.js +62 -0
  144. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/validation.js +152 -0
  145. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/websocket-server.js +554 -0
  146. package/node_modules/@sliverp/qqbot/node_modules/ws/lib/websocket.js +1393 -0
  147. package/node_modules/@sliverp/qqbot/node_modules/ws/package.json +69 -0
  148. package/node_modules/@sliverp/qqbot/node_modules/ws/wrapper.mjs +8 -0
  149. package/node_modules/@sliverp/qqbot/openclaw.plugin.json +16 -0
  150. package/node_modules/@sliverp/qqbot/package.json +81 -0
  151. package/node_modules/@sliverp/qqbot/skills/qqbot-cron/SKILL.md +513 -0
  152. package/node_modules/@sliverp/qqbot/skills/qqbot-media/SKILL.md +194 -0
  153. package/node_modules/@sliverp/qqbot/src/agent.ts +133 -0
  154. package/node_modules/@sliverp/qqbot/src/api.ts +704 -0
  155. package/node_modules/@sliverp/qqbot/src/channel.ts +380 -0
  156. package/node_modules/@sliverp/qqbot/src/config.ts +182 -0
  157. package/node_modules/@sliverp/qqbot/src/demo-standalone.ts +144 -0
  158. package/node_modules/@sliverp/qqbot/src/gateway.ts +2285 -0
  159. package/node_modules/@sliverp/qqbot/src/image-server.ts +474 -0
  160. package/node_modules/@sliverp/qqbot/src/known-users.ts +353 -0
  161. package/node_modules/@sliverp/qqbot/src/onboarding.ts +274 -0
  162. package/node_modules/@sliverp/qqbot/src/openclaw-agent-adapter.ts +168 -0
  163. package/node_modules/@sliverp/qqbot/src/openclaw-plugin-sdk.d.ts +483 -0
  164. package/node_modules/@sliverp/qqbot/src/outbound.ts +1301 -0
  165. package/node_modules/@sliverp/qqbot/src/proactive.ts +530 -0
  166. package/node_modules/@sliverp/qqbot/src/runtime.ts +22 -0
  167. package/node_modules/@sliverp/qqbot/src/session-store.ts +303 -0
  168. package/node_modules/@sliverp/qqbot/src/types.ts +153 -0
  169. package/node_modules/@sliverp/qqbot/src/utils/audio-convert.ts +738 -0
  170. package/node_modules/@sliverp/qqbot/src/utils/file-utils.ts +122 -0
  171. package/node_modules/@sliverp/qqbot/src/utils/image-size.ts +266 -0
  172. package/node_modules/@sliverp/qqbot/src/utils/media-tags.ts +134 -0
  173. package/node_modules/@sliverp/qqbot/src/utils/payload.ts +265 -0
  174. package/node_modules/@sliverp/qqbot/src/utils/platform.ts +404 -0
  175. package/node_modules/@sliverp/qqbot/src/utils/upload-cache.ts +128 -0
  176. package/node_modules/@sliverp/qqbot/standalone.ts +6 -0
  177. package/node_modules/@sliverp/qqbot/tsconfig.json +16 -0
  178. package/package.json +12 -3
  179. package/dist/BasicAgent-QWEYCLV5.js +0 -12
  180. package/dist/ExplorerAgent-4IT22VB7.js +0 -12
  181. package/dist/chunk-3BPSNNK3.js.map +0 -1
  182. package/dist/chunk-LLV3W326.js.map +0 -1
  183. package/dist/chunk-LQTEETML.js.map +0 -1
  184. package/dist/chunk-OBOU27DM.js.map +0 -1
  185. package/dist/chunk-TSASFMRF.js.map +0 -1
  186. /package/dist/{BasicAgent-QWEYCLV5.js.map → BasicAgent-UWXLSZP2.js.map} +0 -0
  187. /package/dist/{ExplorerAgent-4IT22VB7.js.map → ExplorerAgent-LCM3JQS4.js.map} +0 -0
  188. /package/dist/{notification-3VEAP7YF.js.map → notification-NWVOS2WR.js.map} +0 -0
@@ -0,0 +1,551 @@
1
+ // src/features/visual/tools.ts
2
+ import { spawn } from "child_process";
3
+ import { readFileSync } from "fs";
4
+ import { fileURLToPath } from "url";
5
+ import { dirname, join } from "path";
6
+
7
+ // src/core/tool.ts
8
+ function createTool(config, sourceFile) {
9
+ let finalRender = void 0;
10
+ if (config.render) {
11
+ if (typeof config.render === "string") {
12
+ finalRender = {
13
+ call: config.render,
14
+ result: config.render
15
+ };
16
+ } else {
17
+ finalRender = config.render;
18
+ }
19
+ } else if (sourceFile) {
20
+ const renderPath = sourceFile.replace(/\.ts$/, ".render.ts");
21
+ finalRender = {
22
+ __renderPath: renderPath,
23
+ call: void 0,
24
+ result: void 0
25
+ };
26
+ }
27
+ return {
28
+ name: config.name,
29
+ description: config.description,
30
+ parameters: config.parameters,
31
+ execute: config.execute,
32
+ render: finalRender
33
+ };
34
+ }
35
+ var ToolRegistry = class {
36
+ tools = /* @__PURE__ */ new Map();
37
+ enabled = /* @__PURE__ */ new Set();
38
+ // 启用的工具名
39
+ pendingDisabled = /* @__PURE__ */ new Set();
40
+ // 工具注册前的预禁用状态
41
+ sources = /* @__PURE__ */ new Map();
42
+ // 工具来源追踪
43
+ /**
44
+ * 注册工具(默认启用,记录来源)
45
+ */
46
+ register(tool, source) {
47
+ this.tools.set(tool.name, tool);
48
+ if (this.pendingDisabled.has(tool.name)) {
49
+ this.enabled.delete(tool.name);
50
+ } else {
51
+ this.enabled.add(tool.name);
52
+ }
53
+ if (source) {
54
+ this.sources.set(tool.name, source);
55
+ }
56
+ return this;
57
+ }
58
+ /**
59
+ * 禁用工具
60
+ */
61
+ disable(name) {
62
+ this.pendingDisabled.add(name);
63
+ if (!this.tools.has(name)) {
64
+ return true;
65
+ }
66
+ return this.enabled.delete(name);
67
+ }
68
+ /**
69
+ * 启用工具
70
+ */
71
+ enable(name) {
72
+ this.pendingDisabled.delete(name);
73
+ if (this.tools.has(name)) {
74
+ this.enabled.add(name);
75
+ return true;
76
+ }
77
+ return false;
78
+ }
79
+ /**
80
+ * 检查工具是否启用
81
+ */
82
+ isEnabled(name) {
83
+ return this.enabled.has(name);
84
+ }
85
+ /**
86
+ * 获取工具来源(调试用)
87
+ */
88
+ getSource(name) {
89
+ return this.sources.get(name);
90
+ }
91
+ /**
92
+ * 获取工具条目(调试快照用)
93
+ */
94
+ getEntries() {
95
+ return Array.from(this.tools.entries()).map(([name, tool]) => ({
96
+ tool,
97
+ enabled: this.enabled.has(name),
98
+ source: this.sources.get(name)
99
+ }));
100
+ }
101
+ /**
102
+ * 获取工具
103
+ */
104
+ get(name) {
105
+ return this.tools.get(name);
106
+ }
107
+ /**
108
+ * 获取所有工具(只返回启用的)
109
+ */
110
+ getAll() {
111
+ return Array.from(this.enabled).map((name) => this.tools.get(name)).filter((t) => t !== void 0);
112
+ }
113
+ /**
114
+ * 检查工具是否存在
115
+ */
116
+ has(name) {
117
+ return this.tools.has(name);
118
+ }
119
+ /**
120
+ * 获取工具的渲染配置
121
+ */
122
+ getRenderConfig(name) {
123
+ const tool = this.tools.get(name);
124
+ return tool?.render;
125
+ }
126
+ };
127
+
128
+ // src/features/visual/tools.ts
129
+ var __dirname = dirname(fileURLToPath(import.meta.url));
130
+ var VISION_SYSTEM_PROMPT = `# Role
131
+ \u4F60\u662F\u4E00\u4F4D\u4E13\u4E1A\u7684\u89C6\u89C9\u5185\u5BB9\u5206\u6790\u4E13\u5BB6\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u4ED4\u7EC6\u89C2\u5BDF\u7528\u6237\u63D0\u4F9B\u7684\u622A\u56FE\uFF0C\u5E76\u7ED9\u51FA\u51C6\u786E\u3001\u8BE6\u7EC6\u7684\u5185\u5BB9\u63CF\u8FF0\u3002
132
+
133
+ # Task
134
+ \u5206\u6790\u622A\u56FE\u4E2D\u7684\u89C6\u89C9\u5185\u5BB9\uFF0C\u91CD\u70B9\u63CF\u8FF0\uFF1A
135
+ 1. **\u4E3B\u8981\u5185\u5BB9**\uFF1A\u622A\u56FE\u663E\u793A\u7684\u662F\u4EC0\u4E48\u5E94\u7528\u3001\u4EC0\u4E48\u754C\u9762
136
+ 2. **\u6587\u672C\u5185\u5BB9**\uFF1A\u6240\u6709\u53EF\u89C1\u7684\u6587\u5B57\u3001\u6807\u7B7E\u3001\u63D0\u793A\u4FE1\u606F
137
+ 3. **\u5173\u952E\u5143\u7D20**\uFF1A\u91CD\u8981\u7684\u6309\u94AE\u3001\u8F93\u5165\u6846\u3001\u83DC\u5355\u9879\u7B49\u53EF\u4EA4\u4E92\u5143\u7D20
138
+ 4. **\u72B6\u6001\u4FE1\u606F**\uFF1A\u5F53\u524D\u754C\u9762\u72B6\u6001\uFF08\u5982\uFF1A\u9009\u4E2D/\u672A\u9009\u4E2D\u3001\u5C55\u5F00/\u6536\u8D77\u7B49\uFF09
139
+ 5. **\u5F02\u5E38\u6216\u7279\u6B8A\u70B9**\uFF1A\u9519\u8BEF\u63D0\u793A\u3001\u8B66\u544A\u4FE1\u606F\u3001\u5F39\u7A97\u7B49
140
+
141
+ # \u589E\u91CF\u5206\u6790\u7B56\u7565
142
+ \u4F60\u4F1A\u6536\u5230\u7A97\u53E3\u4E0A\u4E0B\u6587\u4FE1\u606F\uFF08\u8FDB\u7A0B\u540D\u3001\u7A97\u53E3\u6807\u9898\uFF09\u548C\u4E0A\u6B21\u7684\u8BC6\u522B\u7ED3\u679C\u3002\u8BF7\u6839\u636E\u8FD9\u4E9B\u4FE1\u606F\u8FDB\u884C\u589E\u91CF\u5206\u6790\uFF1A
143
+
144
+ ## \u60C5\u51B5 1\uFF1A\u5B8C\u5168\u4E0D\u540C\u7684\u9875\u9762
145
+ \u5982\u679C\u65B0\u622A\u56FE\u663E\u793A\u7684\u9875\u9762\u4E0E\u4E0A\u6B21\u8BC6\u522B\u7ED3\u679C\u5B8C\u5168\u4E0D\u540C\uFF08\u4F8B\u5982\uFF1A\u4ECE\u4EE3\u7801\u7F16\u8F91\u5668\u5207\u6362\u5230\u6D4F\u89C8\u5668\uFF09\uFF0C\u8BF7\u76F4\u63A5\u63CF\u8FF0\u65B0\u9875\u9762\u7684\u5B8C\u6574\u5185\u5BB9\u3002
146
+
147
+ ## \u60C5\u51B5 2\uFF1A\u76F8\u540C\u9875\u9762\uFF0C\u89C6\u89D2\u53D8\u5316
148
+ \u5982\u679C\u65B0\u622A\u56FE\u4E0E\u4E0A\u6B21\u662F\u540C\u4E00\u4E2A\u9875\u9762\uFF0C\u4F46\u89C6\u89D2\u53D1\u751F\u4E86\u53D8\u5316\uFF08\u4F8B\u5982\uFF1A\u6EDA\u52A8\u5230\u4E86\u65B0\u4F4D\u7F6E\u3001\u5C55\u5F00\u4E86\u65B0\u7684\u83DC\u5355\u3001\u5207\u6362\u4E86\u6807\u7B7E\u9875\uFF09\uFF0C\u8BF7\u53EA\u63CF\u8FF0**\u65B0\u770B\u5230\u7684\u5185\u5BB9**\uFF0C\u4E0D\u9700\u8981\u91CD\u590D\u63CF\u8FF0\u4E4B\u524D\u5DF2\u7ECF\u8BC6\u522B\u8FC7\u7684\u5185\u5BB9\u3002
149
+
150
+ ## \u60C5\u51B5 3\uFF1A\u76F8\u540C\u9875\u9762\uFF0C\u66F4\u6B63\u4E4B\u524D\u7684\u5185\u5BB9
151
+ \u5982\u679C\u65B0\u622A\u56FE\u4E0E\u4E0A\u6B21\u662F\u540C\u4E00\u4E2A\u9875\u9762\uFF0C\u4F46\u4F60\u53D1\u73B0\u4E4B\u524D\u7684\u8BC6\u522B\u6709\u9519\u8BEF\u6216\u9057\u6F0F\uFF0C\u8BF7\u76F4\u63A5\u66F4\u6B63\u6216\u8865\u5145\u3002\u4F8B\u5982\uFF1A
152
+ - "\u66F4\u6B63\uFF1A\u4E4B\u524D\u8BC6\u522B\u4E3A'\u786E\u8BA4'\u7684\u6309\u94AE\u5B9E\u9645\u662F'\u53D6\u6D88'"
153
+ - "\u8865\u5145\uFF1A\u9875\u9762\u4E0A\u65B9\u8FD8\u6709\u4E00\u4E2A\u641C\u7D22\u680F\uFF0C\u8F93\u5165\u6846\u663E\u793A'\u8BF7\u8F93\u5165\u5173\u952E\u8BCD'"
154
+
155
+ # Output Requirements
156
+ - \u4F7F\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\uFF0C\u4E0D\u8981\u4F7F\u7528 JSON \u6216\u5176\u4ED6\u7ED3\u6784\u5316\u683C\u5F0F
157
+ - \u63CF\u8FF0\u8981\u51C6\u786E\u3001\u6E05\u6670\uFF0C\u4FBF\u4E8E\u540E\u7EED\u5904\u7406
158
+ - \u5982\u679C\u56FE\u7247\u4E0D\u6E05\u6670\u6216\u65E0\u6CD5\u7406\u89E3\uFF0C\u76F4\u63A5\u8BF4\u660E
159
+ - \u8F93\u51FA\u8BED\u8A00\u4E0E\u622A\u56FE\u4E2D\u7684\u4E3B\u8981\u8BED\u8A00\u4FDD\u6301\u4E00\u81F4
160
+
161
+ # Example Output (\u5B8C\u5168\u4E0D\u540C\u7684\u9875\u9762)
162
+ \u8FD9\u662F\u4E00\u4E2A\u6D4F\u89C8\u5668\u7A97\u53E3\uFF0C\u663E\u793A\u7684\u662F\u4E00\u4E2A\u8D2D\u7269\u7F51\u7AD9\u3002\u9876\u90E8\u6709\u5BFC\u822A\u680F\uFF0C\u663E\u793A"\u9996\u9875\u3001\u5206\u7C7B\u3001\u8D2D\u7269\u8F66\u3001\u6211\u7684"\u3002\u4E2D\u95F4\u90E8\u5206\u662F\u5546\u54C1\u5217\u8868\uFF0C\u6BCF\u4E2A\u5546\u54C1\u5361\u7247\u5305\u542B\u56FE\u7247\u3001\u540D\u79F0\u3001\u4EF7\u683C\u548C"\u52A0\u5165\u8D2D\u7269\u8F66"\u6309\u94AE\u3002\u5F53\u524D\u663E\u793A\u7684\u662F"\u7535\u5B50\u4EA7\u54C1"\u5206\u7C7B\u3002
163
+
164
+ # Example Output (\u76F8\u540C\u9875\u9762\uFF0C\u89C6\u89D2\u53D8\u5316)
165
+ \u9875\u9762\u5411\u4E0B\u6EDA\u52A8\u540E\uFF0C\u663E\u793A\u4E86\u66F4\u591A\u7684\u5546\u54C1\u3002\u65B0\u770B\u5230\u7684\u5546\u54C1\u5305\u62EC\uFF1A\u4E00\u6B3E\u65E0\u7EBF\u9F20\u6807\uFF08\u4EF7\u683C\uFF1A199\u5143\uFF09\u3001\u4E00\u4E2A\u673A\u68B0\u952E\u76D8\uFF08\u4EF7\u683C\uFF1A399\u5143\uFF09\u548C\u4E00\u4E2A\u663E\u793A\u5668\uFF08\u4EF7\u683C\uFF1A1299\u5143\uFF09\u3002\u6BCF\u4E2A\u5546\u54C1\u90FD\u6709"\u52A0\u5165\u8D2D\u7269\u8F66"\u6309\u94AE\u3002
166
+
167
+ # Example Output (\u66F4\u6B63\u4E4B\u524D\u7684\u5185\u5BB9)
168
+ \u66F4\u6B63\uFF1A\u4E4B\u524D\u5C06\u9875\u9762\u9876\u90E8\u7684\u6587\u5B57\u8BC6\u522B\u4E3A"\u767B\u5F55"\uFF0C\u5B9E\u9645\u5E94\u8BE5\u662F"\u6CE8\u518C"\u3002\u9875\u9762\u9876\u90E8\u53F3\u4FA7\u6709"\u6CE8\u518C"\u548C"\u767B\u5F55"\u4E24\u4E2A\u6309\u94AE\uFF0C\u5F53\u524D"\u6CE8\u518C"\u6309\u94AE\u5904\u4E8E\u9AD8\u4EAE\u72B6\u6001\u3002`;
169
+ function isSolidColorImage(base64Image) {
170
+ try {
171
+ const imageBuffer = Buffer.from(base64Image, "base64");
172
+ if (imageBuffer.length < 1024) {
173
+ return true;
174
+ }
175
+ if (imageBuffer.length > 24) {
176
+ const width = imageBuffer.readUInt32BE(16);
177
+ const height = imageBuffer.readUInt32BE(20);
178
+ const pixelCount = width * height;
179
+ const bytesPerPixel = imageBuffer.length / pixelCount;
180
+ if (bytesPerPixel < 0.02) {
181
+ return true;
182
+ }
183
+ }
184
+ return false;
185
+ } catch (error) {
186
+ console.warn("[VisualFeature] Error checking for solid color image:", error);
187
+ return false;
188
+ }
189
+ }
190
+ function getCachedImageBase64(cacheManager, hwnd) {
191
+ try {
192
+ const capturePath = cacheManager.getCapturePath(hwnd);
193
+ if (!capturePath) {
194
+ return null;
195
+ }
196
+ const imageBuffer = readFileSync(capturePath);
197
+ return imageBuffer.toString("base64");
198
+ } catch (error) {
199
+ console.warn(`[VisualFeature] Failed to read cached image for ${hwnd}:`, error);
200
+ return null;
201
+ }
202
+ }
203
+ function getCachedWindowInfo(cacheManager, hwnd) {
204
+ try {
205
+ const entries = cacheManager.getAllEntries();
206
+ const entry = entries.find((e) => e.hwnd === hwnd);
207
+ if (entry) {
208
+ return {
209
+ title: entry.title
210
+ };
211
+ }
212
+ return null;
213
+ } catch (error) {
214
+ return null;
215
+ }
216
+ }
217
+ function getCachedAnalysisForModel(cacheManager, hwnd, model) {
218
+ try {
219
+ const entry = cacheManager["metadata"]?.entries?.[hwnd];
220
+ if (!entry?.analysis?.description) {
221
+ return null;
222
+ }
223
+ if (entry.analysis.model !== model) {
224
+ console.log(`[VisualFeature] Cached analysis model mismatch: cached=${entry.analysis.model}, requested=${model}`);
225
+ return null;
226
+ }
227
+ const now = Date.now();
228
+ const age = now - entry.analysis.createdAt;
229
+ const cacheReuseTTL = 24 * 60 * 60 * 1e3;
230
+ if (age > cacheReuseTTL) {
231
+ console.log(`[VisualFeature] Cached analysis too old: ${Math.round(age / 1e3)}s`);
232
+ return null;
233
+ }
234
+ console.log(`[VisualFeature] Reusing cached analysis for ${hwnd} (model: ${model})`);
235
+ return {
236
+ description: entry.analysis.description,
237
+ createdAt: entry.analysis.createdAt
238
+ };
239
+ } catch (error) {
240
+ return null;
241
+ }
242
+ }
243
+ async function captureWindow(hwnd, pythonPath = "python", pythonArgs) {
244
+ const scriptPath = join(__dirname, "python", "capture.py");
245
+ return new Promise((resolve) => {
246
+ const args = pythonArgs ? [...pythonArgs, scriptPath, hwnd] : [scriptPath, hwnd];
247
+ const child = spawn(pythonPath, args, {
248
+ stdio: ["ignore", "pipe", "pipe"],
249
+ env: { ...process.env }
250
+ // 继承环境变量
251
+ });
252
+ let stdout = "";
253
+ let stderr = "";
254
+ child.stdout?.on("data", (data) => {
255
+ stdout += data.toString();
256
+ });
257
+ child.stderr?.on("data", (data) => {
258
+ stderr += data.toString();
259
+ });
260
+ child.on("close", (code) => {
261
+ if (code !== 0) {
262
+ resolve({ error: `Python script failed (exit code ${code}): ${stderr}` });
263
+ return;
264
+ }
265
+ try {
266
+ const result = JSON.parse(stdout.trim());
267
+ resolve(result);
268
+ } catch (error) {
269
+ resolve({ error: `Failed to parse Python output: ${error}` });
270
+ }
271
+ });
272
+ child.on("error", (error) => {
273
+ resolve({ error: `Failed to spawn Python: ${error.message}` });
274
+ });
275
+ });
276
+ }
277
+ async function understandImage(base64Image, client, model, windowInfo, previousAnalysis) {
278
+ let userText = "\u8BF7\u5206\u6790\u8FD9\u5F20\u622A\u56FE\u7684\u5185\u5BB9\u3002";
279
+ if (windowInfo?.processName || windowInfo?.windowTitle) {
280
+ userText += "\n\n# \u7A97\u53E3\u4FE1\u606F";
281
+ if (windowInfo.processName) {
282
+ userText += `
283
+ - \u8FDB\u7A0B\u540D\u79F0\uFF1A${windowInfo.processName}`;
284
+ }
285
+ if (windowInfo.windowTitle) {
286
+ userText += `
287
+ - \u7A97\u53E3\u6807\u9898\uFF1A${windowInfo.windowTitle}`;
288
+ }
289
+ }
290
+ if (previousAnalysis) {
291
+ userText += `
292
+
293
+ # \u4E0A\u6B21\u8BC6\u522B\u7ED3\u679C
294
+ ${previousAnalysis}`;
295
+ userText += "\n\n\u8BF7\u6839\u636E\u4E0A\u6B21\u8BC6\u522B\u7ED3\u679C\u8FDB\u884C\u589E\u91CF\u5206\u6790\u3002\u5982\u679C\u9875\u9762\u5B8C\u5168\u4E0D\u540C\uFF0C\u8BF7\u76F4\u63A5\u63CF\u8FF0\u65B0\u9875\u9762\u7684\u5B8C\u6574\u5185\u5BB9\uFF1B\u5982\u679C\u662F\u76F8\u540C\u9875\u9762\u4F46\u89C6\u89D2\u53D8\u5316\uFF0C\u8BF7\u53EA\u63CF\u8FF0\u65B0\u770B\u5230\u7684\u5185\u5BB9\uFF1B\u5982\u679C\u9700\u8981\u66F4\u6B63\u4E4B\u524D\u7684\u5185\u5BB9\uFF0C\u8BF7\u76F4\u63A5\u8BF4\u660E\u3002";
296
+ }
297
+ const response = await client.chat.completions.create({
298
+ model,
299
+ messages: [
300
+ {
301
+ role: "system",
302
+ content: VISION_SYSTEM_PROMPT
303
+ },
304
+ {
305
+ role: "user",
306
+ content: [
307
+ {
308
+ type: "text",
309
+ text: userText
310
+ },
311
+ {
312
+ type: "image_url",
313
+ image_url: {
314
+ url: `data:image/png;base64,${base64Image}`
315
+ }
316
+ }
317
+ ]
318
+ }
319
+ ],
320
+ temperature: 0.3
321
+ });
322
+ return response.choices[0]?.message?.content ?? "\u65E0\u6CD5\u7406\u89E3\u56FE\u7247\u5185\u5BB9";
323
+ }
324
+ function createCaptureAndUnderstandTool(client, model, pythonPath = "python", pythonArgs, cacheManager) {
325
+ return createTool({
326
+ name: "capture_and_understand_window",
327
+ description: '\u622A\u53D6\u6307\u5B9A\u7A97\u53E3\u7684\u622A\u56FE\u5E76\u4F7F\u7528\u89C6\u89C9\u6A21\u578B\u7406\u89E3\u5176\u5185\u5BB9\u3002\u8F93\u5165\u53C2\u6570\u4E3A\u7A97\u53E3\u53E5\u67C4\uFF08HWND\uFF09\uFF0C\u5982 "0x12345" \u6216 "12345"\u3002\u8FD4\u56DE\u622A\u56FE\u5185\u5BB9\u7684\u8BE6\u7EC6\u63CF\u8FF0\u3002',
328
+ parameters: {
329
+ type: "object",
330
+ properties: {
331
+ hwnd: {
332
+ type: "string",
333
+ description: '\u7A97\u53E3\u53E5\u67C4\uFF0C\u652F\u6301 16 \u8FDB\u5236\u683C\u5F0F\uFF08\u5982 "0x12345"\uFF09\u6216 10 \u8FDB\u5236\u683C\u5F0F\uFF08\u5982 "12345"\uFF09'
334
+ }
335
+ },
336
+ required: ["hwnd"]
337
+ },
338
+ render: { call: "capture", result: "capture" },
339
+ execute: async ({ hwnd }) => {
340
+ let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);
341
+ let imageToAnalyze = captureResult.data;
342
+ let imageSource = "\u65B0\u622A\u56FE";
343
+ let analysisNote = "";
344
+ let usedCachedImage = false;
345
+ if (captureResult.error || !captureResult.data) {
346
+ if (cacheManager) {
347
+ console.log(`[capture_and_understand_window] Screenshot failed for ${hwnd}, trying cache...`);
348
+ const cachedImage = getCachedImageBase64(cacheManager, hwnd);
349
+ if (cachedImage) {
350
+ imageToAnalyze = cachedImage;
351
+ imageSource = "\u7F13\u5B58\u56FE\u7247";
352
+ usedCachedImage = true;
353
+ analysisNote = `[\u4F7F\u7528\u7F13\u5B58\u56FE\u7247\uFF0C\u56E0\u4E3A\u622A\u56FE\u5931\u8D25\uFF1A${captureResult.error}]`;
354
+ console.log(`[capture_and_understand_window] Using cached image for ${hwnd}`);
355
+ } else {
356
+ return `\u622A\u56FE\u5931\u8D25\u4E14\u65E0\u7F13\u5B58\u53EF\u7528\uFF1A${captureResult.error ?? "\u672A\u77E5\u9519\u8BEF"}`;
357
+ }
358
+ } else {
359
+ return `\u622A\u56FE\u5931\u8D25\uFF1A${captureResult.error ?? "\u672A\u77E5\u9519\u8BEF"}`;
360
+ }
361
+ }
362
+ if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {
363
+ console.log(`[capture_and_understand_window] Detected solid color image for ${hwnd}, trying cache...`);
364
+ if (cacheManager) {
365
+ const cachedImage = getCachedImageBase64(cacheManager, hwnd);
366
+ if (cachedImage) {
367
+ imageToAnalyze = cachedImage;
368
+ imageSource = "\u7F13\u5B58\u56FE\u7247";
369
+ usedCachedImage = true;
370
+ analysisNote = "[\u4F7F\u7528\u7F13\u5B58\u56FE\u7247\uFF0C\u56E0\u4E3A\u65B0\u622A\u56FE\u662F\u7EAF\u8272\u56FE\uFF08\u7A97\u53E3\u53EF\u80FD\u88AB\u6700\u5C0F\u5316\u6216\u906E\u6321\uFF09]";
371
+ console.log(`[capture_and_understand_window] Using cached image for ${hwnd} (solid color detected)`);
372
+ }
373
+ }
374
+ }
375
+ if (!imageToAnalyze) {
376
+ return "\u65E0\u6CD5\u83B7\u53D6\u53EF\u7528\u7684\u56FE\u7247\u8FDB\u884C\u5206\u6790";
377
+ }
378
+ if (usedCachedImage && cacheManager) {
379
+ const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);
380
+ if (cachedAnalysis) {
381
+ const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1e3);
382
+ const timeAgo = age < 60 ? `${age}\u79D2\u524D` : age < 3600 ? `${Math.round(age / 60)}\u5206\u949F\u524D` : `${Math.round(age / 3600)}\u5C0F\u65F6\u524D`;
383
+ const parts = [
384
+ `\u4F7F\u7528\u7F13\u5B58\u56FE\u7247\uFF08\u6765\u6E90\uFF1A${imageSource}\uFF09`,
385
+ analysisNote || "",
386
+ "",
387
+ `\u5185\u5BB9\u5206\u6790\uFF08${timeAgo}\u7684\u5206\u6790\u7ED3\u679C\uFF09\uFF1A`,
388
+ cachedAnalysis.description
389
+ ].filter(Boolean);
390
+ return parts.join("\n");
391
+ }
392
+ }
393
+ let windowInfo;
394
+ if (cacheManager) {
395
+ const cached = getCachedWindowInfo(cacheManager, hwnd);
396
+ if (cached) {
397
+ windowInfo = {
398
+ windowTitle: cached.title
399
+ };
400
+ }
401
+ }
402
+ let previousAnalysis;
403
+ if (cacheManager) {
404
+ const cachedAnalysis = cacheManager.getAnalysis(hwnd);
405
+ if (cachedAnalysis) {
406
+ previousAnalysis = cachedAnalysis.description;
407
+ }
408
+ }
409
+ try {
410
+ const description = await understandImage(
411
+ imageToAnalyze,
412
+ client,
413
+ model,
414
+ windowInfo,
415
+ previousAnalysis
416
+ );
417
+ const parts = [
418
+ `\u622A\u56FE\u6210\u529F\uFF08${captureResult.width ?? "?"}x${captureResult.height ?? "?"}\uFF0C\u6765\u6E90\uFF1A${imageSource}\uFF09`
419
+ ];
420
+ if (analysisNote) {
421
+ parts.push(analysisNote);
422
+ }
423
+ parts.push("");
424
+ parts.push("\u5185\u5BB9\u5206\u6790\uFF1A");
425
+ parts.push(description);
426
+ return parts.join("\n");
427
+ } catch (error) {
428
+ return `\u89C6\u89C9\u7406\u89E3\u5931\u8D25\uFF1A${error}`;
429
+ }
430
+ }
431
+ });
432
+ }
433
+ function createCaptureAndUnderstandAdvancedTool(client, model, pythonPath = "python", pythonArgs, cacheManager) {
434
+ return createTool({
435
+ name: "capture_and_understand_window_advanced",
436
+ description: '\u4F7F\u7528\u9AD8\u7EA7\u89C6\u89C9\u6A21\u578B\uFF089B\uFF09\u622A\u53D6\u6307\u5B9A\u7A97\u53E3\u7684\u622A\u56FE\u5E76\u8FDB\u884C\u6DF1\u5EA6\u7406\u89E3\u3002\u63D0\u4F9B\u66F4\u51C6\u786E\u7684\u5185\u5BB9\u8BC6\u522B\u548C\u5206\u6790\u3002\u8F93\u5165\u53C2\u6570\u4E3A\u7A97\u53E3\u53E5\u67C4\uFF08HWND\uFF09\uFF0C\u5982 "0x12345" \u6216 "12345"\u3002',
437
+ parameters: {
438
+ type: "object",
439
+ properties: {
440
+ hwnd: {
441
+ type: "string",
442
+ description: '\u7A97\u53E3\u53E5\u67C4\uFF0C\u652F\u6301 16 \u8FDB\u5236\u683C\u5F0F\uFF08\u5982 "0x12345"\uFF09\u6216 10 \u8FDB\u5236\u683C\u5F0F\uFF08\u5982 "12345"\uFF09'
443
+ }
444
+ },
445
+ required: ["hwnd"]
446
+ },
447
+ render: { call: "capture", result: "capture" },
448
+ execute: async ({ hwnd }) => {
449
+ let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);
450
+ let imageToAnalyze = captureResult.data;
451
+ let imageSource = "\u65B0\u622A\u56FE";
452
+ let analysisNote = "";
453
+ let usedCachedImage = false;
454
+ if (captureResult.error || !captureResult.data) {
455
+ if (cacheManager) {
456
+ console.log(`[capture_and_understand_window_advanced] Screenshot failed for ${hwnd}, trying cache...`);
457
+ const cachedImage = getCachedImageBase64(cacheManager, hwnd);
458
+ if (cachedImage) {
459
+ imageToAnalyze = cachedImage;
460
+ imageSource = "\u7F13\u5B58\u56FE\u7247";
461
+ usedCachedImage = true;
462
+ analysisNote = `[\u4F7F\u7528\u7F13\u5B58\u56FE\u7247\uFF0C\u56E0\u4E3A\u622A\u56FE\u5931\u8D25\uFF1A${captureResult.error}]`;
463
+ console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd}`);
464
+ } else {
465
+ return `\u622A\u56FE\u5931\u8D25\u4E14\u65E0\u7F13\u5B58\u53EF\u7528\uFF1A${captureResult.error ?? "\u672A\u77E5\u9519\u8BEF"}`;
466
+ }
467
+ } else {
468
+ return `\u622A\u56FE\u5931\u8D25\uFF1A${captureResult.error ?? "\u672A\u77E5\u9519\u8BEF"}`;
469
+ }
470
+ }
471
+ if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {
472
+ console.log(`[capture_and_understand_window_advanced] Detected solid color image for ${hwnd}, trying cache...`);
473
+ if (cacheManager) {
474
+ const cachedImage = getCachedImageBase64(cacheManager, hwnd);
475
+ if (cachedImage) {
476
+ imageToAnalyze = cachedImage;
477
+ imageSource = "\u7F13\u5B58\u56FE\u7247";
478
+ usedCachedImage = true;
479
+ analysisNote = "[\u4F7F\u7528\u7F13\u5B58\u56FE\u7247\uFF0C\u56E0\u4E3A\u65B0\u622A\u56FE\u662F\u7EAF\u8272\u56FE\uFF08\u7A97\u53E3\u53EF\u80FD\u88AB\u6700\u5C0F\u5316\u6216\u906E\u6321\uFF09]";
480
+ console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd} (solid color detected)`);
481
+ }
482
+ }
483
+ }
484
+ if (!imageToAnalyze) {
485
+ return "\u65E0\u6CD5\u83B7\u53D6\u53EF\u7528\u7684\u56FE\u7247\u8FDB\u884C\u5206\u6790";
486
+ }
487
+ if (usedCachedImage && cacheManager) {
488
+ const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);
489
+ if (cachedAnalysis) {
490
+ const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1e3);
491
+ const timeAgo = age < 60 ? `${age}\u79D2\u524D` : age < 3600 ? `${Math.round(age / 60)}\u5206\u949F\u524D` : `${Math.round(age / 3600)}\u5C0F\u65F6\u524D`;
492
+ const parts = [
493
+ `[\u9AD8\u7EA7\u6A21\u578B] \u4F7F\u7528\u7F13\u5B58\u56FE\u7247\uFF08\u6765\u6E90\uFF1A${imageSource}\uFF09`,
494
+ analysisNote || "",
495
+ "",
496
+ `\u5185\u5BB9\u5206\u6790\uFF08${timeAgo}\u7684\u5206\u6790\u7ED3\u679C\uFF09\uFF1A`,
497
+ cachedAnalysis.description
498
+ ].filter(Boolean);
499
+ return parts.join("\n");
500
+ }
501
+ }
502
+ let windowInfo;
503
+ if (cacheManager) {
504
+ const cached = getCachedWindowInfo(cacheManager, hwnd);
505
+ if (cached) {
506
+ windowInfo = {
507
+ windowTitle: cached.title
508
+ };
509
+ }
510
+ }
511
+ let previousAnalysis;
512
+ if (cacheManager) {
513
+ const cachedAnalysis = cacheManager.getAnalysis(hwnd);
514
+ if (cachedAnalysis) {
515
+ previousAnalysis = cachedAnalysis.description;
516
+ }
517
+ }
518
+ try {
519
+ const description = await understandImage(
520
+ imageToAnalyze,
521
+ client,
522
+ model,
523
+ windowInfo,
524
+ previousAnalysis
525
+ );
526
+ const parts = [
527
+ `[\u9AD8\u7EA7\u6A21\u578B] \u622A\u56FE\u6210\u529F\uFF08${captureResult.width ?? "?"}x${captureResult.height ?? "?"}\uFF0C\u6765\u6E90\uFF1A${imageSource}\uFF09`
528
+ ];
529
+ if (analysisNote) {
530
+ parts.push(analysisNote);
531
+ }
532
+ parts.push("");
533
+ parts.push("\u5185\u5BB9\u5206\u6790\uFF1A");
534
+ parts.push(description);
535
+ return parts.join("\n");
536
+ } catch (error) {
537
+ return `\u89C6\u89C9\u7406\u89E3\u5931\u8D25\uFF1A${error}`;
538
+ }
539
+ }
540
+ });
541
+ }
542
+
543
+ export {
544
+ createTool,
545
+ ToolRegistry,
546
+ captureWindow,
547
+ understandImage,
548
+ createCaptureAndUnderstandTool,
549
+ createCaptureAndUnderstandAdvancedTool
550
+ };
551
+ //# sourceMappingURL=chunk-G5ECPY4K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/features/visual/tools.ts","../src/core/tool.ts"],"sourcesContent":["/**\n * VisualFeature 工具定义\n *\n * 提供两个视觉理解工具:\n * 1. capture_and_understand_window - 使用 4B 模型(快速)\n * 2. capture_and_understand_window_advanced - 使用 9B 模型(准确)\n *\n * 两个工具都支持缓存回退:\n * - 截图失败时(窗口缩小)使用缓存的图片\n * - 检测到纯色图时使用缓存的图片\n */\n\nimport { spawn } from 'child_process';\nimport { readFileSync } from 'fs';\nimport OpenAI from 'openai';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { createTool } from '../../core/tool.js';\nimport type { Tool } from '../../core/types.js';\nimport type {\n CaptureResult,\n VisualUnderstandingResult,\n} from './types.js';\nimport type { VisualCacheManager } from './cache.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// ========== LLM 配置 ==========\n\nconst DEFAULT_BASE_URL = 'http://localhost:7575';\nconst DEFAULT_MODEL = 'Qwen3.5-4B-Q5_K_M';\n\nconst DEFAULT_ADVANCED_BASE_URL = 'http://localhost:7577';\nconst DEFAULT_ADVANCED_MODEL = 'Qwen3.5-9B-Q4_K_M';\n\n// 视觉理解系统提示词(增量分析版本)\nconst VISION_SYSTEM_PROMPT = `# Role\n你是一位专业的视觉内容分析专家。你的任务是仔细观察用户提供的截图,并给出准确、详细的内容描述。\n\n# Task\n分析截图中的视觉内容,重点描述:\n1. **主要内容**:截图显示的是什么应用、什么界面\n2. **文本内容**:所有可见的文字、标签、提示信息\n3. **关键元素**:重要的按钮、输入框、菜单项等可交互元素\n4. **状态信息**:当前界面状态(如:选中/未选中、展开/收起等)\n5. **异常或特殊点**:错误提示、警告信息、弹窗等\n\n# 增量分析策略\n你会收到窗口上下文信息(进程名、窗口标题)和上次的识别结果。请根据这些信息进行增量分析:\n\n## 情况 1:完全不同的页面\n如果新截图显示的页面与上次识别结果完全不同(例如:从代码编辑器切换到浏览器),请直接描述新页面的完整内容。\n\n## 情况 2:相同页面,视角变化\n如果新截图与上次是同一个页面,但视角发生了变化(例如:滚动到了新位置、展开了新的菜单、切换了标签页),请只描述**新看到的内容**,不需要重复描述之前已经识别过的内容。\n\n## 情况 3:相同页面,更正之前的内容\n如果新截图与上次是同一个页面,但你发现之前的识别有错误或遗漏,请直接更正或补充。例如:\n- \"更正:之前识别为'确认'的按钮实际是'取消'\"\n- \"补充:页面上方还有一个搜索栏,输入框显示'请输入关键词'\"\n\n# Output Requirements\n- 使用自然语言描述,不要使用 JSON 或其他结构化格式\n- 描述要准确、清晰,便于后续处理\n- 如果图片不清晰或无法理解,直接说明\n- 输出语言与截图中的主要语言保持一致\n\n# Example Output (完全不同的页面)\n这是一个浏览器窗口,显示的是一个购物网站。顶部有导航栏,显示\"首页、分类、购物车、我的\"。中间部分是商品列表,每个商品卡片包含图片、名称、价格和\"加入购物车\"按钮。当前显示的是\"电子产品\"分类。\n\n# Example Output (相同页面,视角变化)\n页面向下滚动后,显示了更多的商品。新看到的商品包括:一款无线鼠标(价格:199元)、一个机械键盘(价格:399元)和一个显示器(价格:1299元)。每个商品都有\"加入购物车\"按钮。\n\n# Example Output (更正之前的内容)\n更正:之前将页面顶部的文字识别为\"登录\",实际应该是\"注册\"。页面顶部右侧有\"注册\"和\"登录\"两个按钮,当前\"注册\"按钮处于高亮状态。`;\n\n// ========== 辅助函数 ==========\n\n/**\n * 检测是否为纯色图\n * 通过采样图片的几个关键点来判断是否为纯色\n */\nfunction isSolidColorImage(base64Image: string): boolean {\n try {\n // 解码 base64 获取 PNG 数据\n const imageBuffer = Buffer.from(base64Image, 'base64');\n\n // 简单检测:检查图片大小,如果太小(小于 1KB)可能是纯色或错误\n if (imageBuffer.length < 1024) {\n return true;\n }\n\n // PNG 文件头的 IHDR chunk 之后是 IDAT(图像数据)\n // 我们可以检查图片是否包含足够的颜色变化\n // 这里做一个简单的启发式检查:如果压缩后非常小,可能是纯色\n\n // 更精确的方法是解码 PNG,但这需要额外的库\n // 作为替代,我们检查文件大小与像素数的比例\n // 对于普通截图,这个比例通常在 0.1 - 1 字节/像素之间\n // 纯色图压缩后通常 < 0.01 字节/像素\n\n // 从 PNG IHDR chunk 读取宽高(简化版)\n // IHDR 位于 PNG 签名后 8 字节,宽度 4 字节,高度 4 字节\n if (imageBuffer.length > 24) {\n const width = imageBuffer.readUInt32BE(16);\n const height = imageBuffer.readUInt32BE(20);\n const pixelCount = width * height;\n const bytesPerPixel = imageBuffer.length / pixelCount;\n\n // 如果每个像素平均 < 0.02 字节,很可能是纯色\n if (bytesPerPixel < 0.02) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n console.warn('[VisualFeature] Error checking for solid color image:', error);\n return false;\n }\n}\n\n/**\n * 从缓存读取最新的图片\n */\nfunction getCachedImageBase64(cacheManager: VisualCacheManager, hwnd: string): string | null {\n try {\n const capturePath = cacheManager.getCapturePath(hwnd);\n if (!capturePath) {\n return null;\n }\n\n // 读取图片文件并转换为 base64\n const imageBuffer = readFileSync(capturePath);\n return imageBuffer.toString('base64');\n } catch (error) {\n console.warn(`[VisualFeature] Failed to read cached image for ${hwnd}:`, error);\n return null;\n }\n}\n\n/**\n * 从缓存获取上次的窗口信息(用于标题、进程名等)\n */\nfunction getCachedWindowInfo(cacheManager: VisualCacheManager, hwnd: string): {\n title: string;\n processName?: string;\n} | null {\n try {\n const entries = cacheManager.getAllEntries();\n const entry = entries.find(e => e.hwnd === hwnd);\n if (entry) {\n return {\n title: entry.title,\n };\n }\n return null;\n } catch (error) {\n return null;\n }\n}\n\n/**\n * 检查缓存图片是否已有该模型的分析结果\n * 返回分析结果(如果存在且模型匹配)或 null\n */\nfunction getCachedAnalysisForModel(\n cacheManager: VisualCacheManager,\n hwnd: string,\n model: string\n): { description: string; createdAt: number } | null {\n try {\n const entry = cacheManager['metadata']?.entries?.[hwnd];\n if (!entry?.analysis?.description) {\n return null;\n }\n\n // 检查模型是否匹配\n if (entry.analysis.model !== model) {\n console.log(`[VisualFeature] Cached analysis model mismatch: cached=${entry.analysis.model}, requested=${model}`);\n return null;\n }\n\n // 检查分析结果是否过期(使用较长的 TTL,因为缓存图片的分析结果可以复用)\n const now = Date.now();\n const age = now - entry.analysis.createdAt;\n const cacheReuseTTL = 24 * 60 * 60 * 1000; // 24小时 - 缓存图片的分析结果可以复用更久\n\n if (age > cacheReuseTTL) {\n console.log(`[VisualFeature] Cached analysis too old: ${Math.round(age / 1000)}s`);\n return null;\n }\n\n console.log(`[VisualFeature] Reusing cached analysis for ${hwnd} (model: ${model})`);\n return {\n description: entry.analysis.description,\n createdAt: entry.analysis.createdAt,\n };\n } catch (error) {\n return null;\n }\n}\n\n// ========== 核心功能函数 ==========\n\n/**\n * 调用 Python 脚本截图指定窗口\n */\nexport async function captureWindow(\n hwnd: string,\n pythonPath: string = 'python',\n pythonArgs?: string[]\n): Promise<CaptureResult> {\n const scriptPath = join(__dirname, 'python', 'capture.py');\n\n return new Promise((resolve) => {\n // 支持 pythonArgs 配置(如 uv run)\n const args = pythonArgs\n ? [...pythonArgs, scriptPath, hwnd]\n : [scriptPath, hwnd];\n\n // 继承父进程的环境变量(包括 PATH),这样能找到 uv 管理的 Python\n const child = spawn(pythonPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env }, // 继承环境变量\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr?.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n resolve({ error: `Python script failed (exit code ${code}): ${stderr}` });\n return;\n }\n\n try {\n const result: CaptureResult = JSON.parse(stdout.trim());\n resolve(result);\n } catch (error) {\n resolve({ error: `Failed to parse Python output: ${error}` });\n }\n });\n\n child.on('error', (error) => {\n resolve({ error: `Failed to spawn Python: ${error.message}` });\n });\n });\n}\n\n/**\n * 调用 LLM 进行视觉理解\n * @param base64Image base64 编码的图片\n * @param client OpenAI 客户端\n * @param model 模型名称\n * @param windowInfo 窗口信息(进程名、标题等)\n * @param previousAnalysis 上次的分析结果(可选)\n */\nexport async function understandImage(\n base64Image: string,\n client: OpenAI,\n model: string,\n windowInfo?: {\n processName?: string;\n windowTitle?: string;\n },\n previousAnalysis?: string\n): Promise<string> {\n // 构建用户消息,包含窗口上下文和上次分析结果\n let userText = '请分析这张截图的内容。';\n\n if (windowInfo?.processName || windowInfo?.windowTitle) {\n userText += '\\n\\n# 窗口信息';\n if (windowInfo.processName) {\n userText += `\\n- 进程名称:${windowInfo.processName}`;\n }\n if (windowInfo.windowTitle) {\n userText += `\\n- 窗口标题:${windowInfo.windowTitle}`;\n }\n }\n\n if (previousAnalysis) {\n userText += `\\n\\n# 上次识别结果\\n${previousAnalysis}`;\n userText += '\\n\\n请根据上次识别结果进行增量分析。如果页面完全不同,请直接描述新页面的完整内容;如果是相同页面但视角变化,请只描述新看到的内容;如果需要更正之前的内容,请直接说明。';\n }\n\n const response = await client.chat.completions.create({\n model,\n messages: [\n {\n role: 'system',\n content: VISION_SYSTEM_PROMPT,\n },\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: userText,\n },\n {\n type: 'image_url',\n image_url: {\n url: `data:image/png;base64,${base64Image}`,\n },\n },\n ],\n },\n ],\n temperature: 0.3,\n });\n\n return response.choices[0]?.message?.content ?? '无法理解图片内容';\n}\n\n// ========== 工具工厂函数 ==========\n\n/**\n * 创建基础视觉理解工具(4B 模型)\n * 支持缓存回退:截图失败或纯色图时使用缓存图片\n */\nexport function createCaptureAndUnderstandTool(\n client: OpenAI,\n model: string,\n pythonPath: string = 'python',\n pythonArgs?: string[],\n cacheManager?: VisualCacheManager | null\n): Tool {\n return createTool({\n name: 'capture_and_understand_window',\n description: '截取指定窗口的截图并使用视觉模型理解其内容。输入参数为窗口句柄(HWND),如 \"0x12345\" 或 \"12345\"。返回截图内容的详细描述。',\n parameters: {\n type: 'object',\n properties: {\n hwnd: {\n type: 'string',\n description: '窗口句柄,支持 16 进制格式(如 \"0x12345\")或 10 进制格式(如 \"12345\")',\n },\n },\n required: ['hwnd'],\n },\n render: { call: 'capture', result: 'capture' },\n execute: async ({ hwnd }: { hwnd: string }) => {\n // 1. 尝试截图\n let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);\n\n let imageToAnalyze = captureResult.data;\n let imageSource = '新截图';\n let analysisNote = '';\n let usedCachedImage = false;\n\n // 2. 如果截图失败,尝试使用缓存\n if (captureResult.error || !captureResult.data) {\n if (cacheManager) {\n console.log(`[capture_and_understand_window] Screenshot failed for ${hwnd}, trying cache...`);\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = `[使用缓存图片,因为截图失败:${captureResult.error}]`;\n console.log(`[capture_and_understand_window] Using cached image for ${hwnd}`);\n } else {\n return `截图失败且无缓存可用:${captureResult.error ?? '未知错误'}`;\n }\n } else {\n return `截图失败:${captureResult.error ?? '未知错误'}`;\n }\n }\n\n // 3. 检查是否为纯色图,如果是则使用缓存\n if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {\n console.log(`[capture_and_understand_window] Detected solid color image for ${hwnd}, trying cache...`);\n if (cacheManager) {\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = '[使用缓存图片,因为新截图是纯色图(窗口可能被最小化或遮挡)]';\n console.log(`[capture_and_understand_window] Using cached image for ${hwnd} (solid color detected)`);\n }\n }\n }\n\n // 4. 如果没有可用的图片\n if (!imageToAnalyze) {\n return '无法获取可用的图片进行分析';\n }\n\n // 5. 缓存去重:如果使用了缓存图片,检查是否已有该模型的分析结果\n if (usedCachedImage && cacheManager) {\n const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);\n if (cachedAnalysis) {\n // 直接返回缓存的分析结果,不需要重新调用模型\n const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1000);\n const timeAgo = age < 60 ? `${age}秒前` : age < 3600 ? `${Math.round(age / 60)}分钟前` : `${Math.round(age / 3600)}小时前`;\n\n const parts = [\n `使用缓存图片(来源:${imageSource})`,\n analysisNote || '',\n '',\n `内容分析(${timeAgo}的分析结果):`,\n cachedAnalysis.description,\n ].filter(Boolean);\n\n return parts.join('\\n');\n }\n }\n\n // 6. 获取窗口信息(从缓存)\n let windowInfo: { processName?: string; windowTitle?: string } | undefined;\n if (cacheManager) {\n const cached = getCachedWindowInfo(cacheManager, hwnd);\n if (cached) {\n windowInfo = {\n windowTitle: cached.title,\n };\n }\n }\n\n // 7. 获取上次的分析结果(从缓存,用于增量分析)\n let previousAnalysis: string | undefined;\n if (cacheManager) {\n const cachedAnalysis = cacheManager.getAnalysis(hwnd);\n if (cachedAnalysis) {\n previousAnalysis = cachedAnalysis.description;\n }\n }\n\n // 8. 视觉理解\n try {\n const description = await understandImage(\n imageToAnalyze,\n client,\n model,\n windowInfo,\n previousAnalysis\n );\n\n const parts = [\n `截图成功(${captureResult.width ?? '?'}x${captureResult.height ?? '?'},来源:${imageSource})`,\n ];\n\n if (analysisNote) {\n parts.push(analysisNote);\n }\n\n parts.push('');\n parts.push('内容分析:');\n parts.push(description);\n\n return parts.join('\\n');\n } catch (error) {\n return `视觉理解失败:${error}`;\n }\n },\n });\n}\n\n/**\n * 创建高级视觉理解工具(9B 模型)\n * 支持缓存回退:截图失败或纯色图时使用缓存图片\n */\nexport function createCaptureAndUnderstandAdvancedTool(\n client: OpenAI,\n model: string,\n pythonPath: string = 'python',\n pythonArgs?: string[],\n cacheManager?: VisualCacheManager | null\n): Tool {\n return createTool({\n name: 'capture_and_understand_window_advanced',\n description: '使用高级视觉模型(9B)截取指定窗口的截图并进行深度理解。提供更准确的内容识别和分析。输入参数为窗口句柄(HWND),如 \"0x12345\" 或 \"12345\"。',\n parameters: {\n type: 'object',\n properties: {\n hwnd: {\n type: 'string',\n description: '窗口句柄,支持 16 进制格式(如 \"0x12345\")或 10 进制格式(如 \"12345\")',\n },\n },\n required: ['hwnd'],\n },\n render: { call: 'capture', result: 'capture' },\n execute: async ({ hwnd }: { hwnd: string }) => {\n // 1. 尝试截图(与基础工具相同的逻辑)\n let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);\n\n let imageToAnalyze = captureResult.data;\n let imageSource = '新截图';\n let analysisNote = '';\n let usedCachedImage = false;\n\n // 2. 如果截图失败,尝试使用缓存\n if (captureResult.error || !captureResult.data) {\n if (cacheManager) {\n console.log(`[capture_and_understand_window_advanced] Screenshot failed for ${hwnd}, trying cache...`);\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = `[使用缓存图片,因为截图失败:${captureResult.error}]`;\n console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd}`);\n } else {\n return `截图失败且无缓存可用:${captureResult.error ?? '未知错误'}`;\n }\n } else {\n return `截图失败:${captureResult.error ?? '未知错误'}`;\n }\n }\n\n // 3. 检查是否为纯色图\n if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {\n console.log(`[capture_and_understand_window_advanced] Detected solid color image for ${hwnd}, trying cache...`);\n if (cacheManager) {\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = '[使用缓存图片,因为新截图是纯色图(窗口可能被最小化或遮挡)]';\n console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd} (solid color detected)`);\n }\n }\n }\n\n // 4. 如果没有可用的图片\n if (!imageToAnalyze) {\n return '无法获取可用的图片进行分析';\n }\n\n // 5. 缓存去重:如果使用了缓存图片,检查是否已有该模型的分析结果\n if (usedCachedImage && cacheManager) {\n const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);\n if (cachedAnalysis) {\n // 直接返回缓存的分析结果,不需要重新调用模型\n const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1000);\n const timeAgo = age < 60 ? `${age}秒前` : age < 3600 ? `${Math.round(age / 60)}分钟前` : `${Math.round(age / 3600)}小时前`;\n\n const parts = [\n `[高级模型] 使用缓存图片(来源:${imageSource})`,\n analysisNote || '',\n '',\n `内容分析(${timeAgo}的分析结果):`,\n cachedAnalysis.description,\n ].filter(Boolean);\n\n return parts.join('\\n');\n }\n }\n\n // 6. 获取窗口信息\n let windowInfo: { processName?: string; windowTitle?: string } | undefined;\n if (cacheManager) {\n const cached = getCachedWindowInfo(cacheManager, hwnd);\n if (cached) {\n windowInfo = {\n windowTitle: cached.title,\n };\n }\n }\n\n // 7. 获取上次的分析结果(用于增量分析)\n let previousAnalysis: string | undefined;\n if (cacheManager) {\n const cachedAnalysis = cacheManager.getAnalysis(hwnd);\n if (cachedAnalysis) {\n previousAnalysis = cachedAnalysis.description;\n }\n }\n\n // 8. 高级视觉理解\n try {\n const description = await understandImage(\n imageToAnalyze,\n client,\n model,\n windowInfo,\n previousAnalysis\n );\n\n const parts = [\n `[高级模型] 截图成功(${captureResult.width ?? '?'}x${captureResult.height ?? '?'},来源:${imageSource})`,\n ];\n\n if (analysisNote) {\n parts.push(analysisNote);\n }\n\n parts.push('');\n parts.push('内容分析:');\n parts.push(description);\n\n return parts.join('\\n');\n } catch (error) {\n return `视觉理解失败:${error}`;\n }\n },\n });\n}\n","/**\n * 工具定义\n * 简单的工具创建函数\n */\n\nimport type { Tool, ToolRenderConfig } from './types.js';\n\n/**\n * 渲染配置扩展类型\n * 支持字符串(模板名称)或配置对象\n */\nexport type ToolRenderInput = ToolRenderConfig | string;\n\n/**\n * 创建一个工具\n * @param config 工具配置\n * @param sourceFile 可选:调用此函数的源文件路径(用于自动查找渲染文件)\n */\nexport function createTool(\n config: {\n name: string;\n description: string;\n parameters?: Record<string, any>;\n execute: (args: any, context?: any) => Promise<any>;\n render?: ToolRenderInput;\n },\n sourceFile?: string\n): Tool {\n let finalRender: ToolRenderConfig | undefined = undefined;\n\n if (config.render) {\n // 如果 render 是字符串,转换为配置对象(call 和 result 使用同一模板)\n if (typeof config.render === 'string') {\n finalRender = {\n call: config.render,\n result: config.render,\n };\n } else {\n finalRender = config.render;\n }\n } else if (sourceFile) {\n // render 未定义时,尝试自动查找同目录下的 .render.ts 文件\n // 例如:tools/fs.ts -> tools/fs.render.ts\n const renderPath = sourceFile.replace(/\\.ts$/, '.render.ts');\n // 使用特殊标记,延迟加载\n (finalRender as any) = {\n __renderPath: renderPath,\n call: undefined,\n result: undefined,\n };\n }\n\n return {\n name: config.name,\n description: config.description,\n parameters: config.parameters,\n execute: config.execute,\n render: finalRender,\n };\n}\n\n/**\n * 工具注册表 - 管理多个工具\n */\nexport class ToolRegistry {\n private tools = new Map<string, Tool>();\n private enabled = new Set<string>(); // 启用的工具名\n private pendingDisabled = new Set<string>(); // 工具注册前的预禁用状态\n private sources = new Map<string, string>(); // 工具来源追踪\n\n /**\n * 注册工具(默认启用,记录来源)\n */\n register(tool: Tool, source?: string): this {\n this.tools.set(tool.name, tool);\n if (this.pendingDisabled.has(tool.name)) {\n this.enabled.delete(tool.name);\n } else {\n this.enabled.add(tool.name); // 默认启用\n }\n if (source) {\n this.sources.set(tool.name, source);\n }\n return this;\n }\n\n /**\n * 禁用工具\n */\n disable(name: string): boolean {\n this.pendingDisabled.add(name);\n if (!this.tools.has(name)) {\n return true;\n }\n return this.enabled.delete(name);\n }\n\n /**\n * 启用工具\n */\n enable(name: string): boolean {\n this.pendingDisabled.delete(name);\n if (this.tools.has(name)) {\n this.enabled.add(name);\n return true;\n }\n return false;\n }\n\n /**\n * 检查工具是否启用\n */\n isEnabled(name: string): boolean {\n return this.enabled.has(name);\n }\n\n /**\n * 获取工具来源(调试用)\n */\n getSource(name: string): string | undefined {\n return this.sources.get(name);\n }\n\n /**\n * 获取工具条目(调试快照用)\n */\n getEntries(): Array<{ tool: Tool; enabled: boolean; source?: string }> {\n return Array.from(this.tools.entries()).map(([name, tool]) => ({\n tool,\n enabled: this.enabled.has(name),\n source: this.sources.get(name),\n }));\n }\n\n /**\n * 获取工具\n */\n get(name: string): Tool | undefined {\n return this.tools.get(name);\n }\n\n /**\n * 获取所有工具(只返回启用的)\n */\n getAll(): Tool[] {\n return Array.from(this.enabled)\n .map(name => this.tools.get(name))\n .filter((t): t is Tool => t !== undefined);\n }\n\n /**\n * 检查工具是否存在\n */\n has(name: string): boolean {\n return this.tools.has(name);\n }\n\n /**\n * 获取工具的渲染配置\n */\n getRenderConfig(name: string): ToolRenderConfig | undefined {\n const tool = this.tools.get(name);\n return tool?.render;\n }\n}\n"],"mappings":"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","names":[]}