minimal-agent 0.10.0 → 0.12.0

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 (573) hide show
  1. package/README.md +11 -3
  2. package/package.json +52 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
  4. package/plugins/computer_use/README.md +94 -0
  5. package/plugins/computer_use/app/_template.md +142 -0
  6. package/plugins/computer_use/app/chrome.md +165 -0
  7. package/plugins/computer_use/app/finder.md +76 -0
  8. package/plugins/computer_use/app/macos-system-settings.md +81 -0
  9. package/plugins/computer_use/app/microsoft-excel.md +80 -0
  10. package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
  11. package/plugins/computer_use/app/microsoft-word.md +79 -0
  12. package/plugins/computer_use/app/wechat.md +413 -0
  13. package/plugins/computer_use/app/weread.md +319 -0
  14. package/plugins/computer_use/commands/computeruse.md +25 -0
  15. package/plugins/computer_use/commands/cu.md +9 -0
  16. package/plugins/computer_use/plugin.js +26 -0
  17. package/plugins/computer_use/plugin.ts +30 -0
  18. package/plugins/computer_use/src/capture.js +90 -0
  19. package/plugins/computer_use/src/capture.ts +104 -0
  20. package/plugins/computer_use/src/commands.js +91 -0
  21. package/plugins/computer_use/src/commands.ts +103 -0
  22. package/plugins/computer_use/src/daemon.js +134 -0
  23. package/plugins/computer_use/src/daemon.ts +161 -0
  24. package/plugins/computer_use/src/manuals.js +135 -0
  25. package/plugins/computer_use/src/manuals.ts +159 -0
  26. package/plugins/computer_use/src/verify.js +168 -0
  27. package/plugins/computer_use/src/verify.ts +219 -0
  28. package/plugins/computer_use/test/commands.test.ts +73 -0
  29. package/plugins/cron-runner/plugin.ts +38 -0
  30. package/plugins/cron-runner/src/commands.ts +373 -0
  31. package/plugins/cron-runner/src/cronTime.ts +155 -0
  32. package/plugins/cron-runner/src/lock.ts +79 -0
  33. package/plugins/cron-runner/src/runs.ts +99 -0
  34. package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
  35. package/plugins/cron-runner/src/scheduler.ts +376 -0
  36. package/plugins/cron-runner/src/schema.ts +139 -0
  37. package/plugins/cron-runner/src/service.ts +193 -0
  38. package/plugins/cron-runner/src/store.ts +333 -0
  39. package/plugins/cron-runner/src/types.ts +141 -0
  40. package/plugins/cron-runner/test/cronTime.test.ts +41 -0
  41. package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
  42. package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
  43. package/plugins/cron-runner/test/service.test.ts +76 -0
  44. package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
  45. package/plugins/workflow-runner/plugin.js +56 -2
  46. package/plugins/workflow-runner/plugin.ts +113 -0
  47. package/plugins/workflow-runner/src/agentCommon.js +43 -4
  48. package/plugins/workflow-runner/src/agentCommon.ts +435 -0
  49. package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
  50. package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
  51. package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
  52. package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
  53. package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
  54. package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
  55. package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
  56. package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
  57. package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
  58. package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
  59. package/plugins/workflow-runner/src/dslManifest.ts +127 -0
  60. package/plugins/workflow-runner/src/expressions.ts +622 -0
  61. package/plugins/workflow-runner/src/index.js +13 -4
  62. package/plugins/workflow-runner/src/index.ts +338 -0
  63. package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
  64. package/plugins/workflow-runner/src/loader.js +23 -0
  65. package/plugins/workflow-runner/src/loader.ts +721 -0
  66. package/plugins/workflow-runner/src/multimodal.js +2 -1
  67. package/plugins/workflow-runner/src/multimodal.ts +64 -0
  68. package/plugins/workflow-runner/src/recovery.ts +149 -0
  69. package/plugins/workflow-runner/src/runner.js +75 -659
  70. package/plugins/workflow-runner/src/runner.ts +604 -0
  71. package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
  72. package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
  73. package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
  74. package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
  75. package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
  76. package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
  77. package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
  78. package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
  79. package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
  80. package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
  81. package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
  82. package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
  83. package/plugins/workflow-runner/src/stepPipeline.js +277 -0
  84. package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
  85. package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
  86. package/plugins/workflow-runner/src/toolRuntime.js +48 -0
  87. package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
  88. package/plugins/workflow-runner/src/types.js +37 -1
  89. package/plugins/workflow-runner/src/types.ts +602 -0
  90. package/plugins/workflow-runner/src/workflowState.ts +733 -0
  91. package/plugins/workflow-runner/test/agent.test.ts +587 -0
  92. package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
  93. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
  94. package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
  95. package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
  96. package/plugins/workflow-runner/test/context-files.test.ts +320 -0
  97. package/plugins/workflow-runner/test/e2e.test.ts +370 -0
  98. package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
  99. package/plugins/workflow-runner/test/expressions.test.ts +397 -0
  100. package/plugins/workflow-runner/test/foundation.test.ts +126 -0
  101. package/plugins/workflow-runner/test/graceful.test.ts +139 -0
  102. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
  103. package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
  104. package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
  105. package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
  106. package/plugins/workflow-runner/test/loader.test.ts +617 -0
  107. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
  108. package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
  109. package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
  110. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
  111. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
  112. package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
  113. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
  114. package/plugins/workflow-runner/test/recovery.test.ts +265 -0
  115. package/plugins/workflow-runner/test/runner.test.ts +911 -0
  116. package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
  117. package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
  118. package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
  119. package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
  120. package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
  121. package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
  122. package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
  123. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
  124. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
  125. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
  126. package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
  127. package/plugins/workflow-runner/test/word.load.test.ts +208 -0
  128. package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
  129. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
  130. package/skills/browser-automation/SKILL.md +9 -19
  131. package/skills/web-crawler/SKILL.md +9 -11
  132. package/src/askBridge.ts +249 -0
  133. package/src/bootstrap/cwdArg.js +10 -3
  134. package/src/bootstrap/cwdArg.ts +31 -0
  135. package/src/bootstrap/workingDir.ts +48 -0
  136. package/src/cli/args.js +70 -7
  137. package/src/cli/args.ts +146 -0
  138. package/src/cli/computerUse.js +185 -0
  139. package/src/cli/computerUse.ts +221 -0
  140. package/src/cli/configMenu.js +274 -202
  141. package/src/cli/configMenu.tsx +521 -0
  142. package/src/cli/memoryAccess.js +45 -0
  143. package/src/cli/memoryAccess.ts +53 -0
  144. package/src/cli/print.js +119 -5
  145. package/src/cli/print.ts +597 -0
  146. package/src/cli/quickModelSetup.js +60 -0
  147. package/src/cli/quickModelSetup.tsx +96 -0
  148. package/src/cli/streamJson.js +80 -4
  149. package/src/cli/streamJson.ts +433 -0
  150. package/src/cli/wizards/advisorWizard.js +1 -0
  151. package/src/cli/wizards/advisorWizard.tsx +195 -0
  152. package/src/cli/wizards/agentRuntimeWizard.js +84 -0
  153. package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
  154. package/src/cli/wizards/browserWizard.js +55 -0
  155. package/src/cli/wizards/browserWizard.tsx +77 -0
  156. package/src/cli/wizards/contextAdvisorWizard.js +84 -0
  157. package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
  158. package/src/cli/wizards/fieldForm.js +7 -4
  159. package/src/cli/wizards/fieldForm.tsx +250 -0
  160. package/src/cli/wizards/imageWizard.js +4 -5
  161. package/src/cli/wizards/imageWizard.tsx +96 -0
  162. package/src/cli/wizards/openRouterWizard.tsx +48 -0
  163. package/src/cli/wizards/providerPresets.js +1 -8
  164. package/src/cli/wizards/providerPresets.ts +106 -0
  165. package/src/cli/wizards/providerWizard.js +152 -205
  166. package/src/cli/wizards/providerWizard.tsx +266 -0
  167. package/src/cli/wizards/searchWizard.tsx +47 -0
  168. package/src/cli/wizards/ttsWizard.js +4 -3
  169. package/src/cli/wizards/ttsWizard.tsx +61 -0
  170. package/src/config/configDefaults.js +25 -0
  171. package/src/config/configDefaults.ts +33 -0
  172. package/src/config/configDiagnostics.js +117 -0
  173. package/src/config/configDiagnostics.ts +166 -0
  174. package/src/config/configFile.js +52 -32
  175. package/src/config/configFile.ts +633 -0
  176. package/src/config/configManagement.js +126 -0
  177. package/src/config/configManagement.ts +159 -0
  178. package/src/config/configService.js +66 -11
  179. package/src/config/configService.ts +554 -0
  180. package/src/config/numericLimits.js +61 -0
  181. package/src/config/numericLimits.ts +80 -0
  182. package/src/config.ts +109 -0
  183. package/src/context/agentHome.ts +11 -0
  184. package/src/context/archive.ts +144 -0
  185. package/src/context/budgetPlan.js +54 -0
  186. package/src/context/budgetPlan.ts +88 -0
  187. package/src/context/compact.js +179 -106
  188. package/src/context/compact.ts +584 -0
  189. package/src/context/contextBundle.js +119 -0
  190. package/src/context/contextBundle.ts +176 -0
  191. package/src/context/contextLock.ts +84 -0
  192. package/src/context/microCompactLite.ts +218 -0
  193. package/src/context/persistContext.ts +177 -0
  194. package/src/context/reactiveCompact.js +19 -4
  195. package/src/context/reactiveCompact.ts +240 -0
  196. package/src/context/recentDirs.ts +42 -0
  197. package/src/context/sessionPath.ts +206 -0
  198. package/src/context/sessionRegistry.js +8 -2
  199. package/src/context/sessionRegistry.ts +414 -0
  200. package/src/context/snipCompact.ts +163 -0
  201. package/src/context/tokenCounter.ts +106 -0
  202. package/src/context/workspaceIndex.js +2 -2
  203. package/src/context/workspaceIndex.ts +751 -0
  204. package/src/entry.compiled.tsx +20 -0
  205. package/src/harness/claimValidation.js +9 -0
  206. package/src/harness/claimValidation.ts +22 -0
  207. package/src/harness/evidenceLedger.js +92 -0
  208. package/src/harness/evidenceLedger.ts +96 -0
  209. package/src/harness/stallGuard.js +60 -0
  210. package/src/harness/stallGuard.ts +81 -0
  211. package/src/humanIntervention.js +129 -0
  212. package/src/humanIntervention.ts +216 -0
  213. package/src/infra/verification.ts +295 -0
  214. package/src/irreversible/rules.js +216 -0
  215. package/src/irreversible/rules.ts +261 -0
  216. package/src/llm/client.js +40 -3
  217. package/src/llm/client.ts +621 -0
  218. package/src/llm/promptCache.js +27 -0
  219. package/src/llm/promptCache.ts +41 -0
  220. package/src/llm/providers/anthropicFormat.js +41 -5
  221. package/src/llm/providers/anthropicFormat.ts +290 -0
  222. package/src/llm/providers/openaiFormat.js +186 -8
  223. package/src/llm/providers/openaiFormat.ts +428 -0
  224. package/src/loop.js +614 -63
  225. package/src/loop.ts +1454 -0
  226. package/src/main.js +67 -10
  227. package/src/main.tsx +344 -0
  228. package/src/memory/codec.js +204 -0
  229. package/src/memory/codec.ts +249 -0
  230. package/src/memory/constants.js +34 -0
  231. package/src/memory/constants.ts +50 -0
  232. package/src/memory/episodicMemory.js +684 -0
  233. package/src/memory/episodicMemory.ts +815 -0
  234. package/src/memory/fileStore.js +148 -0
  235. package/src/memory/fileStore.ts +184 -0
  236. package/src/memory/memoryService.js +570 -0
  237. package/src/memory/memoryService.ts +859 -0
  238. package/src/memory/migration.js +128 -0
  239. package/src/memory/migration.ts +160 -0
  240. package/src/memory/repository.js +789 -0
  241. package/src/memory/repository.ts +893 -0
  242. package/src/memory/retrieval.js +108 -0
  243. package/src/memory/retrieval.ts +145 -0
  244. package/src/memory/searchIndex.js +107 -0
  245. package/src/memory/searchIndex.ts +153 -0
  246. package/src/memory/topicView.js +82 -0
  247. package/src/memory/topicView.ts +95 -0
  248. package/src/memory/turnJournal.js +95 -0
  249. package/src/memory/turnJournal.ts +120 -0
  250. package/src/memory/types.js +2 -0
  251. package/src/memory/types.ts +186 -0
  252. package/src/messageQueue.ts +123 -0
  253. package/src/multimodal.js +128 -46
  254. package/src/multimodal.ts +349 -0
  255. package/src/plugin-sdk.js +21 -1
  256. package/src/plugin-sdk.ts +134 -0
  257. package/src/plugins/builtinPluginIds.js +47 -0
  258. package/src/plugins/builtinPluginIds.ts +51 -0
  259. package/src/plugins/builtinRegistry.ts +43 -0
  260. package/src/plugins/commandRouter.js +40 -26
  261. package/src/plugins/commandRouter.ts +261 -0
  262. package/src/plugins/hookEngine.ts +341 -0
  263. package/src/plugins/pluginApi.ts +133 -0
  264. package/src/plugins/pluginLifecycle.ts +34 -0
  265. package/src/plugins/pluginLoader.ts +147 -0
  266. package/src/plugins/pluginManifest.js +127 -0
  267. package/src/plugins/pluginManifest.ts +152 -0
  268. package/src/plugins/pluginRunner.js +47 -2
  269. package/src/plugins/pluginRunner.ts +207 -0
  270. package/src/plugins/transcript.ts +208 -0
  271. package/src/prompts/lint.js +169 -0
  272. package/src/prompts/lint.ts +220 -0
  273. package/src/prompts/pluginList.js +32 -16
  274. package/src/prompts/pluginList.ts +86 -0
  275. package/src/prompts/policyRegistry.js +318 -0
  276. package/src/prompts/policyRegistry.ts +358 -0
  277. package/src/prompts/projectInstructions.js +24 -0
  278. package/src/prompts/projectInstructions.ts +81 -0
  279. package/src/prompts/promptAssembly.js +311 -202
  280. package/src/prompts/promptAssembly.ts +510 -0
  281. package/src/prompts/skillList.js +106 -45
  282. package/src/prompts/skillList.ts +198 -0
  283. package/src/prompts/system.js +31 -6
  284. package/src/prompts/system.ts +81 -0
  285. package/src/runtime/agentRuntimeContext.js +53 -0
  286. package/src/runtime/agentRuntimeContext.ts +112 -0
  287. package/src/session/runAgentTurn.js +71 -12
  288. package/src/session/runAgentTurn.ts +181 -0
  289. package/src/session/runTurn.js +29 -0
  290. package/src/session/runTurn.ts +160 -0
  291. package/src/session/runtimeStore.js +149 -0
  292. package/src/session/runtimeStore.ts +197 -0
  293. package/src/session/sessionState.js +79 -3
  294. package/src/session/sessionState.ts +134 -0
  295. package/src/toolGate.ts +50 -0
  296. package/src/tools/advisor/advisor.js +76 -15
  297. package/src/tools/advisor/advisor.ts +512 -0
  298. package/src/tools/agent/agent.js +245 -29
  299. package/src/tools/agent/agent.ts +837 -0
  300. package/src/tools/agent/agentTaskService.js +60 -0
  301. package/src/tools/agent/agentTaskService.ts +113 -0
  302. package/src/tools/agent/agentTypes.js +20 -8
  303. package/src/tools/agent/agentTypes.ts +94 -0
  304. package/src/tools/agent/coordinator.ts +111 -0
  305. package/src/tools/agent/worktree.ts +296 -0
  306. package/src/tools/ask/ask.js +17 -0
  307. package/src/tools/ask/ask.ts +122 -0
  308. package/src/tools/bash/attachImage.js +223 -0
  309. package/src/tools/bash/attachImage.ts +281 -0
  310. package/src/tools/bash/bash.js +132 -14
  311. package/src/tools/bash/bash.ts +642 -0
  312. package/src/tools/bash/semantics.ts +103 -0
  313. package/src/tools/bash/warnings.js +17 -11
  314. package/src/tools/bash/warnings.ts +117 -0
  315. package/src/tools/capabilities.js +42 -0
  316. package/src/tools/capabilities.ts +69 -0
  317. package/src/tools/computeruse/accessibilityPermission.js +261 -0
  318. package/src/tools/computeruse/accessibilityPermission.ts +294 -0
  319. package/src/tools/computeruse/axSearch.js +96 -0
  320. package/src/tools/computeruse/axSearch.ts +120 -0
  321. package/src/tools/computeruse/axSnap.js +272 -0
  322. package/src/tools/computeruse/axSnap.ts +349 -0
  323. package/src/tools/computeruse/capabilityLabels.js +95 -0
  324. package/src/tools/computeruse/capabilityLabels.ts +115 -0
  325. package/src/tools/computeruse/computerUseService.js +2235 -0
  326. package/src/tools/computeruse/computerUseService.ts +2686 -0
  327. package/src/tools/computeruse/computeruse.js +2742 -0
  328. package/src/tools/computeruse/computeruse.ts +3265 -0
  329. package/src/tools/computeruse/diagnosticTrace.js +154 -0
  330. package/src/tools/computeruse/diagnosticTrace.ts +195 -0
  331. package/src/tools/computeruse/diagnostics.js +369 -0
  332. package/src/tools/computeruse/diagnostics.ts +453 -0
  333. package/src/tools/computeruse/grants.js +53 -0
  334. package/src/tools/computeruse/grants.ts +70 -0
  335. package/src/tools/computeruse/humanBoundary.js +159 -0
  336. package/src/tools/computeruse/humanBoundary.ts +199 -0
  337. package/src/tools/computeruse/intervention.js +77 -0
  338. package/src/tools/computeruse/intervention.ts +110 -0
  339. package/src/tools/computeruse/manualSchema.js +709 -0
  340. package/src/tools/computeruse/manualSchema.ts +916 -0
  341. package/src/tools/computeruse/observationImages.js +705 -0
  342. package/src/tools/computeruse/observationImages.ts +889 -0
  343. package/src/tools/computeruse/protocol.js +60 -0
  344. package/src/tools/computeruse/protocol.ts +515 -0
  345. package/src/tools/computeruse/telemetryLog.js +57 -0
  346. package/src/tools/computeruse/telemetryLog.ts +67 -0
  347. package/src/tools/computeruse/vision/appGuide.js +325 -0
  348. package/src/tools/computeruse/vision/appGuide.ts +482 -0
  349. package/src/tools/computeruse/vision/focus.js +291 -0
  350. package/src/tools/computeruse/vision/focus.ts +382 -0
  351. package/src/tools/computeruse/vision/frames.js +248 -0
  352. package/src/tools/computeruse/vision/frames.ts +329 -0
  353. package/src/tools/computeruse/vision/geometry.js +131 -0
  354. package/src/tools/computeruse/vision/geometry.ts +163 -0
  355. package/src/tools/computeruse/vision/imaging.js +201 -0
  356. package/src/tools/computeruse/vision/imaging.ts +281 -0
  357. package/src/tools/computeruse/vision/png.js +274 -0
  358. package/src/tools/computeruse/vision/png.ts +317 -0
  359. package/src/tools/computeruse/vision/raster.js +320 -0
  360. package/src/tools/computeruse/vision/raster.ts +408 -0
  361. package/src/tools/computeruse/vision/settle.js +131 -0
  362. package/src/tools/computeruse/vision/settle.ts +220 -0
  363. package/src/tools/computeruse/vision/telemetry.js +196 -0
  364. package/src/tools/computeruse/vision/telemetry.ts +241 -0
  365. package/src/tools/computeruse/visionOcr.js +115 -0
  366. package/src/tools/computeruse/visionOcr.ts +128 -0
  367. package/src/tools/computeruse/waitFor.js +237 -0
  368. package/src/tools/computeruse/waitFor.ts +291 -0
  369. package/src/tools/download/download.js +10 -6
  370. package/src/tools/download/download.ts +187 -0
  371. package/src/tools/edit/edit.js +30 -18
  372. package/src/tools/edit/edit.ts +349 -0
  373. package/src/tools/edit/multi-edit.js +25 -14
  374. package/src/tools/edit/multi-edit.ts +223 -0
  375. package/src/tools/effectiveToolSet.js +73 -0
  376. package/src/tools/effectiveToolSet.ts +91 -0
  377. package/src/tools/genimage/genimage.ts +257 -0
  378. package/src/tools/genspeech/genspeech.ts +83 -0
  379. package/src/tools/glob/glob.js +176 -29
  380. package/src/tools/glob/glob.ts +283 -0
  381. package/src/tools/grep/grep.js +61 -19
  382. package/src/tools/grep/grep.ts +239 -0
  383. package/src/tools/grep/rgPath.ts +206 -0
  384. package/src/tools/index.js +99 -5
  385. package/src/tools/index.ts +293 -0
  386. package/src/tools/lsp/config.js +113 -0
  387. package/src/tools/lsp/config.ts +113 -0
  388. package/src/tools/lsp/lsp.js +256 -0
  389. package/src/tools/lsp/lsp.ts +274 -0
  390. package/src/tools/lsp/service.js +303 -0
  391. package/src/tools/lsp/service.ts +296 -0
  392. package/src/tools/memory/memory.js +520 -0
  393. package/src/tools/memory/memory.ts +625 -0
  394. package/src/tools/memory/policy.js +53 -0
  395. package/src/tools/memory/policy.ts +72 -0
  396. package/src/tools/notebook/notebookEdit.js +114 -0
  397. package/src/tools/notebook/notebookEdit.ts +126 -0
  398. package/src/tools/powershell/powershell.js +210 -0
  399. package/src/tools/powershell/powershell.ts +239 -0
  400. package/src/tools/presentresult/presentresult.js +145 -0
  401. package/src/tools/presentresult/presentresult.ts +156 -0
  402. package/src/tools/read/read.js +371 -163
  403. package/src/tools/read/read.ts +558 -0
  404. package/src/tools/shared/diffSummary.js +44 -0
  405. package/src/tools/shared/diffSummary.ts +63 -0
  406. package/src/tools/shared/fileActivity.js +51 -0
  407. package/src/tools/shared/fileActivity.ts +66 -0
  408. package/src/tools/shared/fileMutationBus.js +13 -0
  409. package/src/tools/shared/fileMutationBus.ts +14 -0
  410. package/src/tools/shared/fileState.js +73 -65
  411. package/src/tools/shared/fileState.ts +145 -0
  412. package/src/tools/shared/fileUtils.js +81 -4
  413. package/src/tools/shared/fileUtils.ts +504 -0
  414. package/src/tools/shared/killTree.ts +50 -0
  415. package/src/tools/shared/permanentDeleteGuard.js +26 -0
  416. package/src/tools/shared/permanentDeleteGuard.ts +34 -0
  417. package/src/tools/shared/schemas.ts +20 -0
  418. package/src/tools/shared/toolOutputStore.js +97 -0
  419. package/src/tools/shared/toolOutputStore.ts +110 -0
  420. package/src/tools/skill/skill.js +86 -0
  421. package/src/tools/skill/skill.ts +94 -0
  422. package/src/tools/snip/snip.js +67 -0
  423. package/src/tools/snip/snip.ts +80 -0
  424. package/src/tools/tasks/backgroundTaskService.js +275 -0
  425. package/src/tools/tasks/backgroundTaskService.ts +369 -0
  426. package/src/tools/tasks/processEvidence.js +24 -0
  427. package/src/tools/tasks/processEvidence.ts +34 -0
  428. package/src/tools/tasks/taskManagement.js +163 -0
  429. package/src/tools/tasks/taskManagement.ts +180 -0
  430. package/src/tools/tasks/taskOutput.js +113 -0
  431. package/src/tools/tasks/taskOutput.ts +145 -0
  432. package/src/tools/tasks/taskStop.js +74 -0
  433. package/src/tools/tasks/taskStop.ts +99 -0
  434. package/src/tools/tasks/taskStore.js +46 -0
  435. package/src/tools/tasks/taskStore.ts +77 -0
  436. package/src/tools/terminal/terminalCapture.js +167 -0
  437. package/src/tools/terminal/terminalCapture.ts +179 -0
  438. package/src/tools/trash/trash.js +233 -0
  439. package/src/tools/trash/trash.ts +283 -0
  440. package/src/tools/types.js +1 -1
  441. package/src/tools/types.ts +24 -0
  442. package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
  443. package/src/tools/webbrowser/browser.js +214 -11
  444. package/src/tools/webbrowser/browser.ts +293 -0
  445. package/src/tools/webbrowser/browserControl.js +153 -48
  446. package/src/tools/webbrowser/browserControl.ts +262 -0
  447. package/src/tools/webbrowser/browserDaemon.js +147 -39
  448. package/src/tools/webbrowser/browserDaemon.ts +270 -0
  449. package/src/tools/webbrowser/browserOutcome.js +273 -0
  450. package/src/tools/webbrowser/browserOutcome.ts +306 -0
  451. package/src/tools/webbrowser/browserRunRegistry.js +150 -0
  452. package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
  453. package/src/tools/webbrowser/browserService.js +20 -6
  454. package/src/tools/webbrowser/browserService.ts +56 -0
  455. package/src/tools/webbrowser/browserStream.js +30 -1
  456. package/src/tools/webbrowser/browserStream.ts +216 -0
  457. package/src/tools/webbrowser/browserTabs.js +143 -0
  458. package/src/tools/webbrowser/browserTabs.ts +210 -0
  459. package/src/tools/webbrowser/webbrowser.js +831 -150
  460. package/src/tools/webbrowser/webbrowser.ts +1537 -0
  461. package/src/tools/webfetch/preapproved.ts +274 -0
  462. package/src/tools/webfetch/webfetch.js +24 -23
  463. package/src/tools/webfetch/webfetch.ts +383 -0
  464. package/src/tools/websearch/websearch.js +12 -11
  465. package/src/tools/websearch/websearch.ts +292 -0
  466. package/src/tools/worktree/sessionWorktree.js +128 -0
  467. package/src/tools/worktree/sessionWorktree.ts +158 -0
  468. package/src/tools/write/write.js +27 -44
  469. package/src/tools/write/write.ts +136 -0
  470. package/src/types.ts +960 -0
  471. package/src/ui/App.js +216 -12
  472. package/src/ui/App.tsx +879 -0
  473. package/src/ui/AskQuestion.tsx +300 -0
  474. package/src/ui/InputBox.js +77 -1
  475. package/src/ui/InputBox.tsx +567 -0
  476. package/src/ui/LiveArea.tsx +344 -0
  477. package/src/ui/MessageList.js +27 -6
  478. package/src/ui/MessageList.tsx +559 -0
  479. package/src/ui/ModelPicker.tsx +107 -0
  480. package/src/ui/QueuedMessagesBanner.tsx +51 -0
  481. package/src/ui/Root.js +6 -8
  482. package/src/ui/Root.tsx +100 -0
  483. package/src/ui/SessionPicker.tsx +169 -0
  484. package/src/ui/StatusLine.js +22 -1
  485. package/src/ui/StatusLine.tsx +145 -0
  486. package/src/ui/WorkspacePicker.tsx +84 -0
  487. package/src/ui/advisorCommand.js +54 -0
  488. package/src/ui/advisorCommand.ts +63 -0
  489. package/src/ui/computerCommand.js +19 -0
  490. package/src/ui/computerCommand.ts +35 -0
  491. package/src/ui/figures.ts +45 -0
  492. package/src/ui/hooks/askSlot.ts +112 -0
  493. package/src/ui/hooks/useChat.js +271 -7
  494. package/src/ui/hooks/useChat.ts +1578 -0
  495. package/src/ui/hooks/useInputHistory.ts +235 -0
  496. package/src/ui/hooks/usePasteHandler.ts +220 -0
  497. package/src/ui/hooks/useTerminalSize.ts +41 -0
  498. package/src/ui/hooks/useTextBuffer.ts +108 -0
  499. package/src/ui/hooks/useTokenUsage.ts +44 -0
  500. package/src/ui/liveViewport.ts +189 -0
  501. package/src/ui/markdown.ts +230 -0
  502. package/src/ui/textBuffer.ts +239 -0
  503. package/src/ui/theme.ts +35 -0
  504. package/src/utils/clipboardImage.js +358 -0
  505. package/src/utils/clipboardImage.ts +420 -0
  506. package/src/utils/debugLog.ts +50 -0
  507. package/src/utils/greenRoot.ts +35 -0
  508. package/src/utils/packageRoot.ts +40 -0
  509. package/src/utils/pathContains.js +28 -0
  510. package/src/utils/pathContains.ts +31 -0
  511. package/src/utils/resourcePaths.ts +71 -0
  512. package/src/utils/stripThink.ts +49 -0
  513. package/src/utils/zodToJson.ts +73 -0
  514. package/src/web/browserBridge.js +365 -66
  515. package/src/web/browserBridge.ts +672 -0
  516. package/src/web/protocol.ts +1804 -0
  517. package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
  518. package/workflows/ppt.yaml +13 -2
  519. package/workflows/wechat-reading-shots.yaml +315 -0
  520. package/workflows/word.yaml +11 -1
  521. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  522. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  523. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  524. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  525. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  526. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  527. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  528. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  529. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  530. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  531. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  532. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  533. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  534. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  535. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  536. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  537. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  538. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  539. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  540. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  541. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  542. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  543. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  544. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  545. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  546. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  547. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  548. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  549. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  550. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  551. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  552. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  553. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  554. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  555. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  556. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  557. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  558. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  559. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  560. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  561. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  562. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  563. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  564. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  565. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  566. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  567. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  568. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  569. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  570. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  571. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  572. /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
  573. /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * ============================================================
3
+ * vision/frames.ts —— 帧差、稳态、事件锚点(纯函数)
4
+ * ------------------------------------------------------------
5
+ * 这是「把判断从慢循环下沉到快循环」的落点:
6
+ *
7
+ * 慢循环(模型)Think→Act→Observe→Review 秒级,千 token
8
+ * 快循环(本模块)采帧→判变化→判稳态 毫秒级,零 token
9
+ *
10
+ * 全部运算在 **1/16 分辨率的灰度帧**(480×270)上做 —— 13 万像素的
11
+ * absdiff + 阈值 + 包围盒在 typed array 上约 0.3ms,而 30fps 的预算是 33ms。
12
+ * 这就是「不需要 GPU、也不需要 OpenCV」的全部依据。
13
+ *
14
+ * 职责边界(与 Rust 原生侧的分工):
15
+ * - 高频采样与逐帧 absdiff 在原生侧做,跨 IPC 传时间线而不是像素
16
+ * - 本模块消费**时间线**(`{t, ratio, bbox}[]`)做稳态/聚类/锚点判断,
17
+ * 并在 TS 侧直接比对 ring 帧时复用同一套 `diffFrames`
18
+ *
19
+ * 一条设计上的硬教训(写进代码免得后人改回去):
20
+ *
21
+ * ★ **稳态判据不能是「全屏静止」。**
22
+ * 微信有动图表情和视频预览、剪映有播放预览、任何带实时日志的界面都在动,
23
+ * 全屏判据在这些应用上**永远为假**,settle 退化成纯超时,闭环原语全废。
24
+ * 正确判据是「**我操作的那块区域**静止 AND 焦点未被抢走」——
25
+ * 前者在这里,后者在 focus.ts。
26
+ * ============================================================
27
+ */
28
+ import { unionRect } from './geometry.js';
29
+ /** 单像素灰度差超过它才算「变了」。8 足以滤掉视频压缩噪点与抗锯齿抖动,又能捕获真实 UI 变化。 */
30
+ export const DEFAULT_PIXEL_THRESHOLD = 8;
31
+ /** 变化面积占比超过它就判「全局变化」(切窗口/跳页/播放视频),观测形态随之切换。 */
32
+ export const GLOBAL_CHANGE_RATIO = 0.4;
33
+ /** 低于这个变化占比算「安静」(默认 ≈ 480×270 上 130 像素)。 */
34
+ export const DEFAULT_QUIET_RATIO = 0.001;
35
+ /** 两帧灰度差分。 */
36
+ export function diffFrames(a, b, width, height, options = {}) {
37
+ const threshold = options.threshold ?? DEFAULT_PIXEL_THRESHOLD;
38
+ const roi = options.roi;
39
+ const x0 = roi ? Math.max(0, roi.x) : 0;
40
+ const y0 = roi ? Math.max(0, roi.y) : 0;
41
+ const x1 = roi ? Math.min(width, roi.x + roi.width) : width;
42
+ const y1 = roi ? Math.min(height, roi.y + roi.height) : height;
43
+ let changed = 0;
44
+ let minX = Infinity;
45
+ let minY = Infinity;
46
+ let maxX = -1;
47
+ let maxY = -1;
48
+ for (let y = y0; y < y1; y += 1) {
49
+ const row = y * width;
50
+ for (let x = x0; x < x1; x += 1) {
51
+ const i = row + x;
52
+ const av = a[i];
53
+ const bv = b[i];
54
+ const delta = av > bv ? av - bv : bv - av;
55
+ if (delta < threshold)
56
+ continue;
57
+ changed += 1;
58
+ if (x < minX)
59
+ minX = x;
60
+ if (x > maxX)
61
+ maxX = x;
62
+ if (y < minY)
63
+ minY = y;
64
+ if (y > maxY)
65
+ maxY = y;
66
+ }
67
+ }
68
+ const totalPixels = Math.max(1, (x1 - x0) * (y1 - y0));
69
+ return {
70
+ changedPixels: changed,
71
+ totalPixels,
72
+ ratio: changed / totalPixels,
73
+ bbox: maxX >= 0
74
+ ? { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 }
75
+ : null,
76
+ };
77
+ }
78
+ /** 变化是否已经大到「整屏换了」的程度 —— 观测形态要从裁剪切成前后两张全屏。 */
79
+ export function isGlobalChange(diff, ratio = GLOBAL_CHANGE_RATIO) {
80
+ return diff.ratio >= ratio;
81
+ }
82
+ /**
83
+ * 从一段帧序列算出变化时间线。相邻帧两两比较,产出每个时刻的变化量与位置。
84
+ * 输出长度 = frames.length - 1。
85
+ */
86
+ export function buildTimeline(frames, width, height, options = {}) {
87
+ const out = [];
88
+ for (let i = 1; i < frames.length; i += 1) {
89
+ const diff = diffFrames(frames[i - 1].gray, frames[i].gray, width, height, options);
90
+ out.push({
91
+ t: frames[i].t,
92
+ changedPixels: diff.changedPixels,
93
+ ratio: diff.ratio,
94
+ bbox: diff.bbox,
95
+ });
96
+ }
97
+ return out;
98
+ }
99
+ /**
100
+ * 稳态判定:从**最后一帧往回**看,连续多久没有超过 quietRatio 的变化。
101
+ *
102
+ * 返回 stableMs 而不是布尔值,调用方自己比阈值 —— 因为不同动作需要的静默
103
+ * 窗口不同(点击后 200ms 够了,页面跳转要 800ms)。
104
+ */
105
+ export function stableMillis(timeline, now, quietRatio = DEFAULT_QUIET_RATIO) {
106
+ if (timeline.length === 0)
107
+ return 0;
108
+ let lastNoisy = null;
109
+ for (let i = timeline.length - 1; i >= 0; i -= 1) {
110
+ if (timeline[i].ratio > quietRatio) {
111
+ lastNoisy = timeline[i].t;
112
+ break;
113
+ }
114
+ }
115
+ if (lastNoisy === null)
116
+ return now - timeline[0].t;
117
+ return now - lastNoisy;
118
+ }
119
+ /**
120
+ * 本段时间线里所有变化的合并包围盒 —— 用于「这几秒里屏幕的哪一块动过」。
121
+ * 多区域同时变化时会合并成一个大框;需要分开的场景用 clusterChanges。
122
+ */
123
+ export function overallChangeBox(timeline, quietRatio = DEFAULT_QUIET_RATIO) {
124
+ let box = null;
125
+ for (const point of timeline) {
126
+ if (point.ratio <= quietRatio || !point.bbox)
127
+ continue;
128
+ box = unionRect(box, point.bbox);
129
+ }
130
+ return box;
131
+ }
132
+ /**
133
+ * 把时间线上的变化按「是否相邻/重叠」聚成若干独立区域。
134
+ *
135
+ * 存在的理由是**多 app 联合操作**:剪映在导出(进度条在动)、浏览器在上传
136
+ * (状态条在动),这两块离得很远,合并成一个大框等于把整屏都框进去、
137
+ * ROI 裁剪就失去意义了。
138
+ *
139
+ * 返回按面积降序,最多 maxClusters 个。
140
+ */
141
+ export function clusterChanges(timeline, options = {}) {
142
+ const quietRatio = options.quietRatio ?? DEFAULT_QUIET_RATIO;
143
+ const maxClusters = options.maxClusters ?? 3;
144
+ const mergeSlack = options.mergeSlack ?? 0.5;
145
+ const clusters = [];
146
+ for (const point of timeline) {
147
+ if (point.ratio <= quietRatio || !point.bbox)
148
+ continue;
149
+ const hit = clusters.find((c) => nearby(c, point.bbox, mergeSlack));
150
+ if (hit) {
151
+ const merged = unionRect(hit, point.bbox);
152
+ hit.x = merged.x;
153
+ hit.y = merged.y;
154
+ hit.width = merged.width;
155
+ hit.height = merged.height;
156
+ }
157
+ else {
158
+ clusters.push({ ...point.bbox });
159
+ }
160
+ }
161
+ return clusters
162
+ .sort((p, q) => q.width * q.height - p.width * p.height)
163
+ .slice(0, maxClusters);
164
+ }
165
+ function nearby(a, b, slack) {
166
+ const gapX = Math.max(0, Math.max(a.x, b.x) - Math.min(a.x + a.width, b.x + b.width));
167
+ const gapY = Math.max(0, Math.max(a.y, b.y) - Math.min(a.y + a.height, b.y + b.height));
168
+ return (gapX <= Math.max(a.width, b.width) * slack &&
169
+ gapY <= Math.max(a.height, b.height) * slack);
170
+ }
171
+ /**
172
+ * 事件锚点:从帧序列里挑出**最有信息量的** N 个时刻。
173
+ *
174
+ * 为什么不等间隔采样:界面变化是**突发**的。8.5 秒静止、最后 0.5 秒发生全部
175
+ * 事情的情况下,等间隔 9 帧里有 8 帧是同一张图 —— 花了 9 倍的钱买到 1 帧的信息。
176
+ *
177
+ * 锚点语义:动作前 → 首次变化 → 变化峰值 → 稳定后。
178
+ */
179
+ export function pickEventAnchors(frames, timeline, options = {}) {
180
+ const quietRatio = options.quietRatio ?? DEFAULT_QUIET_RATIO;
181
+ const max = options.max ?? 4;
182
+ if (frames.length === 0)
183
+ return [];
184
+ if (timeline.length === 0) {
185
+ return [{ label: '当前', index: frames.length - 1, t: frames[frames.length - 1].t }];
186
+ }
187
+ const anchors = [];
188
+ const push = (label, index) => {
189
+ if (index < 0 || index >= frames.length)
190
+ return;
191
+ anchors.push({ label, index, t: frames[index].t });
192
+ };
193
+ push('动作前', 0);
194
+ const firstChange = timeline.findIndex((p) => p.ratio > quietRatio);
195
+ if (firstChange >= 0)
196
+ push('首次变化', firstChange + 1);
197
+ let peakIndex = -1;
198
+ let peakRatio = quietRatio;
199
+ timeline.forEach((point, i) => {
200
+ if (point.ratio > peakRatio) {
201
+ peakRatio = point.ratio;
202
+ peakIndex = i;
203
+ }
204
+ });
205
+ if (peakIndex >= 0)
206
+ push('变化峰值', peakIndex + 1);
207
+ push('稳定后', frames.length - 1);
208
+ // 同一帧被多个语义命中时保留**最先**的标签(更能解释「为什么选它」)
209
+ const seen = new Set();
210
+ return anchors
211
+ .filter((a) => (seen.has(a.index) ? false : (seen.add(a.index), true)))
212
+ .sort((p, q) => p.index - q.index)
213
+ .slice(0, max);
214
+ }
215
+ /**
216
+ * 环形缓冲。帧**只在内存**,永不落盘 —— 持续截屏在客户机器上是隐私敏感的,
217
+ * 这条是产品要求不是性能优化。
218
+ */
219
+ export class FrameRing {
220
+ capacity;
221
+ items = [];
222
+ constructor(capacity) {
223
+ this.capacity = capacity;
224
+ }
225
+ push(record) {
226
+ this.items.push(record);
227
+ if (this.items.length > this.capacity) {
228
+ this.items.splice(0, this.items.length - this.capacity);
229
+ }
230
+ }
231
+ /** 取时间戳 ≥ since 的帧(升序)。 */
232
+ since(since) {
233
+ return this.items.filter((f) => f.t >= since);
234
+ }
235
+ /** 最近 n 帧(升序)。 */
236
+ recent(n) {
237
+ return n <= 0 ? [] : this.items.slice(-n);
238
+ }
239
+ get latest() {
240
+ return this.items[this.items.length - 1] ?? null;
241
+ }
242
+ get size() {
243
+ return this.items.length;
244
+ }
245
+ clear() {
246
+ this.items = [];
247
+ }
248
+ }
@@ -0,0 +1,329 @@
1
+ /**
2
+ * ============================================================
3
+ * vision/frames.ts —— 帧差、稳态、事件锚点(纯函数)
4
+ * ------------------------------------------------------------
5
+ * 这是「把判断从慢循环下沉到快循环」的落点:
6
+ *
7
+ * 慢循环(模型)Think→Act→Observe→Review 秒级,千 token
8
+ * 快循环(本模块)采帧→判变化→判稳态 毫秒级,零 token
9
+ *
10
+ * 全部运算在 **1/16 分辨率的灰度帧**(480×270)上做 —— 13 万像素的
11
+ * absdiff + 阈值 + 包围盒在 typed array 上约 0.3ms,而 30fps 的预算是 33ms。
12
+ * 这就是「不需要 GPU、也不需要 OpenCV」的全部依据。
13
+ *
14
+ * 职责边界(与 Rust 原生侧的分工):
15
+ * - 高频采样与逐帧 absdiff 在原生侧做,跨 IPC 传时间线而不是像素
16
+ * - 本模块消费**时间线**(`{t, ratio, bbox}[]`)做稳态/聚类/锚点判断,
17
+ * 并在 TS 侧直接比对 ring 帧时复用同一套 `diffFrames`
18
+ *
19
+ * 一条设计上的硬教训(写进代码免得后人改回去):
20
+ *
21
+ * ★ **稳态判据不能是「全屏静止」。**
22
+ * 微信有动图表情和视频预览、剪映有播放预览、任何带实时日志的界面都在动,
23
+ * 全屏判据在这些应用上**永远为假**,settle 退化成纯超时,闭环原语全废。
24
+ * 正确判据是「**我操作的那块区域**静止 AND 焦点未被抢走」——
25
+ * 前者在这里,后者在 focus.ts。
26
+ * ============================================================
27
+ */
28
+
29
+ import { unionRect } from './geometry.js';
30
+ import type { Rect } from './raster.js';
31
+
32
+ /** 单像素灰度差超过它才算「变了」。8 足以滤掉视频压缩噪点与抗锯齿抖动,又能捕获真实 UI 变化。 */
33
+ export const DEFAULT_PIXEL_THRESHOLD = 8;
34
+
35
+ /** 变化面积占比超过它就判「全局变化」(切窗口/跳页/播放视频),观测形态随之切换。 */
36
+ export const GLOBAL_CHANGE_RATIO = 0.4;
37
+
38
+ /** 低于这个变化占比算「安静」(默认 ≈ 480×270 上 130 像素)。 */
39
+ export const DEFAULT_QUIET_RATIO = 0.001;
40
+
41
+ export interface FrameDiff {
42
+ changedPixels: number;
43
+ totalPixels: number;
44
+ ratio: number;
45
+ bbox: Rect | null;
46
+ }
47
+
48
+ export interface DiffOptions {
49
+ threshold?: number;
50
+ /** 限定只看某块区域 —— settle 判据用它把「屏幕别处在放视频」排除掉。 */
51
+ roi?: Rect;
52
+ }
53
+
54
+ /** 两帧灰度差分。 */
55
+ export function diffFrames(
56
+ a: Uint8Array,
57
+ b: Uint8Array,
58
+ width: number,
59
+ height: number,
60
+ options: DiffOptions = {},
61
+ ): FrameDiff {
62
+ const threshold = options.threshold ?? DEFAULT_PIXEL_THRESHOLD;
63
+ const roi = options.roi;
64
+
65
+ const x0 = roi ? Math.max(0, roi.x) : 0;
66
+ const y0 = roi ? Math.max(0, roi.y) : 0;
67
+ const x1 = roi ? Math.min(width, roi.x + roi.width) : width;
68
+ const y1 = roi ? Math.min(height, roi.y + roi.height) : height;
69
+
70
+ let changed = 0;
71
+ let minX = Infinity;
72
+ let minY = Infinity;
73
+ let maxX = -1;
74
+ let maxY = -1;
75
+
76
+ for (let y = y0; y < y1; y += 1) {
77
+ const row = y * width;
78
+ for (let x = x0; x < x1; x += 1) {
79
+ const i = row + x;
80
+ const av = a[i]!;
81
+ const bv = b[i]!;
82
+ const delta = av > bv ? av - bv : bv - av;
83
+ if (delta < threshold) continue;
84
+ changed += 1;
85
+ if (x < minX) minX = x;
86
+ if (x > maxX) maxX = x;
87
+ if (y < minY) minY = y;
88
+ if (y > maxY) maxY = y;
89
+ }
90
+ }
91
+
92
+ const totalPixels = Math.max(1, (x1 - x0) * (y1 - y0));
93
+ return {
94
+ changedPixels: changed,
95
+ totalPixels,
96
+ ratio: changed / totalPixels,
97
+ bbox:
98
+ maxX >= 0
99
+ ? { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 }
100
+ : null,
101
+ };
102
+ }
103
+
104
+ /** 变化是否已经大到「整屏换了」的程度 —— 观测形态要从裁剪切成前后两张全屏。 */
105
+ export function isGlobalChange(diff: FrameDiff, ratio = GLOBAL_CHANGE_RATIO): boolean {
106
+ return diff.ratio >= ratio;
107
+ }
108
+
109
+ /** 一条帧记录。环形缓冲存的就是它。 */
110
+ export interface FrameRecord {
111
+ t: number;
112
+ gray: Uint8Array;
113
+ }
114
+
115
+ /** 时间线上的一个采样点。原生 settle 返回的就是这个形状。 */
116
+ export interface TimelinePoint {
117
+ t: number;
118
+ changedPixels?: number;
119
+ ratio: number;
120
+ bbox?: Rect | null;
121
+ }
122
+
123
+ /**
124
+ * 从一段帧序列算出变化时间线。相邻帧两两比较,产出每个时刻的变化量与位置。
125
+ * 输出长度 = frames.length - 1。
126
+ */
127
+ export function buildTimeline(
128
+ frames: FrameRecord[],
129
+ width: number,
130
+ height: number,
131
+ options: DiffOptions = {},
132
+ ): TimelinePoint[] {
133
+ const out: TimelinePoint[] = [];
134
+ for (let i = 1; i < frames.length; i += 1) {
135
+ const diff = diffFrames(frames[i - 1]!.gray, frames[i]!.gray, width, height, options);
136
+ out.push({
137
+ t: frames[i]!.t,
138
+ changedPixels: diff.changedPixels,
139
+ ratio: diff.ratio,
140
+ bbox: diff.bbox,
141
+ });
142
+ }
143
+ return out;
144
+ }
145
+
146
+ /**
147
+ * 稳态判定:从**最后一帧往回**看,连续多久没有超过 quietRatio 的变化。
148
+ *
149
+ * 返回 stableMs 而不是布尔值,调用方自己比阈值 —— 因为不同动作需要的静默
150
+ * 窗口不同(点击后 200ms 够了,页面跳转要 800ms)。
151
+ */
152
+ export function stableMillis(
153
+ timeline: TimelinePoint[],
154
+ now: number,
155
+ quietRatio = DEFAULT_QUIET_RATIO,
156
+ ): number {
157
+ if (timeline.length === 0) return 0;
158
+ let lastNoisy: number | null = null;
159
+ for (let i = timeline.length - 1; i >= 0; i -= 1) {
160
+ if (timeline[i]!.ratio > quietRatio) {
161
+ lastNoisy = timeline[i]!.t;
162
+ break;
163
+ }
164
+ }
165
+ if (lastNoisy === null) return now - timeline[0]!.t;
166
+ return now - lastNoisy;
167
+ }
168
+
169
+ /**
170
+ * 本段时间线里所有变化的合并包围盒 —— 用于「这几秒里屏幕的哪一块动过」。
171
+ * 多区域同时变化时会合并成一个大框;需要分开的场景用 clusterChanges。
172
+ */
173
+ export function overallChangeBox(
174
+ timeline: TimelinePoint[],
175
+ quietRatio = DEFAULT_QUIET_RATIO,
176
+ ): Rect | null {
177
+ let box: Rect | null = null;
178
+ for (const point of timeline) {
179
+ if (point.ratio <= quietRatio || !point.bbox) continue;
180
+ box = unionRect(box, point.bbox);
181
+ }
182
+ return box;
183
+ }
184
+
185
+ export interface ClusterOptions {
186
+ quietRatio?: number;
187
+ maxClusters?: number;
188
+ /** 两框间距小于各自尺寸的这个比例就并成一簇。 */
189
+ mergeSlack?: number;
190
+ }
191
+
192
+ /**
193
+ * 把时间线上的变化按「是否相邻/重叠」聚成若干独立区域。
194
+ *
195
+ * 存在的理由是**多 app 联合操作**:剪映在导出(进度条在动)、浏览器在上传
196
+ * (状态条在动),这两块离得很远,合并成一个大框等于把整屏都框进去、
197
+ * ROI 裁剪就失去意义了。
198
+ *
199
+ * 返回按面积降序,最多 maxClusters 个。
200
+ */
201
+ export function clusterChanges(timeline: TimelinePoint[], options: ClusterOptions = {}): Rect[] {
202
+ const quietRatio = options.quietRatio ?? DEFAULT_QUIET_RATIO;
203
+ const maxClusters = options.maxClusters ?? 3;
204
+ const mergeSlack = options.mergeSlack ?? 0.5;
205
+
206
+ const clusters: Rect[] = [];
207
+ for (const point of timeline) {
208
+ if (point.ratio <= quietRatio || !point.bbox) continue;
209
+ const hit = clusters.find((c) => nearby(c, point.bbox!, mergeSlack));
210
+ if (hit) {
211
+ const merged = unionRect(hit, point.bbox)!;
212
+ hit.x = merged.x;
213
+ hit.y = merged.y;
214
+ hit.width = merged.width;
215
+ hit.height = merged.height;
216
+ } else {
217
+ clusters.push({ ...point.bbox });
218
+ }
219
+ }
220
+
221
+ return clusters
222
+ .sort((p, q) => q.width * q.height - p.width * p.height)
223
+ .slice(0, maxClusters);
224
+ }
225
+
226
+ function nearby(a: Rect, b: Rect, slack: number): boolean {
227
+ const gapX = Math.max(0, Math.max(a.x, b.x) - Math.min(a.x + a.width, b.x + b.width));
228
+ const gapY = Math.max(0, Math.max(a.y, b.y) - Math.min(a.y + a.height, b.y + b.height));
229
+ return (
230
+ gapX <= Math.max(a.width, b.width) * slack &&
231
+ gapY <= Math.max(a.height, b.height) * slack
232
+ );
233
+ }
234
+
235
+ export interface EventAnchor {
236
+ label: string;
237
+ index: number;
238
+ t: number;
239
+ }
240
+
241
+ /**
242
+ * 事件锚点:从帧序列里挑出**最有信息量的** N 个时刻。
243
+ *
244
+ * 为什么不等间隔采样:界面变化是**突发**的。8.5 秒静止、最后 0.5 秒发生全部
245
+ * 事情的情况下,等间隔 9 帧里有 8 帧是同一张图 —— 花了 9 倍的钱买到 1 帧的信息。
246
+ *
247
+ * 锚点语义:动作前 → 首次变化 → 变化峰值 → 稳定后。
248
+ */
249
+ export function pickEventAnchors(
250
+ frames: Pick<FrameRecord, 't'>[],
251
+ timeline: TimelinePoint[],
252
+ options: { quietRatio?: number; max?: number } = {},
253
+ ): EventAnchor[] {
254
+ const quietRatio = options.quietRatio ?? DEFAULT_QUIET_RATIO;
255
+ const max = options.max ?? 4;
256
+ if (frames.length === 0) return [];
257
+ if (timeline.length === 0) {
258
+ return [{ label: '当前', index: frames.length - 1, t: frames[frames.length - 1]!.t }];
259
+ }
260
+
261
+ const anchors: EventAnchor[] = [];
262
+ const push = (label: string, index: number): void => {
263
+ if (index < 0 || index >= frames.length) return;
264
+ anchors.push({ label, index, t: frames[index]!.t });
265
+ };
266
+
267
+ push('动作前', 0);
268
+
269
+ const firstChange = timeline.findIndex((p) => p.ratio > quietRatio);
270
+ if (firstChange >= 0) push('首次变化', firstChange + 1);
271
+
272
+ let peakIndex = -1;
273
+ let peakRatio = quietRatio;
274
+ timeline.forEach((point, i) => {
275
+ if (point.ratio > peakRatio) {
276
+ peakRatio = point.ratio;
277
+ peakIndex = i;
278
+ }
279
+ });
280
+ if (peakIndex >= 0) push('变化峰值', peakIndex + 1);
281
+
282
+ push('稳定后', frames.length - 1);
283
+
284
+ // 同一帧被多个语义命中时保留**最先**的标签(更能解释「为什么选它」)
285
+ const seen = new Set<number>();
286
+ return anchors
287
+ .filter((a) => (seen.has(a.index) ? false : (seen.add(a.index), true)))
288
+ .sort((p, q) => p.index - q.index)
289
+ .slice(0, max);
290
+ }
291
+
292
+ /**
293
+ * 环形缓冲。帧**只在内存**,永不落盘 —— 持续截屏在客户机器上是隐私敏感的,
294
+ * 这条是产品要求不是性能优化。
295
+ */
296
+ export class FrameRing<T extends { t: number }> {
297
+ private items: T[] = [];
298
+
299
+ constructor(readonly capacity: number) {}
300
+
301
+ push(record: T): void {
302
+ this.items.push(record);
303
+ if (this.items.length > this.capacity) {
304
+ this.items.splice(0, this.items.length - this.capacity);
305
+ }
306
+ }
307
+
308
+ /** 取时间戳 ≥ since 的帧(升序)。 */
309
+ since(since: number): T[] {
310
+ return this.items.filter((f) => f.t >= since);
311
+ }
312
+
313
+ /** 最近 n 帧(升序)。 */
314
+ recent(n: number): T[] {
315
+ return n <= 0 ? [] : this.items.slice(-n);
316
+ }
317
+
318
+ get latest(): T | null {
319
+ return this.items[this.items.length - 1] ?? null;
320
+ }
321
+
322
+ get size(): number {
323
+ return this.items.length;
324
+ }
325
+
326
+ clear(): void {
327
+ this.items = [];
328
+ }
329
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * ============================================================
3
+ * vision/geometry.ts —— 坐标系(纯函数)
4
+ * ------------------------------------------------------------
5
+ * 整个 computer-use 只有**一个**对外坐标约定:
6
+ *
7
+ * 0–1000 相对值。左上 (0,0),右下 (1000,1000)。
8
+ *
9
+ * 照抄 MiniMax M3 在 OSWorld computer-use 评测里的设置。它一次性绕开了
10
+ * 三类真实事故:
11
+ * - macOS Retina:物理像素是逻辑点的 2 倍(scaleFactor=2),截图坐标
12
+ * 直接当鼠标坐标用会偏一半
13
+ * - Windows 显示缩放:进程若没声明 per-monitor DPI aware,125%/150% 下
14
+ * 系统会悄悄给缩放过的坐标,落点系统性偏移(第三方普遍踩这个坑)
15
+ * - 多显示器 / 换分辨率:apps/*.md 里写的位置描述若绑死像素就全废
16
+ *
17
+ * **物理换算只发生在本文件里。** 其它模块一律只见相对坐标。
18
+ * ============================================================
19
+ */
20
+ /** 相对坐标空间的边长。取 1000 而非 100 是为了保留亚百分点精度(小控件需要)。 */
21
+ export const REL_SPAN = 1000;
22
+ /**
23
+ * scaleFactor = 物理像素 / 逻辑点。
24
+ * **必须实测算出,不能假设 1 或 2** —— 外接显示器、缩放设置、Sidecar
25
+ * 都会给出非整数或非 2 的值。
26
+ */
27
+ export function scaleFactor(physical, logical) {
28
+ if (!logical.width)
29
+ return 1;
30
+ return physical.width / logical.width;
31
+ }
32
+ /** 相对点 → 逻辑点(鼠标事件用的坐标系)。越界钳住而非报错:模型偶尔给 1000 或轻微越界。 */
33
+ export function relToLogical(rel, screen) {
34
+ return {
35
+ x: screen.x + clamp((rel.x / REL_SPAN) * screen.width, 0, screen.width - 1),
36
+ y: screen.y + clamp((rel.y / REL_SPAN) * screen.height, 0, screen.height - 1),
37
+ };
38
+ }
39
+ /** 逻辑点 → 相对点(把 OS 报来的 caret/窗口位置翻译给模型看)。 */
40
+ export function logicalToRel(point, screen) {
41
+ if (!screen.width || !screen.height)
42
+ return { x: 0, y: 0 };
43
+ return {
44
+ x: Math.round(((point.x - screen.x) / screen.width) * REL_SPAN),
45
+ y: Math.round(((point.y - screen.y) / screen.height) * REL_SPAN),
46
+ };
47
+ }
48
+ /** 相对矩形 → 某张图上的像素矩形(图可以是任意缩放尺寸,故传 imageSize 而非 screen)。 */
49
+ export function relRectToPixels(rect, imageSize) {
50
+ const x = (rect.x / REL_SPAN) * imageSize.width;
51
+ const y = (rect.y / REL_SPAN) * imageSize.height;
52
+ return {
53
+ x: Math.round(x),
54
+ y: Math.round(y),
55
+ width: Math.max(1, Math.round((rect.width / REL_SPAN) * imageSize.width)),
56
+ height: Math.max(1, Math.round((rect.height / REL_SPAN) * imageSize.height)),
57
+ };
58
+ }
59
+ /** 像素矩形 → 相对矩形。 */
60
+ export function pixelRectToRel(rect, imageSize) {
61
+ if (!imageSize.width || !imageSize.height)
62
+ return { x: 0, y: 0, width: 0, height: 0 };
63
+ return {
64
+ x: Math.round((rect.x / imageSize.width) * REL_SPAN),
65
+ y: Math.round((rect.y / imageSize.height) * REL_SPAN),
66
+ width: Math.round((rect.width / imageSize.width) * REL_SPAN),
67
+ height: Math.round((rect.height / imageSize.height) * REL_SPAN),
68
+ };
69
+ }
70
+ /**
71
+ * 向外扩边。变化区检出的包围盒往往刚好卡住控件边缘,裁出来会切掉半个按钮或
72
+ * 一圈阴影 —— 按比例外扩一圈(默认 20%)再裁,模型才看得到上下文。
73
+ */
74
+ export function padRect(rect, ratio = 0.2, bounds = null) {
75
+ const dx = rect.width * ratio;
76
+ const dy = rect.height * ratio;
77
+ const padded = {
78
+ x: rect.x - dx,
79
+ y: rect.y - dy,
80
+ width: rect.width + dx * 2,
81
+ height: rect.height + dy * 2,
82
+ };
83
+ return bounds ? clampRectTo(padded, bounds) : padded;
84
+ }
85
+ /** 钳进边界(bounds 用 {width, height},原点视为 0,0)。 */
86
+ export function clampRectTo(rect, bounds) {
87
+ const x = clamp(rect.x, 0, bounds.width - 1);
88
+ const y = clamp(rect.y, 0, bounds.height - 1);
89
+ return {
90
+ x,
91
+ y,
92
+ width: Math.max(1, Math.min(bounds.width - x, Math.round(rect.width))),
93
+ height: Math.max(1, Math.min(bounds.height - y, Math.round(rect.height))),
94
+ };
95
+ }
96
+ /** 两个矩形的最小外接矩形。 */
97
+ export function unionRect(a, b) {
98
+ if (!a)
99
+ return b;
100
+ if (!b)
101
+ return a;
102
+ const x = Math.min(a.x, b.x);
103
+ const y = Math.min(a.y, b.y);
104
+ return {
105
+ x,
106
+ y,
107
+ width: Math.max(a.x + a.width, b.x + b.width) - x,
108
+ height: Math.max(a.y + a.height, b.y + b.height) - y,
109
+ };
110
+ }
111
+ /** 是否相交(用于判断「模型要点的地方」是否落在变化区里 —— epoch 陈旧性检查的核心)。 */
112
+ export function rectsIntersect(a, b) {
113
+ return (a.x < b.x + b.width &&
114
+ b.x < a.x + a.width &&
115
+ a.y < b.y + b.height &&
116
+ b.y < a.y + a.height);
117
+ }
118
+ /** 点是否在矩形内。 */
119
+ export function pointInRect(point, rect) {
120
+ return (point.x >= rect.x &&
121
+ point.x < rect.x + rect.width &&
122
+ point.y >= rect.y &&
123
+ point.y < rect.y + rect.height);
124
+ }
125
+ export function clamp(value, min, max) {
126
+ return Math.max(min, Math.min(max, Math.round(value)));
127
+ }
128
+ /** 人话描述一个相对矩形,供文本通道使用(比塞一张图便宜 20 倍)。 */
129
+ export function describeRelRect(rect) {
130
+ return `(${rect.x},${rect.y})-(${rect.x + rect.width},${rect.y + rect.height})`;
131
+ }