@midscene/core 1.0.1-beta-20251208071759.0 → 1.0.1-beta-20251208085324.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 (81) hide show
  1. package/dist/es/agent/agent.mjs +10 -6
  2. package/dist/es/agent/agent.mjs.map +1 -1
  3. package/dist/es/agent/task-builder.mjs +26 -23
  4. package/dist/es/agent/task-builder.mjs.map +1 -1
  5. package/dist/es/agent/task-cache.mjs +1 -1
  6. package/dist/es/agent/task-cache.mjs.map +1 -1
  7. package/dist/es/agent/tasks.mjs +9 -4
  8. package/dist/es/agent/tasks.mjs.map +1 -1
  9. package/dist/es/agent/utils.mjs +1 -1
  10. package/dist/es/ai-model/conversation-history.mjs +19 -2
  11. package/dist/es/ai-model/conversation-history.mjs.map +1 -1
  12. package/dist/es/ai-model/llm-planning.mjs +3 -2
  13. package/dist/es/ai-model/llm-planning.mjs.map +1 -1
  14. package/dist/es/ai-model/prompt/llm-planning.mjs +20 -95
  15. package/dist/es/ai-model/prompt/llm-planning.mjs.map +1 -1
  16. package/dist/es/ai-model/service-caller/index.mjs +54 -39
  17. package/dist/es/ai-model/service-caller/index.mjs.map +1 -1
  18. package/dist/es/device/index.mjs +1 -1
  19. package/dist/es/device/index.mjs.map +1 -1
  20. package/dist/es/utils.mjs +2 -2
  21. package/dist/lib/agent/agent.js +12 -8
  22. package/dist/lib/agent/agent.js.map +1 -1
  23. package/dist/lib/agent/common.js +1 -1
  24. package/dist/lib/agent/execution-session.js +2 -2
  25. package/dist/lib/agent/index.js +2 -2
  26. package/dist/lib/agent/task-builder.js +28 -25
  27. package/dist/lib/agent/task-builder.js.map +1 -1
  28. package/dist/lib/agent/task-cache.js +3 -3
  29. package/dist/lib/agent/task-cache.js.map +1 -1
  30. package/dist/lib/agent/tasks.js +11 -6
  31. package/dist/lib/agent/tasks.js.map +1 -1
  32. package/dist/lib/agent/ui-utils.js +2 -2
  33. package/dist/lib/agent/utils.js +3 -3
  34. package/dist/lib/ai-model/conversation-history.js +21 -4
  35. package/dist/lib/ai-model/conversation-history.js.map +1 -1
  36. package/dist/lib/ai-model/index.js +2 -2
  37. package/dist/lib/ai-model/inspect.js +2 -2
  38. package/dist/lib/ai-model/llm-planning.js +5 -4
  39. package/dist/lib/ai-model/llm-planning.js.map +1 -1
  40. package/dist/lib/ai-model/prompt/assertion.js +2 -2
  41. package/dist/lib/ai-model/prompt/common.js +2 -2
  42. package/dist/lib/ai-model/prompt/describe.js +2 -2
  43. package/dist/lib/ai-model/prompt/extraction.js +2 -2
  44. package/dist/lib/ai-model/prompt/llm-locator.js +2 -2
  45. package/dist/lib/ai-model/prompt/llm-planning.js +22 -100
  46. package/dist/lib/ai-model/prompt/llm-planning.js.map +1 -1
  47. package/dist/lib/ai-model/prompt/llm-section-locator.js +2 -2
  48. package/dist/lib/ai-model/prompt/order-sensitive-judge.js +2 -2
  49. package/dist/lib/ai-model/prompt/playwright-generator.js +2 -2
  50. package/dist/lib/ai-model/prompt/ui-tars-locator.js +2 -2
  51. package/dist/lib/ai-model/prompt/ui-tars-planning.js +2 -2
  52. package/dist/lib/ai-model/prompt/util.js +2 -2
  53. package/dist/lib/ai-model/prompt/yaml-generator.js +2 -2
  54. package/dist/lib/ai-model/service-caller/index.js +55 -43
  55. package/dist/lib/ai-model/service-caller/index.js.map +1 -1
  56. package/dist/lib/ai-model/ui-tars-planning.js +2 -2
  57. package/dist/lib/common.js +2 -2
  58. package/dist/lib/device/device-options.js +1 -1
  59. package/dist/lib/device/index.js +3 -3
  60. package/dist/lib/device/index.js.map +1 -1
  61. package/dist/lib/image/index.js +2 -2
  62. package/dist/lib/index.js +2 -2
  63. package/dist/lib/report.js +2 -2
  64. package/dist/lib/service/index.js +2 -2
  65. package/dist/lib/service/utils.js +2 -2
  66. package/dist/lib/task-runner.js +2 -2
  67. package/dist/lib/tree.js +2 -2
  68. package/dist/lib/types.js +3 -3
  69. package/dist/lib/utils.js +4 -4
  70. package/dist/lib/yaml/builder.js +2 -2
  71. package/dist/lib/yaml/index.js +4 -4
  72. package/dist/lib/yaml/player.js +2 -2
  73. package/dist/lib/yaml/utils.js +2 -2
  74. package/dist/lib/yaml.js +1 -1
  75. package/dist/types/agent/agent.d.ts +1 -1
  76. package/dist/types/agent/tasks.d.ts +1 -1
  77. package/dist/types/ai-model/conversation-history.d.ts +6 -1
  78. package/dist/types/ai-model/llm-planning.d.ts +1 -0
  79. package/dist/types/ai-model/prompt/llm-planning.d.ts +0 -2
  80. package/dist/types/ai-model/service-caller/index.d.ts +1 -3
  81. package/package.json +5 -5
@@ -26,9 +26,9 @@ const cacheFileExt = '.cache.yaml';
26
26
  class TaskCache {
27
27
  matchCache(prompt, type) {
28
28
  if (!this.isCacheResultUsed) return;
29
+ const promptStr = 'string' == typeof prompt ? prompt : JSON.stringify(prompt);
29
30
  for(let i = 0; i < this.cacheOriginalLength; i++){
30
31
  const item = this.cache.caches[i];
31
- const promptStr = 'string' == typeof prompt ? prompt : JSON.stringify(prompt);
32
32
  const key = `${type}:${promptStr}:${i}`;
33
33
  if (item.type === type && isDeepStrictEqual(item.prompt, prompt) && !this.matchedCacheIndices.has(key)) {
34
34
  if ('locate' === item.type) {
@@ -1 +1 @@
1
- {"version":3,"file":"agent/task-cache.mjs","sources":["../../../src/agent/task-cache.ts"],"sourcesContent":["import assert from 'node:assert';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\nimport type { TUserPrompt } from '@/ai-model';\nimport type { ElementCacheFeature } from '@/types';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport {\n MIDSCENE_CACHE_MAX_FILENAME_LENGTH,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser, ifInWorker } from '@midscene/shared/utils';\nimport { generateHashId } from '@midscene/shared/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport yaml from 'js-yaml';\nimport semver from 'semver';\nimport { getMidsceneVersion } from './utils';\n\nconst DEFAULT_CACHE_MAX_FILENAME_LENGTH = 200;\n\nexport const debug = getDebug('cache');\n\nexport interface PlanningCache {\n type: 'plan';\n prompt: string;\n yamlWorkflow: string;\n}\n\nexport interface LocateCache {\n type: 'locate';\n prompt: TUserPrompt;\n cache?: ElementCacheFeature;\n /** @deprecated kept for backward compatibility */\n xpaths?: string[];\n}\n\nexport interface MatchCacheResult<T extends PlanningCache | LocateCache> {\n cacheContent: T;\n updateFn: (cb: (cache: T) => void) => void;\n}\n\nexport type CacheFileContent = {\n midsceneVersion: string;\n cacheId: string;\n caches: Array<PlanningCache | LocateCache>;\n};\n\nconst lowestSupportedMidsceneVersion = '0.16.10';\nexport const cacheFileExt = '.cache.yaml';\n\nexport class TaskCache {\n cacheId: string;\n\n cacheFilePath?: string;\n\n cache: CacheFileContent;\n\n isCacheResultUsed: boolean; // a flag to indicate if the cache result should be used\n cacheOriginalLength: number;\n\n readOnlyMode: boolean; // a flag to indicate if the cache is in read-only mode\n\n writeOnlyMode: boolean; // a flag to indicate if the cache is in write-only mode\n\n private matchedCacheIndices: Set<string> = new Set(); // Track matched records\n\n constructor(\n cacheId: string,\n isCacheResultUsed: boolean,\n cacheFilePath?: string,\n options: { readOnly?: boolean; writeOnly?: boolean } = {},\n ) {\n assert(cacheId, 'cacheId is required');\n let safeCacheId = replaceIllegalPathCharsAndSpace(cacheId);\n const cacheMaxFilenameLength =\n globalConfigManager.getEnvConfigInNumber(\n MIDSCENE_CACHE_MAX_FILENAME_LENGTH,\n ) || DEFAULT_CACHE_MAX_FILENAME_LENGTH;\n if (Buffer.byteLength(safeCacheId, 'utf8') > cacheMaxFilenameLength) {\n const prefix = safeCacheId.slice(0, 32);\n const hash = generateHashId(undefined, safeCacheId);\n safeCacheId = `${prefix}-${hash}`;\n }\n this.cacheId = safeCacheId;\n\n this.cacheFilePath =\n ifInBrowser || ifInWorker\n ? undefined\n : cacheFilePath ||\n join(getMidsceneRunSubDir('cache'), `${this.cacheId}${cacheFileExt}`);\n const readOnlyMode = Boolean(options?.readOnly);\n const writeOnlyMode = Boolean(options?.writeOnly);\n\n if (readOnlyMode && writeOnlyMode) {\n throw new Error('TaskCache cannot be both read-only and write-only');\n }\n\n this.isCacheResultUsed = writeOnlyMode ? false : isCacheResultUsed;\n this.readOnlyMode = readOnlyMode;\n this.writeOnlyMode = writeOnlyMode;\n\n let cacheContent;\n if (this.cacheFilePath && !this.writeOnlyMode) {\n cacheContent = this.loadCacheFromFile();\n }\n if (!cacheContent) {\n cacheContent = {\n midsceneVersion: getMidsceneVersion(),\n cacheId: this.cacheId,\n caches: [],\n };\n }\n this.cache = cacheContent;\n this.cacheOriginalLength = this.isCacheResultUsed\n ? this.cache.caches.length\n : 0;\n }\n\n matchCache(\n prompt: TUserPrompt,\n type: 'plan' | 'locate',\n ): MatchCacheResult<PlanningCache | LocateCache> | undefined {\n if (!this.isCacheResultUsed) {\n return undefined;\n }\n // Find the first unused matching cache\n for (let i = 0; i < this.cacheOriginalLength; i++) {\n const item = this.cache.caches[i];\n const promptStr =\n typeof prompt === 'string' ? prompt : JSON.stringify(prompt);\n const key = `${type}:${promptStr}:${i}`;\n if (\n item.type === type &&\n isDeepStrictEqual(item.prompt, prompt) &&\n !this.matchedCacheIndices.has(key)\n ) {\n if (item.type === 'locate') {\n const locateItem = item as LocateCache;\n if (!locateItem.cache && Array.isArray(locateItem.xpaths)) {\n locateItem.cache = { xpaths: locateItem.xpaths };\n }\n if ('xpaths' in locateItem) {\n locateItem.xpaths = undefined;\n }\n }\n this.matchedCacheIndices.add(key);\n debug(\n 'cache found and marked as used, type: %s, prompt: %s, index: %d',\n type,\n prompt,\n i,\n );\n return {\n cacheContent: item,\n updateFn: (cb: (cache: PlanningCache | LocateCache) => void) => {\n debug(\n 'will call updateFn to update cache, type: %s, prompt: %s, index: %d',\n type,\n prompt,\n i,\n );\n cb(item);\n\n if (this.readOnlyMode) {\n debug(\n 'read-only mode, cache updated in memory but not flushed to file',\n );\n return;\n }\n\n debug(\n 'cache updated, will flush to file, type: %s, prompt: %s, index: %d',\n type,\n prompt,\n i,\n );\n this.flushCacheToFile();\n },\n };\n }\n }\n debug('no unused cache found, type: %s, prompt: %s', type, prompt);\n return undefined;\n }\n\n matchPlanCache(prompt: string): MatchCacheResult<PlanningCache> | undefined {\n return this.matchCache(prompt, 'plan') as\n | MatchCacheResult<PlanningCache>\n | undefined;\n }\n\n matchLocateCache(\n prompt: TUserPrompt,\n ): MatchCacheResult<LocateCache> | undefined {\n return this.matchCache(prompt, 'locate') as\n | MatchCacheResult<LocateCache>\n | undefined;\n }\n\n appendCache(cache: PlanningCache | LocateCache) {\n debug('will append cache', cache);\n this.cache.caches.push(cache);\n\n if (this.readOnlyMode) {\n debug('read-only mode, cache appended to memory but not flushed to file');\n return;\n }\n\n this.flushCacheToFile();\n }\n\n loadCacheFromFile() {\n const cacheFile = this.cacheFilePath;\n assert(cacheFile, 'cache file path is required');\n\n if (!existsSync(cacheFile)) {\n debug('no cache file found, path: %s', cacheFile);\n return undefined;\n }\n\n // detect old cache file\n const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, '.json');\n if (existsSync(jsonTypeCacheFile) && this.isCacheResultUsed) {\n console.warn(\n `An outdated cache file from an earlier version of Midscene has been detected. Since version 0.17, we have implemented an improved caching strategy. Please delete the old file located at: ${jsonTypeCacheFile}.`,\n );\n return undefined;\n }\n\n try {\n const data = readFileSync(cacheFile, 'utf8');\n const jsonData = yaml.load(data) as CacheFileContent;\n\n const version = getMidsceneVersion();\n if (!version) {\n debug('no midscene version info, will not read cache from file');\n return undefined;\n }\n\n if (\n semver.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) &&\n !jsonData.midsceneVersion.includes('beta') // for internal test\n ) {\n console.warn(\n `You are using an old version of Midscene cache file, and we cannot match any info from it. Starting from Midscene v0.17, we changed our strategy to use xpath for cache info, providing better performance.\\nPlease delete the existing cache and rebuild it. Sorry for the inconvenience.\\ncache file: ${cacheFile}`,\n );\n return undefined;\n }\n\n debug(\n 'cache loaded from file, path: %s, cache version: %s, record length: %s',\n cacheFile,\n jsonData.midsceneVersion,\n jsonData.caches.length,\n );\n jsonData.midsceneVersion = getMidsceneVersion(); // update the version\n return jsonData;\n } catch (err) {\n debug(\n 'cache file exists but load failed, path: %s, error: %s',\n cacheFile,\n err,\n );\n return undefined;\n }\n }\n\n flushCacheToFile(options?: { cleanUnused?: boolean }) {\n const version = getMidsceneVersion();\n if (!version) {\n debug('no midscene version info, will not write cache to file');\n return;\n }\n\n if (!this.cacheFilePath) {\n debug('no cache file path, will not write cache to file');\n return;\n }\n\n // Clean unused caches if requested\n if (options?.cleanUnused) {\n // Skip cleaning in write-only mode or when cache is not used\n if (this.isCacheResultUsed) {\n const originalLength = this.cache.caches.length;\n\n // Collect indices of used caches\n const usedIndices = new Set<number>();\n for (const key of this.matchedCacheIndices) {\n // key format: \"type:prompt:index\"\n const parts = key.split(':');\n const index = Number.parseInt(parts[parts.length - 1], 10);\n if (!Number.isNaN(index)) {\n usedIndices.add(index);\n }\n }\n\n // Filter: keep used caches and newly added caches\n this.cache.caches = this.cache.caches.filter((_, index) => {\n const isUsed = usedIndices.has(index);\n const isNew = index >= this.cacheOriginalLength;\n return isUsed || isNew;\n });\n\n const removedCount = originalLength - this.cache.caches.length;\n if (removedCount > 0) {\n debug('cleaned %d unused cache record(s)', removedCount);\n } else {\n debug('no unused cache to clean');\n }\n } else {\n debug('skip cleaning: cache is not used for reading');\n }\n }\n\n try {\n const dir = dirname(this.cacheFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n debug('created cache directory: %s', dir);\n }\n\n // Sort caches to ensure plan entries come before locate entries for better readability\n // Create a sorted copy for writing to disk while keeping in-memory order unchanged\n const sortedCaches = [...this.cache.caches].sort((a, b) => {\n if (a.type === 'plan' && b.type === 'locate') return -1;\n if (a.type === 'locate' && b.type === 'plan') return 1;\n return 0;\n });\n\n const cacheToWrite = {\n ...this.cache,\n caches: sortedCaches,\n };\n\n const yamlData = yaml.dump(cacheToWrite);\n writeFileSync(this.cacheFilePath, yamlData);\n debug('cache flushed to file: %s', this.cacheFilePath);\n } catch (err) {\n debug(\n 'write cache to file failed, path: %s, error: %s',\n this.cacheFilePath,\n err,\n );\n }\n }\n\n updateOrAppendCacheRecord(\n newRecord: PlanningCache | LocateCache,\n cachedRecord?: MatchCacheResult<PlanningCache | LocateCache>,\n ) {\n if (cachedRecord) {\n // update existing record\n if (newRecord.type === 'plan') {\n cachedRecord.updateFn((cache) => {\n (cache as PlanningCache).yamlWorkflow = newRecord.yamlWorkflow;\n });\n } else {\n cachedRecord.updateFn((cache) => {\n const locateCache = cache as LocateCache;\n locateCache.cache = newRecord.cache;\n if ('xpaths' in locateCache) {\n locateCache.xpaths = undefined;\n }\n });\n }\n } else {\n this.appendCache(newRecord);\n }\n }\n}\n"],"names":["DEFAULT_CACHE_MAX_FILENAME_LENGTH","debug","getDebug","lowestSupportedMidsceneVersion","cacheFileExt","TaskCache","prompt","type","i","item","promptStr","JSON","key","isDeepStrictEqual","locateItem","Array","undefined","cb","cache","cacheFile","assert","existsSync","jsonTypeCacheFile","console","data","readFileSync","jsonData","yaml","version","getMidsceneVersion","semver","err","options","originalLength","usedIndices","Set","parts","index","Number","_","isUsed","isNew","removedCount","dir","dirname","mkdirSync","sortedCaches","a","b","cacheToWrite","yamlData","writeFileSync","newRecord","cachedRecord","locateCache","cacheId","isCacheResultUsed","cacheFilePath","safeCacheId","replaceIllegalPathCharsAndSpace","cacheMaxFilenameLength","globalConfigManager","MIDSCENE_CACHE_MAX_FILENAME_LENGTH","Buffer","prefix","hash","generateHashId","ifInBrowser","ifInWorker","join","getMidsceneRunSubDir","readOnlyMode","Boolean","writeOnlyMode","Error","cacheContent"],"mappings":"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"}
1
+ {"version":3,"file":"agent/task-cache.mjs","sources":["../../../src/agent/task-cache.ts"],"sourcesContent":["import assert from 'node:assert';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\nimport type { TUserPrompt } from '@/ai-model';\nimport type { ElementCacheFeature } from '@/types';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport {\n MIDSCENE_CACHE_MAX_FILENAME_LENGTH,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser, ifInWorker } from '@midscene/shared/utils';\nimport { generateHashId } from '@midscene/shared/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport yaml from 'js-yaml';\nimport semver from 'semver';\nimport { getMidsceneVersion } from './utils';\n\nconst DEFAULT_CACHE_MAX_FILENAME_LENGTH = 200;\n\nexport const debug = getDebug('cache');\n\nexport interface PlanningCache {\n type: 'plan';\n prompt: string;\n yamlWorkflow: string;\n}\n\nexport interface LocateCache {\n type: 'locate';\n prompt: TUserPrompt;\n cache?: ElementCacheFeature;\n /** @deprecated kept for backward compatibility */\n xpaths?: string[];\n}\n\nexport interface MatchCacheResult<T extends PlanningCache | LocateCache> {\n cacheContent: T;\n updateFn: (cb: (cache: T) => void) => void;\n}\n\nexport type CacheFileContent = {\n midsceneVersion: string;\n cacheId: string;\n caches: Array<PlanningCache | LocateCache>;\n};\n\nconst lowestSupportedMidsceneVersion = '0.16.10';\nexport const cacheFileExt = '.cache.yaml';\n\nexport class TaskCache {\n cacheId: string;\n\n cacheFilePath?: string;\n\n cache: CacheFileContent;\n\n isCacheResultUsed: boolean; // a flag to indicate if the cache result should be used\n cacheOriginalLength: number;\n\n readOnlyMode: boolean; // a flag to indicate if the cache is in read-only mode\n\n writeOnlyMode: boolean; // a flag to indicate if the cache is in write-only mode\n\n private matchedCacheIndices: Set<string> = new Set(); // Track matched records\n\n constructor(\n cacheId: string,\n isCacheResultUsed: boolean,\n cacheFilePath?: string,\n options: { readOnly?: boolean; writeOnly?: boolean } = {},\n ) {\n assert(cacheId, 'cacheId is required');\n let safeCacheId = replaceIllegalPathCharsAndSpace(cacheId);\n const cacheMaxFilenameLength =\n globalConfigManager.getEnvConfigInNumber(\n MIDSCENE_CACHE_MAX_FILENAME_LENGTH,\n ) || DEFAULT_CACHE_MAX_FILENAME_LENGTH;\n if (Buffer.byteLength(safeCacheId, 'utf8') > cacheMaxFilenameLength) {\n const prefix = safeCacheId.slice(0, 32);\n const hash = generateHashId(undefined, safeCacheId);\n safeCacheId = `${prefix}-${hash}`;\n }\n this.cacheId = safeCacheId;\n\n this.cacheFilePath =\n ifInBrowser || ifInWorker\n ? undefined\n : cacheFilePath ||\n join(getMidsceneRunSubDir('cache'), `${this.cacheId}${cacheFileExt}`);\n const readOnlyMode = Boolean(options?.readOnly);\n const writeOnlyMode = Boolean(options?.writeOnly);\n\n if (readOnlyMode && writeOnlyMode) {\n throw new Error('TaskCache cannot be both read-only and write-only');\n }\n\n this.isCacheResultUsed = writeOnlyMode ? false : isCacheResultUsed;\n this.readOnlyMode = readOnlyMode;\n this.writeOnlyMode = writeOnlyMode;\n\n let cacheContent;\n if (this.cacheFilePath && !this.writeOnlyMode) {\n cacheContent = this.loadCacheFromFile();\n }\n if (!cacheContent) {\n cacheContent = {\n midsceneVersion: getMidsceneVersion(),\n cacheId: this.cacheId,\n caches: [],\n };\n }\n this.cache = cacheContent;\n this.cacheOriginalLength = this.isCacheResultUsed\n ? this.cache.caches.length\n : 0;\n }\n\n matchCache(\n prompt: TUserPrompt,\n type: 'plan' | 'locate',\n ): MatchCacheResult<PlanningCache | LocateCache> | undefined {\n if (!this.isCacheResultUsed) {\n return undefined;\n }\n // Find the first unused matching cache\n const promptStr =\n typeof prompt === 'string' ? prompt : JSON.stringify(prompt);\n for (let i = 0; i < this.cacheOriginalLength; i++) {\n const item = this.cache.caches[i];\n const key = `${type}:${promptStr}:${i}`;\n if (\n item.type === type &&\n isDeepStrictEqual(item.prompt, prompt) &&\n !this.matchedCacheIndices.has(key)\n ) {\n if (item.type === 'locate') {\n const locateItem = item as LocateCache;\n if (!locateItem.cache && Array.isArray(locateItem.xpaths)) {\n locateItem.cache = { xpaths: locateItem.xpaths };\n }\n if ('xpaths' in locateItem) {\n locateItem.xpaths = undefined;\n }\n }\n this.matchedCacheIndices.add(key);\n debug(\n 'cache found and marked as used, type: %s, prompt: %s, index: %d',\n type,\n prompt,\n i,\n );\n return {\n cacheContent: item,\n updateFn: (cb: (cache: PlanningCache | LocateCache) => void) => {\n debug(\n 'will call updateFn to update cache, type: %s, prompt: %s, index: %d',\n type,\n prompt,\n i,\n );\n cb(item);\n\n if (this.readOnlyMode) {\n debug(\n 'read-only mode, cache updated in memory but not flushed to file',\n );\n return;\n }\n\n debug(\n 'cache updated, will flush to file, type: %s, prompt: %s, index: %d',\n type,\n prompt,\n i,\n );\n this.flushCacheToFile();\n },\n };\n }\n }\n debug('no unused cache found, type: %s, prompt: %s', type, prompt);\n return undefined;\n }\n\n matchPlanCache(prompt: string): MatchCacheResult<PlanningCache> | undefined {\n return this.matchCache(prompt, 'plan') as\n | MatchCacheResult<PlanningCache>\n | undefined;\n }\n\n matchLocateCache(\n prompt: TUserPrompt,\n ): MatchCacheResult<LocateCache> | undefined {\n return this.matchCache(prompt, 'locate') as\n | MatchCacheResult<LocateCache>\n | undefined;\n }\n\n appendCache(cache: PlanningCache | LocateCache) {\n debug('will append cache', cache);\n this.cache.caches.push(cache);\n\n if (this.readOnlyMode) {\n debug('read-only mode, cache appended to memory but not flushed to file');\n return;\n }\n\n this.flushCacheToFile();\n }\n\n loadCacheFromFile() {\n const cacheFile = this.cacheFilePath;\n assert(cacheFile, 'cache file path is required');\n\n if (!existsSync(cacheFile)) {\n debug('no cache file found, path: %s', cacheFile);\n return undefined;\n }\n\n // detect old cache file\n const jsonTypeCacheFile = cacheFile.replace(cacheFileExt, '.json');\n if (existsSync(jsonTypeCacheFile) && this.isCacheResultUsed) {\n console.warn(\n `An outdated cache file from an earlier version of Midscene has been detected. Since version 0.17, we have implemented an improved caching strategy. Please delete the old file located at: ${jsonTypeCacheFile}.`,\n );\n return undefined;\n }\n\n try {\n const data = readFileSync(cacheFile, 'utf8');\n const jsonData = yaml.load(data) as CacheFileContent;\n\n const version = getMidsceneVersion();\n if (!version) {\n debug('no midscene version info, will not read cache from file');\n return undefined;\n }\n\n if (\n semver.lt(jsonData.midsceneVersion, lowestSupportedMidsceneVersion) &&\n !jsonData.midsceneVersion.includes('beta') // for internal test\n ) {\n console.warn(\n `You are using an old version of Midscene cache file, and we cannot match any info from it. Starting from Midscene v0.17, we changed our strategy to use xpath for cache info, providing better performance.\\nPlease delete the existing cache and rebuild it. Sorry for the inconvenience.\\ncache file: ${cacheFile}`,\n );\n return undefined;\n }\n\n debug(\n 'cache loaded from file, path: %s, cache version: %s, record length: %s',\n cacheFile,\n jsonData.midsceneVersion,\n jsonData.caches.length,\n );\n jsonData.midsceneVersion = getMidsceneVersion(); // update the version\n return jsonData;\n } catch (err) {\n debug(\n 'cache file exists but load failed, path: %s, error: %s',\n cacheFile,\n err,\n );\n return undefined;\n }\n }\n\n flushCacheToFile(options?: { cleanUnused?: boolean }) {\n const version = getMidsceneVersion();\n if (!version) {\n debug('no midscene version info, will not write cache to file');\n return;\n }\n\n if (!this.cacheFilePath) {\n debug('no cache file path, will not write cache to file');\n return;\n }\n\n // Clean unused caches if requested\n if (options?.cleanUnused) {\n // Skip cleaning in write-only mode or when cache is not used\n if (this.isCacheResultUsed) {\n const originalLength = this.cache.caches.length;\n\n // Collect indices of used caches\n const usedIndices = new Set<number>();\n for (const key of this.matchedCacheIndices) {\n // key format: \"type:prompt:index\"\n const parts = key.split(':');\n const index = Number.parseInt(parts[parts.length - 1], 10);\n if (!Number.isNaN(index)) {\n usedIndices.add(index);\n }\n }\n\n // Filter: keep used caches and newly added caches\n this.cache.caches = this.cache.caches.filter((_, index) => {\n const isUsed = usedIndices.has(index);\n const isNew = index >= this.cacheOriginalLength;\n return isUsed || isNew;\n });\n\n const removedCount = originalLength - this.cache.caches.length;\n if (removedCount > 0) {\n debug('cleaned %d unused cache record(s)', removedCount);\n } else {\n debug('no unused cache to clean');\n }\n } else {\n debug('skip cleaning: cache is not used for reading');\n }\n }\n\n try {\n const dir = dirname(this.cacheFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n debug('created cache directory: %s', dir);\n }\n\n // Sort caches to ensure plan entries come before locate entries for better readability\n // Create a sorted copy for writing to disk while keeping in-memory order unchanged\n const sortedCaches = [...this.cache.caches].sort((a, b) => {\n if (a.type === 'plan' && b.type === 'locate') return -1;\n if (a.type === 'locate' && b.type === 'plan') return 1;\n return 0;\n });\n\n const cacheToWrite = {\n ...this.cache,\n caches: sortedCaches,\n };\n\n const yamlData = yaml.dump(cacheToWrite);\n writeFileSync(this.cacheFilePath, yamlData);\n debug('cache flushed to file: %s', this.cacheFilePath);\n } catch (err) {\n debug(\n 'write cache to file failed, path: %s, error: %s',\n this.cacheFilePath,\n err,\n );\n }\n }\n\n updateOrAppendCacheRecord(\n newRecord: PlanningCache | LocateCache,\n cachedRecord?: MatchCacheResult<PlanningCache | LocateCache>,\n ) {\n if (cachedRecord) {\n // update existing record\n if (newRecord.type === 'plan') {\n cachedRecord.updateFn((cache) => {\n (cache as PlanningCache).yamlWorkflow = newRecord.yamlWorkflow;\n });\n } else {\n cachedRecord.updateFn((cache) => {\n const locateCache = cache as LocateCache;\n locateCache.cache = newRecord.cache;\n if ('xpaths' in locateCache) {\n locateCache.xpaths = undefined;\n }\n });\n }\n } else {\n this.appendCache(newRecord);\n }\n }\n}\n"],"names":["DEFAULT_CACHE_MAX_FILENAME_LENGTH","debug","getDebug","lowestSupportedMidsceneVersion","cacheFileExt","TaskCache","prompt","type","promptStr","JSON","i","item","key","isDeepStrictEqual","locateItem","Array","undefined","cb","cache","cacheFile","assert","existsSync","jsonTypeCacheFile","console","data","readFileSync","jsonData","yaml","version","getMidsceneVersion","semver","err","options","originalLength","usedIndices","Set","parts","index","Number","_","isUsed","isNew","removedCount","dir","dirname","mkdirSync","sortedCaches","a","b","cacheToWrite","yamlData","writeFileSync","newRecord","cachedRecord","locateCache","cacheId","isCacheResultUsed","cacheFilePath","safeCacheId","replaceIllegalPathCharsAndSpace","cacheMaxFilenameLength","globalConfigManager","MIDSCENE_CACHE_MAX_FILENAME_LENGTH","Buffer","prefix","hash","generateHashId","ifInBrowser","ifInWorker","join","getMidsceneRunSubDir","readOnlyMode","Boolean","writeOnlyMode","Error","cacheContent"],"mappings":"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"}
@@ -81,7 +81,7 @@ class TaskExecutor {
81
81
  runner
82
82
  };
83
83
  }
84
- async action(userPrompt, modelConfigForPlanning, modelConfigForDefaultIntent, includeBboxInPlanning, backgroundKnowledge, cacheable, replanningCycleLimitOverride) {
84
+ async action(userPrompt, modelConfigForPlanning, modelConfigForDefaultIntent, includeBboxInPlanning, backgroundKnowledge, cacheable, replanningCycleLimitOverride, imagesIncludeCount) {
85
85
  this.conversationHistory.reset();
86
86
  const session = this.createExecutionSession(taskTitleStr('Action', userPrompt));
87
87
  const runner = session.getRunner();
@@ -96,7 +96,8 @@ class TaskExecutor {
96
96
  subType: 'Plan',
97
97
  param: {
98
98
  userInstruction: userPrompt,
99
- aiActionContext: backgroundKnowledge
99
+ aiActionContext: backgroundKnowledge,
100
+ imagesIncludeCount
100
101
  },
101
102
  executor: async (param, executorContext)=>{
102
103
  const startTime = Date.now();
@@ -116,7 +117,8 @@ class TaskExecutor {
116
117
  actionSpace,
117
118
  modelConfig: modelConfigForPlanning,
118
119
  conversationHistory: this.conversationHistory,
119
- includeBbox: includeBboxInPlanning
120
+ includeBbox: includeBboxInPlanning,
121
+ imagesIncludeCount
120
122
  });
121
123
  debug('planResult', JSON.stringify(planResult, null, 2));
122
124
  const { actions, log, more_actions_needed_by_instruction, error, usage, rawResponse, sleep } = planResult;
@@ -163,15 +165,18 @@ class TaskExecutor {
163
165
  return session.appendErrorPlan(`Error converting plans to executable tasks: ${error}, plans: ${JSON.stringify(plans)}`);
164
166
  }
165
167
  if (this.conversationHistory.pendingFeedbackMessage) console.warn('unconsumed pending feedback message detected, this may lead to unexpected planning result:', this.conversationHistory.pendingFeedbackMessage);
168
+ let errorFlag = false;
166
169
  try {
167
170
  await session.appendAndRun(executables.tasks);
168
171
  } catch (error) {
172
+ errorFlag = true;
169
173
  errorCountInOnePlanningLoop++;
170
174
  this.conversationHistory.pendingFeedbackMessage = `Error executing running tasks: ${error?.message || String(error)}`;
171
175
  debug('error when executing running tasks, but continue to run if it is not too many errors:', error instanceof Error ? error.message : String(error), 'current error count in one planning loop:', errorCountInOnePlanningLoop);
172
176
  }
173
177
  if (errorCountInOnePlanningLoop > maxErrorCountAllowedInOnePlanningLoop) return session.appendErrorPlan('Too many errors in one planning loop');
174
- if (!planResult?.more_actions_needed_by_instruction) break;
178
+ if (!planResult?.more_actions_needed_by_instruction) if (errorFlag) debug('more_actions_needed_by_instruction is false, but there are errors in one planning loop, continue to run');
179
+ else break;
175
180
  ++replanCount;
176
181
  if (replanCount > replanningCycleLimit) {
177
182
  const errorMsg = `Replanned ${replanningCycleLimit} times, exceeding the limit. Please configure a larger value for replanningCycleLimit (or use MIDSCENE_REPLANNING_CYCLE_LIMIT) to handle more complex tasks.`;
@@ -1 +1 @@
1
- {"version":3,"file":"agent/tasks.mjs","sources":["../../../src/agent/tasks.ts"],"sourcesContent":["import { ConversationHistory, plan, uiTarsPlanning } from '@/ai-model';\nimport type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type { AbstractInterface } from '@/device';\nimport type Service from '@/service';\nimport type { TaskRunner } from '@/task-runner';\nimport { TaskExecutionError } from '@/task-runner';\nimport type {\n ExecutionTaskApply,\n ExecutionTaskInsightQueryApply,\n ExecutionTaskPlanningApply,\n ExecutionTaskProgressOptions,\n InterfaceType,\n MidsceneYamlFlowItem,\n PlanningAIResponse,\n PlanningAction,\n PlanningActionParamSleep,\n PlanningActionParamWaitFor,\n ServiceDump,\n ServiceExtractOption,\n ServiceExtractParam,\n} from '@/types';\nimport { ServiceError } from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport { ExecutionSession } from './execution-session';\nimport { TaskBuilder } from './task-builder';\nimport type { TaskCache } from './task-cache';\nexport { locatePlanForLocate } from './task-builder';\nimport { descriptionOfTree } from '@midscene/shared/extractor';\nimport { taskTitleStr } from './ui-utils';\nimport { parsePrompt } from './utils';\n\ninterface ExecutionResult<OutputType = any> {\n output: OutputType;\n thought?: string;\n runner: TaskRunner;\n}\n\ninterface TaskExecutorHooks {\n onTaskUpdate?: (\n runner: TaskRunner,\n error?: TaskExecutionError,\n ) => Promise<void> | void;\n}\n\nconst debug = getDebug('device-task-executor');\nconst maxErrorCountAllowedInOnePlanningLoop = 5;\n\nexport { TaskExecutionError };\n\nexport class TaskExecutor {\n interface: AbstractInterface;\n\n service: Service;\n\n taskCache?: TaskCache;\n\n private readonly taskBuilder: TaskBuilder;\n\n private conversationHistory: ConversationHistory;\n\n onTaskStartCallback?: ExecutionTaskProgressOptions['onTaskStart'];\n\n private readonly hooks?: TaskExecutorHooks;\n\n replanningCycleLimit?: number;\n\n // @deprecated use .interface instead\n get page() {\n return this.interface;\n }\n\n constructor(\n interfaceInstance: AbstractInterface,\n service: Service,\n opts: {\n taskCache?: TaskCache;\n onTaskStart?: ExecutionTaskProgressOptions['onTaskStart'];\n replanningCycleLimit?: number;\n hooks?: TaskExecutorHooks;\n },\n ) {\n this.interface = interfaceInstance;\n this.service = service;\n this.taskCache = opts.taskCache;\n this.onTaskStartCallback = opts?.onTaskStart;\n this.replanningCycleLimit = opts.replanningCycleLimit;\n this.hooks = opts.hooks;\n this.conversationHistory = new ConversationHistory();\n this.taskBuilder = new TaskBuilder({\n interfaceInstance,\n service,\n taskCache: opts.taskCache,\n });\n }\n\n private createExecutionSession(\n title: string,\n options?: { tasks?: ExecutionTaskApply[] },\n ) {\n return new ExecutionSession(\n title,\n () => Promise.resolve(this.service.contextRetrieverFn()),\n {\n onTaskStart: this.onTaskStartCallback,\n tasks: options?.tasks,\n onTaskUpdate: this.hooks?.onTaskUpdate,\n },\n );\n }\n\n public async convertPlanToExecutable(\n plans: PlanningAction[],\n modelConfigForPlanning: IModelConfig,\n modelConfigForDefaultIntent: IModelConfig,\n options?: {\n cacheable?: boolean;\n subTask?: boolean;\n },\n ) {\n return this.taskBuilder.build(\n plans,\n modelConfigForPlanning,\n modelConfigForDefaultIntent,\n options,\n );\n }\n\n async loadYamlFlowAsPlanning(userInstruction: string, yamlString: string) {\n const session = this.createExecutionSession(\n taskTitleStr('Action', userInstruction),\n );\n\n const task: ExecutionTaskPlanningApply = {\n type: 'Planning',\n subType: 'LoadYaml',\n param: {\n userInstruction,\n },\n executor: async (param, executorContext) => {\n const { uiContext } = executorContext;\n assert(uiContext, 'uiContext is required for Planning task');\n return {\n output: {\n actions: [],\n more_actions_needed_by_instruction: false,\n log: '',\n yamlString,\n },\n cache: {\n hit: true,\n },\n hitBy: {\n from: 'Cache',\n context: {\n yamlString,\n },\n },\n };\n },\n };\n const runner = session.getRunner();\n await session.appendAndRun(task);\n\n return {\n runner,\n };\n }\n\n async runPlans(\n title: string,\n plans: PlanningAction[],\n modelConfigForPlanning: IModelConfig,\n modelConfigForDefaultIntent: IModelConfig,\n ): Promise<ExecutionResult> {\n const session = this.createExecutionSession(title);\n const { tasks } = await this.convertPlanToExecutable(\n plans,\n modelConfigForPlanning,\n modelConfigForDefaultIntent,\n );\n const runner = session.getRunner();\n const result = await session.appendAndRun(tasks);\n const { output } = result ?? {};\n return {\n output,\n runner,\n };\n }\n\n async action(\n userPrompt: string,\n modelConfigForPlanning: IModelConfig,\n modelConfigForDefaultIntent: IModelConfig,\n includeBboxInPlanning: boolean,\n backgroundKnowledge?: string,\n cacheable?: boolean,\n replanningCycleLimitOverride?: number,\n ): Promise<\n ExecutionResult<\n | {\n yamlFlow?: MidsceneYamlFlowItem[]; // for cache use\n }\n | undefined\n >\n > {\n this.conversationHistory.reset();\n\n const session = this.createExecutionSession(\n taskTitleStr('Action', userPrompt),\n );\n const runner = session.getRunner();\n\n let replanCount = 0;\n const yamlFlow: MidsceneYamlFlowItem[] = [];\n const replanningCycleLimit =\n replanningCycleLimitOverride ?? this.replanningCycleLimit;\n assert(\n replanningCycleLimit !== undefined,\n 'replanningCycleLimit is required for TaskExecutor.action',\n );\n\n let errorCountInOnePlanningLoop = 0; // count the number of errors in one planning loop\n\n // Main planning loop - unified plan/replan logic\n while (true) {\n const result = await session.appendAndRun(\n {\n type: 'Planning',\n subType: 'Plan',\n param: {\n userInstruction: userPrompt,\n aiActionContext: backgroundKnowledge,\n },\n executor: async (param, executorContext) => {\n const startTime = Date.now();\n const { uiContext } = executorContext;\n assert(uiContext, 'uiContext is required for Planning task');\n const { vlMode } = modelConfigForPlanning;\n const uiTarsModelVersion =\n vlMode === 'vlm-ui-tars'\n ? modelConfigForPlanning.uiTarsModelVersion\n : undefined;\n\n assert(\n this.interface.actionSpace,\n 'actionSpace for device is not implemented',\n );\n const actionSpace = await this.interface.actionSpace();\n debug(\n 'actionSpace for this interface is:',\n actionSpace.map((action) => action.name).join(', '),\n );\n assert(Array.isArray(actionSpace), 'actionSpace must be an array');\n if (actionSpace.length === 0) {\n console.warn(\n `ActionSpace for ${this.interface.interfaceType} is empty. This may lead to unexpected behavior.`,\n );\n }\n\n const planResult = await (uiTarsModelVersion\n ? uiTarsPlanning\n : plan)(param.userInstruction, {\n context: uiContext,\n actionContext: param.aiActionContext,\n interfaceType: this.interface.interfaceType as InterfaceType,\n actionSpace,\n modelConfig: modelConfigForPlanning,\n conversationHistory: this.conversationHistory,\n includeBbox: includeBboxInPlanning,\n });\n debug('planResult', JSON.stringify(planResult, null, 2));\n\n const {\n actions,\n log,\n more_actions_needed_by_instruction,\n error,\n usage,\n rawResponse,\n sleep,\n } = planResult;\n\n executorContext.task.log = {\n ...(executorContext.task.log || {}),\n rawResponse,\n };\n executorContext.task.usage = usage;\n executorContext.task.output = {\n actions: actions || [],\n more_actions_needed_by_instruction,\n log,\n yamlFlow: planResult.yamlFlow,\n };\n executorContext.uiContext = uiContext;\n\n const finalActions = [...(actions || [])];\n\n if (sleep) {\n const timeNow = Date.now();\n const timeRemaining = sleep - (timeNow - startTime);\n if (timeRemaining > 0) {\n finalActions.push(this.sleepPlan(timeRemaining));\n }\n }\n\n assert(!error, `Failed to continue: ${error}\\n${log || ''}`);\n\n return {\n cache: {\n hit: false,\n },\n } as any;\n },\n },\n {\n allowWhenError: true,\n },\n );\n\n const planResult = result?.output as PlanningAIResponse | undefined;\n\n // Execute planned actions\n const plans = planResult?.actions || [];\n yamlFlow.push(...(planResult?.yamlFlow || []));\n\n let executables: Awaited<ReturnType<typeof this.convertPlanToExecutable>>;\n try {\n executables = await this.convertPlanToExecutable(\n plans,\n modelConfigForPlanning,\n modelConfigForDefaultIntent,\n {\n cacheable,\n subTask: true,\n },\n );\n } catch (error) {\n return session.appendErrorPlan(\n `Error converting plans to executable tasks: ${error}, plans: ${JSON.stringify(\n plans,\n )}`,\n );\n }\n if (this.conversationHistory.pendingFeedbackMessage) {\n console.warn(\n 'unconsumed pending feedback message detected, this may lead to unexpected planning result:',\n this.conversationHistory.pendingFeedbackMessage,\n );\n }\n try {\n await session.appendAndRun(executables.tasks);\n } catch (error: any) {\n errorCountInOnePlanningLoop++;\n this.conversationHistory.pendingFeedbackMessage = `Error executing running tasks: ${error?.message || String(error)}`;\n debug(\n 'error when executing running tasks, but continue to run if it is not too many errors:',\n error instanceof Error ? error.message : String(error),\n 'current error count in one planning loop:',\n errorCountInOnePlanningLoop,\n );\n }\n\n if (errorCountInOnePlanningLoop > maxErrorCountAllowedInOnePlanningLoop) {\n return session.appendErrorPlan('Too many errors in one planning loop');\n }\n\n // Check if task is complete\n if (!planResult?.more_actions_needed_by_instruction) {\n break;\n }\n\n // Increment replan count for next iteration\n ++replanCount;\n\n if (replanCount > replanningCycleLimit) {\n const errorMsg = `Replanned ${replanningCycleLimit} times, exceeding the limit. Please configure a larger value for replanningCycleLimit (or use MIDSCENE_REPLANNING_CYCLE_LIMIT) to handle more complex tasks.`;\n return session.appendErrorPlan(errorMsg);\n }\n\n if (!this.conversationHistory.pendingFeedbackMessage) {\n this.conversationHistory.pendingFeedbackMessage =\n 'I have finished the action previously planned.';\n }\n }\n\n const finalResult = {\n output: {\n yamlFlow,\n },\n runner,\n };\n return finalResult;\n }\n\n private createTypeQueryTask(\n type: 'Query' | 'Boolean' | 'Number' | 'String' | 'Assert' | 'WaitFor',\n demand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n multimodalPrompt?: TMultimodalPrompt,\n ) {\n const queryTask: ExecutionTaskInsightQueryApply = {\n type: 'Insight',\n subType: type,\n param: {\n dataDemand: multimodalPrompt\n ? ({\n demand,\n multimodalPrompt,\n } as never)\n : demand, // for user param presentation in report right sidebar\n },\n executor: async (param, taskContext) => {\n const { task } = taskContext;\n let queryDump: ServiceDump | undefined;\n const applyDump = (dump: ServiceDump) => {\n queryDump = dump;\n task.log = {\n dump,\n };\n };\n\n // Get context for query operations\n const uiContext = taskContext.uiContext;\n assert(uiContext, 'uiContext is required for Query task');\n\n const ifTypeRestricted = type !== 'Query';\n let demandInput = demand;\n let keyOfResult = 'result';\n if (ifTypeRestricted && (type === 'Assert' || type === 'WaitFor')) {\n keyOfResult = 'StatementIsTruthy';\n const booleanPrompt =\n type === 'Assert'\n ? `Boolean, whether the following statement is true: ${demand}`\n : `Boolean, the user wants to do some 'wait for' operation, please check whether the following statement is true: ${demand}`;\n demandInput = {\n [keyOfResult]: booleanPrompt,\n };\n } else if (ifTypeRestricted) {\n demandInput = {\n [keyOfResult]: `${type}, ${demand}`,\n };\n }\n\n let extractResult;\n\n let extraPageDescription = '';\n if (opt?.domIncluded && this.interface.getElementsNodeTree) {\n debug('appending tree info for page');\n const tree = await this.interface.getElementsNodeTree();\n extraPageDescription = await descriptionOfTree(\n tree,\n 200,\n false,\n opt?.domIncluded === 'visible-only',\n );\n }\n\n try {\n extractResult = await this.service.extract<any>(\n demandInput,\n modelConfig,\n opt,\n extraPageDescription,\n multimodalPrompt,\n );\n } catch (error) {\n if (error instanceof ServiceError) {\n applyDump(error.dump);\n }\n throw error;\n }\n\n const { data, usage, thought, dump } = extractResult;\n applyDump(dump);\n\n let outputResult = data;\n if (ifTypeRestricted) {\n // If AI returned a plain string instead of structured format, use it directly\n if (typeof data === 'string') {\n outputResult = data;\n } else if (type === 'WaitFor') {\n if (data === null || data === undefined) {\n outputResult = false;\n } else {\n outputResult = (data as any)[keyOfResult];\n }\n } else if (data === null || data === undefined) {\n outputResult = null;\n } else {\n assert(\n data?.[keyOfResult] !== undefined,\n 'No result in query data',\n );\n outputResult = (data as any)[keyOfResult];\n }\n }\n\n if (type === 'Assert' && !outputResult) {\n task.usage = usage;\n task.thought = thought;\n throw new Error(`Assertion failed: ${thought}`);\n }\n\n return {\n output: outputResult,\n log: queryDump,\n usage,\n thought,\n };\n },\n };\n\n return queryTask;\n }\n async createTypeQueryExecution<T>(\n type: 'Query' | 'Boolean' | 'Number' | 'String' | 'Assert',\n demand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n multimodalPrompt?: TMultimodalPrompt,\n ): Promise<ExecutionResult<T>> {\n const session = this.createExecutionSession(\n taskTitleStr(\n type,\n typeof demand === 'string' ? demand : JSON.stringify(demand),\n ),\n );\n\n const queryTask = await this.createTypeQueryTask(\n type,\n demand,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n\n const runner = session.getRunner();\n const result = await session.appendAndRun(queryTask);\n\n if (!result) {\n throw new Error(\n 'result of taskExecutor.flush() is undefined in function createTypeQueryTask',\n );\n }\n\n const { output, thought } = result;\n\n return {\n output,\n thought,\n runner,\n };\n }\n\n private sleepPlan(timeMs: number): PlanningAction<PlanningActionParamSleep> {\n return {\n type: 'Sleep',\n param: {\n timeMs,\n },\n };\n }\n\n async taskForSleep(timeMs: number, _modelConfig: IModelConfig) {\n return this.taskBuilder.createSleepTask({\n timeMs,\n });\n }\n\n async waitFor(\n assertion: TUserPrompt,\n opt: PlanningActionParamWaitFor,\n modelConfig: IModelConfig,\n ): Promise<ExecutionResult<void>> {\n const { textPrompt, multimodalPrompt } = parsePrompt(assertion);\n\n const description = `waitFor: ${textPrompt}`;\n const session = this.createExecutionSession(\n taskTitleStr('WaitFor', description),\n );\n const runner = session.getRunner();\n const { timeoutMs, checkIntervalMs } = opt;\n\n assert(assertion, 'No assertion for waitFor');\n assert(timeoutMs, 'No timeoutMs for waitFor');\n assert(checkIntervalMs, 'No checkIntervalMs for waitFor');\n\n assert(\n checkIntervalMs <= timeoutMs,\n `wrong config for waitFor: checkIntervalMs must be less than timeoutMs, config: {checkIntervalMs: ${checkIntervalMs}, timeoutMs: ${timeoutMs}}`,\n );\n\n const overallStartTime = Date.now();\n let lastCheckStart = overallStartTime;\n let errorThought = '';\n // Continue checking as long as the previous iteration began within the timeout window.\n while (lastCheckStart - overallStartTime <= timeoutMs) {\n const currentCheckStart = Date.now();\n lastCheckStart = currentCheckStart;\n const queryTask = await this.createTypeQueryTask(\n 'WaitFor',\n textPrompt,\n modelConfig,\n undefined,\n multimodalPrompt,\n );\n\n const result = (await session.appendAndRun(queryTask)) as\n | {\n output: boolean;\n thought?: string;\n }\n | undefined;\n\n if (result?.output) {\n return {\n output: undefined,\n runner,\n };\n }\n\n errorThought =\n result?.thought ||\n (!result && `No result from assertion: ${textPrompt}`) ||\n `unknown error when waiting for assertion: ${textPrompt}`;\n const now = Date.now();\n if (now - currentCheckStart < checkIntervalMs) {\n const timeRemaining = checkIntervalMs - (now - currentCheckStart);\n const sleepTask = this.taskBuilder.createSleepTask({\n timeMs: timeRemaining,\n });\n await session.append(sleepTask);\n }\n }\n\n return session.appendErrorPlan(`waitFor timeout: ${errorThought}`);\n }\n}\n"],"names":["debug","getDebug","maxErrorCountAllowedInOnePlanningLoop","TaskExecutor","title","options","ExecutionSession","Promise","plans","modelConfigForPlanning","modelConfigForDefaultIntent","userInstruction","yamlString","session","taskTitleStr","task","param","executorContext","uiContext","assert","runner","tasks","result","output","userPrompt","includeBboxInPlanning","backgroundKnowledge","cacheable","replanningCycleLimitOverride","replanCount","yamlFlow","replanningCycleLimit","undefined","errorCountInOnePlanningLoop","startTime","Date","vlMode","uiTarsModelVersion","actionSpace","action","Array","console","planResult","uiTarsPlanning","plan","JSON","actions","log","more_actions_needed_by_instruction","error","usage","rawResponse","sleep","finalActions","timeNow","timeRemaining","executables","String","Error","errorMsg","finalResult","type","demand","modelConfig","opt","multimodalPrompt","queryTask","taskContext","queryDump","applyDump","dump","ifTypeRestricted","demandInput","keyOfResult","booleanPrompt","extractResult","extraPageDescription","tree","descriptionOfTree","ServiceError","data","thought","outputResult","timeMs","_modelConfig","assertion","textPrompt","parsePrompt","description","timeoutMs","checkIntervalMs","overallStartTime","lastCheckStart","errorThought","currentCheckStart","now","sleepTask","interfaceInstance","service","opts","ConversationHistory","TaskBuilder"],"mappings":"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"}
1
+ {"version":3,"file":"agent/tasks.mjs","sources":["../../../src/agent/tasks.ts"],"sourcesContent":["import { ConversationHistory, plan, uiTarsPlanning } from '@/ai-model';\nimport type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type { AbstractInterface } from '@/device';\nimport type Service from '@/service';\nimport type { TaskRunner } from '@/task-runner';\nimport { TaskExecutionError } from '@/task-runner';\nimport type {\n ExecutionTaskApply,\n ExecutionTaskInsightQueryApply,\n ExecutionTaskPlanningApply,\n ExecutionTaskProgressOptions,\n InterfaceType,\n MidsceneYamlFlowItem,\n PlanningAIResponse,\n PlanningAction,\n PlanningActionParamSleep,\n PlanningActionParamWaitFor,\n ServiceDump,\n ServiceExtractOption,\n ServiceExtractParam,\n} from '@/types';\nimport { ServiceError } from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport { ExecutionSession } from './execution-session';\nimport { TaskBuilder } from './task-builder';\nimport type { TaskCache } from './task-cache';\nexport { locatePlanForLocate } from './task-builder';\nimport { descriptionOfTree } from '@midscene/shared/extractor';\nimport { taskTitleStr } from './ui-utils';\nimport { parsePrompt } from './utils';\n\ninterface ExecutionResult<OutputType = any> {\n output: OutputType;\n thought?: string;\n runner: TaskRunner;\n}\n\ninterface TaskExecutorHooks {\n onTaskUpdate?: (\n runner: TaskRunner,\n error?: TaskExecutionError,\n ) => Promise<void> | void;\n}\n\nconst debug = getDebug('device-task-executor');\nconst maxErrorCountAllowedInOnePlanningLoop = 5;\n\nexport { TaskExecutionError };\n\nexport class TaskExecutor {\n interface: AbstractInterface;\n\n service: Service;\n\n taskCache?: TaskCache;\n\n private readonly taskBuilder: TaskBuilder;\n\n private conversationHistory: ConversationHistory;\n\n onTaskStartCallback?: ExecutionTaskProgressOptions['onTaskStart'];\n\n private readonly hooks?: TaskExecutorHooks;\n\n replanningCycleLimit?: number;\n\n // @deprecated use .interface instead\n get page() {\n return this.interface;\n }\n\n constructor(\n interfaceInstance: AbstractInterface,\n service: Service,\n opts: {\n taskCache?: TaskCache;\n onTaskStart?: ExecutionTaskProgressOptions['onTaskStart'];\n replanningCycleLimit?: number;\n hooks?: TaskExecutorHooks;\n },\n ) {\n this.interface = interfaceInstance;\n this.service = service;\n this.taskCache = opts.taskCache;\n this.onTaskStartCallback = opts?.onTaskStart;\n this.replanningCycleLimit = opts.replanningCycleLimit;\n this.hooks = opts.hooks;\n this.conversationHistory = new ConversationHistory();\n this.taskBuilder = new TaskBuilder({\n interfaceInstance,\n service,\n taskCache: opts.taskCache,\n });\n }\n\n private createExecutionSession(\n title: string,\n options?: { tasks?: ExecutionTaskApply[] },\n ) {\n return new ExecutionSession(\n title,\n () => Promise.resolve(this.service.contextRetrieverFn()),\n {\n onTaskStart: this.onTaskStartCallback,\n tasks: options?.tasks,\n onTaskUpdate: this.hooks?.onTaskUpdate,\n },\n );\n }\n\n public async convertPlanToExecutable(\n plans: PlanningAction[],\n modelConfigForPlanning: IModelConfig,\n modelConfigForDefaultIntent: IModelConfig,\n options?: {\n cacheable?: boolean;\n subTask?: boolean;\n },\n ) {\n return this.taskBuilder.build(\n plans,\n modelConfigForPlanning,\n modelConfigForDefaultIntent,\n options,\n );\n }\n\n async loadYamlFlowAsPlanning(userInstruction: string, yamlString: string) {\n const session = this.createExecutionSession(\n taskTitleStr('Action', userInstruction),\n );\n\n const task: ExecutionTaskPlanningApply = {\n type: 'Planning',\n subType: 'LoadYaml',\n param: {\n userInstruction,\n },\n executor: async (param, executorContext) => {\n const { uiContext } = executorContext;\n assert(uiContext, 'uiContext is required for Planning task');\n return {\n output: {\n actions: [],\n more_actions_needed_by_instruction: false,\n log: '',\n yamlString,\n },\n cache: {\n hit: true,\n },\n hitBy: {\n from: 'Cache',\n context: {\n yamlString,\n },\n },\n };\n },\n };\n const runner = session.getRunner();\n await session.appendAndRun(task);\n\n return {\n runner,\n };\n }\n\n async runPlans(\n title: string,\n plans: PlanningAction[],\n modelConfigForPlanning: IModelConfig,\n modelConfigForDefaultIntent: IModelConfig,\n ): Promise<ExecutionResult> {\n const session = this.createExecutionSession(title);\n const { tasks } = await this.convertPlanToExecutable(\n plans,\n modelConfigForPlanning,\n modelConfigForDefaultIntent,\n );\n const runner = session.getRunner();\n const result = await session.appendAndRun(tasks);\n const { output } = result ?? {};\n return {\n output,\n runner,\n };\n }\n\n async action(\n userPrompt: string,\n modelConfigForPlanning: IModelConfig,\n modelConfigForDefaultIntent: IModelConfig,\n includeBboxInPlanning: boolean,\n backgroundKnowledge?: string,\n cacheable?: boolean,\n replanningCycleLimitOverride?: number,\n imagesIncludeCount?: number,\n ): Promise<\n ExecutionResult<\n | {\n yamlFlow?: MidsceneYamlFlowItem[]; // for cache use\n }\n | undefined\n >\n > {\n this.conversationHistory.reset();\n\n const session = this.createExecutionSession(\n taskTitleStr('Action', userPrompt),\n );\n const runner = session.getRunner();\n\n let replanCount = 0;\n const yamlFlow: MidsceneYamlFlowItem[] = [];\n const replanningCycleLimit =\n replanningCycleLimitOverride ?? this.replanningCycleLimit;\n assert(\n replanningCycleLimit !== undefined,\n 'replanningCycleLimit is required for TaskExecutor.action',\n );\n\n let errorCountInOnePlanningLoop = 0; // count the number of errors in one planning loop\n\n // Main planning loop - unified plan/replan logic\n while (true) {\n const result = await session.appendAndRun(\n {\n type: 'Planning',\n subType: 'Plan',\n param: {\n userInstruction: userPrompt,\n aiActionContext: backgroundKnowledge,\n imagesIncludeCount,\n },\n executor: async (param, executorContext) => {\n const startTime = Date.now();\n const { uiContext } = executorContext;\n assert(uiContext, 'uiContext is required for Planning task');\n const { vlMode } = modelConfigForPlanning;\n const uiTarsModelVersion =\n vlMode === 'vlm-ui-tars'\n ? modelConfigForPlanning.uiTarsModelVersion\n : undefined;\n\n assert(\n this.interface.actionSpace,\n 'actionSpace for device is not implemented',\n );\n const actionSpace = await this.interface.actionSpace();\n debug(\n 'actionSpace for this interface is:',\n actionSpace.map((action) => action.name).join(', '),\n );\n assert(Array.isArray(actionSpace), 'actionSpace must be an array');\n if (actionSpace.length === 0) {\n console.warn(\n `ActionSpace for ${this.interface.interfaceType} is empty. This may lead to unexpected behavior.`,\n );\n }\n\n const planResult = await (uiTarsModelVersion\n ? uiTarsPlanning\n : plan)(param.userInstruction, {\n context: uiContext,\n actionContext: param.aiActionContext,\n interfaceType: this.interface.interfaceType as InterfaceType,\n actionSpace,\n modelConfig: modelConfigForPlanning,\n conversationHistory: this.conversationHistory,\n includeBbox: includeBboxInPlanning,\n imagesIncludeCount,\n });\n debug('planResult', JSON.stringify(planResult, null, 2));\n\n const {\n actions,\n log,\n more_actions_needed_by_instruction,\n error,\n usage,\n rawResponse,\n sleep,\n } = planResult;\n\n executorContext.task.log = {\n ...(executorContext.task.log || {}),\n rawResponse,\n };\n executorContext.task.usage = usage;\n executorContext.task.output = {\n actions: actions || [],\n more_actions_needed_by_instruction,\n log,\n yamlFlow: planResult.yamlFlow,\n };\n executorContext.uiContext = uiContext;\n\n const finalActions = [...(actions || [])];\n\n if (sleep) {\n const timeNow = Date.now();\n const timeRemaining = sleep - (timeNow - startTime);\n if (timeRemaining > 0) {\n finalActions.push(this.sleepPlan(timeRemaining));\n }\n }\n\n assert(!error, `Failed to continue: ${error}\\n${log || ''}`);\n\n return {\n cache: {\n hit: false,\n },\n } as any;\n },\n },\n {\n allowWhenError: true,\n },\n );\n\n const planResult = result?.output as PlanningAIResponse | undefined;\n\n // Execute planned actions\n const plans = planResult?.actions || [];\n yamlFlow.push(...(planResult?.yamlFlow || []));\n\n let executables: Awaited<ReturnType<typeof this.convertPlanToExecutable>>;\n try {\n executables = await this.convertPlanToExecutable(\n plans,\n modelConfigForPlanning,\n modelConfigForDefaultIntent,\n {\n cacheable,\n subTask: true,\n },\n );\n } catch (error) {\n return session.appendErrorPlan(\n `Error converting plans to executable tasks: ${error}, plans: ${JSON.stringify(\n plans,\n )}`,\n );\n }\n if (this.conversationHistory.pendingFeedbackMessage) {\n console.warn(\n 'unconsumed pending feedback message detected, this may lead to unexpected planning result:',\n this.conversationHistory.pendingFeedbackMessage,\n );\n }\n let errorFlag = false;\n try {\n await session.appendAndRun(executables.tasks);\n } catch (error: any) {\n errorFlag = true;\n errorCountInOnePlanningLoop++;\n this.conversationHistory.pendingFeedbackMessage = `Error executing running tasks: ${error?.message || String(error)}`;\n debug(\n 'error when executing running tasks, but continue to run if it is not too many errors:',\n error instanceof Error ? error.message : String(error),\n 'current error count in one planning loop:',\n errorCountInOnePlanningLoop,\n );\n }\n\n if (errorCountInOnePlanningLoop > maxErrorCountAllowedInOnePlanningLoop) {\n return session.appendErrorPlan('Too many errors in one planning loop');\n }\n\n // Check if task is complete\n if (!planResult?.more_actions_needed_by_instruction) {\n if (errorFlag) {\n debug(\n 'more_actions_needed_by_instruction is false, but there are errors in one planning loop, continue to run',\n );\n } else {\n break;\n }\n }\n\n // Increment replan count for next iteration\n ++replanCount;\n\n if (replanCount > replanningCycleLimit) {\n const errorMsg = `Replanned ${replanningCycleLimit} times, exceeding the limit. Please configure a larger value for replanningCycleLimit (or use MIDSCENE_REPLANNING_CYCLE_LIMIT) to handle more complex tasks.`;\n return session.appendErrorPlan(errorMsg);\n }\n\n if (!this.conversationHistory.pendingFeedbackMessage) {\n this.conversationHistory.pendingFeedbackMessage =\n 'I have finished the action previously planned.';\n }\n }\n\n const finalResult = {\n output: {\n yamlFlow,\n },\n runner,\n };\n return finalResult;\n }\n\n private createTypeQueryTask(\n type: 'Query' | 'Boolean' | 'Number' | 'String' | 'Assert' | 'WaitFor',\n demand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n multimodalPrompt?: TMultimodalPrompt,\n ) {\n const queryTask: ExecutionTaskInsightQueryApply = {\n type: 'Insight',\n subType: type,\n param: {\n dataDemand: multimodalPrompt\n ? ({\n demand,\n multimodalPrompt,\n } as never)\n : demand, // for user param presentation in report right sidebar\n },\n executor: async (param, taskContext) => {\n const { task } = taskContext;\n let queryDump: ServiceDump | undefined;\n const applyDump = (dump: ServiceDump) => {\n queryDump = dump;\n task.log = {\n dump,\n };\n };\n\n // Get context for query operations\n const uiContext = taskContext.uiContext;\n assert(uiContext, 'uiContext is required for Query task');\n\n const ifTypeRestricted = type !== 'Query';\n let demandInput = demand;\n let keyOfResult = 'result';\n if (ifTypeRestricted && (type === 'Assert' || type === 'WaitFor')) {\n keyOfResult = 'StatementIsTruthy';\n const booleanPrompt =\n type === 'Assert'\n ? `Boolean, whether the following statement is true: ${demand}`\n : `Boolean, the user wants to do some 'wait for' operation, please check whether the following statement is true: ${demand}`;\n demandInput = {\n [keyOfResult]: booleanPrompt,\n };\n } else if (ifTypeRestricted) {\n demandInput = {\n [keyOfResult]: `${type}, ${demand}`,\n };\n }\n\n let extractResult;\n\n let extraPageDescription = '';\n if (opt?.domIncluded && this.interface.getElementsNodeTree) {\n debug('appending tree info for page');\n const tree = await this.interface.getElementsNodeTree();\n extraPageDescription = await descriptionOfTree(\n tree,\n 200,\n false,\n opt?.domIncluded === 'visible-only',\n );\n }\n\n try {\n extractResult = await this.service.extract<any>(\n demandInput,\n modelConfig,\n opt,\n extraPageDescription,\n multimodalPrompt,\n );\n } catch (error) {\n if (error instanceof ServiceError) {\n applyDump(error.dump);\n }\n throw error;\n }\n\n const { data, usage, thought, dump } = extractResult;\n applyDump(dump);\n\n let outputResult = data;\n if (ifTypeRestricted) {\n // If AI returned a plain string instead of structured format, use it directly\n if (typeof data === 'string') {\n outputResult = data;\n } else if (type === 'WaitFor') {\n if (data === null || data === undefined) {\n outputResult = false;\n } else {\n outputResult = (data as any)[keyOfResult];\n }\n } else if (data === null || data === undefined) {\n outputResult = null;\n } else {\n assert(\n data?.[keyOfResult] !== undefined,\n 'No result in query data',\n );\n outputResult = (data as any)[keyOfResult];\n }\n }\n\n if (type === 'Assert' && !outputResult) {\n task.usage = usage;\n task.thought = thought;\n throw new Error(`Assertion failed: ${thought}`);\n }\n\n return {\n output: outputResult,\n log: queryDump,\n usage,\n thought,\n };\n },\n };\n\n return queryTask;\n }\n async createTypeQueryExecution<T>(\n type: 'Query' | 'Boolean' | 'Number' | 'String' | 'Assert',\n demand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n multimodalPrompt?: TMultimodalPrompt,\n ): Promise<ExecutionResult<T>> {\n const session = this.createExecutionSession(\n taskTitleStr(\n type,\n typeof demand === 'string' ? demand : JSON.stringify(demand),\n ),\n );\n\n const queryTask = await this.createTypeQueryTask(\n type,\n demand,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n\n const runner = session.getRunner();\n const result = await session.appendAndRun(queryTask);\n\n if (!result) {\n throw new Error(\n 'result of taskExecutor.flush() is undefined in function createTypeQueryTask',\n );\n }\n\n const { output, thought } = result;\n\n return {\n output,\n thought,\n runner,\n };\n }\n\n private sleepPlan(timeMs: number): PlanningAction<PlanningActionParamSleep> {\n return {\n type: 'Sleep',\n param: {\n timeMs,\n },\n };\n }\n\n async taskForSleep(timeMs: number, _modelConfig: IModelConfig) {\n return this.taskBuilder.createSleepTask({\n timeMs,\n });\n }\n\n async waitFor(\n assertion: TUserPrompt,\n opt: PlanningActionParamWaitFor,\n modelConfig: IModelConfig,\n ): Promise<ExecutionResult<void>> {\n const { textPrompt, multimodalPrompt } = parsePrompt(assertion);\n\n const description = `waitFor: ${textPrompt}`;\n const session = this.createExecutionSession(\n taskTitleStr('WaitFor', description),\n );\n const runner = session.getRunner();\n const { timeoutMs, checkIntervalMs } = opt;\n\n assert(assertion, 'No assertion for waitFor');\n assert(timeoutMs, 'No timeoutMs for waitFor');\n assert(checkIntervalMs, 'No checkIntervalMs for waitFor');\n\n assert(\n checkIntervalMs <= timeoutMs,\n `wrong config for waitFor: checkIntervalMs must be less than timeoutMs, config: {checkIntervalMs: ${checkIntervalMs}, timeoutMs: ${timeoutMs}}`,\n );\n\n const overallStartTime = Date.now();\n let lastCheckStart = overallStartTime;\n let errorThought = '';\n // Continue checking as long as the previous iteration began within the timeout window.\n while (lastCheckStart - overallStartTime <= timeoutMs) {\n const currentCheckStart = Date.now();\n lastCheckStart = currentCheckStart;\n const queryTask = await this.createTypeQueryTask(\n 'WaitFor',\n textPrompt,\n modelConfig,\n undefined,\n multimodalPrompt,\n );\n\n const result = (await session.appendAndRun(queryTask)) as\n | {\n output: boolean;\n thought?: string;\n }\n | undefined;\n\n if (result?.output) {\n return {\n output: undefined,\n runner,\n };\n }\n\n errorThought =\n result?.thought ||\n (!result && `No result from assertion: ${textPrompt}`) ||\n `unknown error when waiting for assertion: ${textPrompt}`;\n const now = Date.now();\n if (now - currentCheckStart < checkIntervalMs) {\n const timeRemaining = checkIntervalMs - (now - currentCheckStart);\n const sleepTask = this.taskBuilder.createSleepTask({\n timeMs: timeRemaining,\n });\n await session.append(sleepTask);\n }\n }\n\n return session.appendErrorPlan(`waitFor timeout: ${errorThought}`);\n }\n}\n"],"names":["debug","getDebug","maxErrorCountAllowedInOnePlanningLoop","TaskExecutor","title","options","ExecutionSession","Promise","plans","modelConfigForPlanning","modelConfigForDefaultIntent","userInstruction","yamlString","session","taskTitleStr","task","param","executorContext","uiContext","assert","runner","tasks","result","output","userPrompt","includeBboxInPlanning","backgroundKnowledge","cacheable","replanningCycleLimitOverride","imagesIncludeCount","replanCount","yamlFlow","replanningCycleLimit","undefined","errorCountInOnePlanningLoop","startTime","Date","vlMode","uiTarsModelVersion","actionSpace","action","Array","console","planResult","uiTarsPlanning","plan","JSON","actions","log","more_actions_needed_by_instruction","error","usage","rawResponse","sleep","finalActions","timeNow","timeRemaining","executables","errorFlag","String","Error","errorMsg","finalResult","type","demand","modelConfig","opt","multimodalPrompt","queryTask","taskContext","queryDump","applyDump","dump","ifTypeRestricted","demandInput","keyOfResult","booleanPrompt","extractResult","extraPageDescription","tree","descriptionOfTree","ServiceError","data","thought","outputResult","timeMs","_modelConfig","assertion","textPrompt","parsePrompt","description","timeoutMs","checkIntervalMs","overallStartTime","lastCheckStart","errorThought","currentCheckStart","now","sleepTask","interfaceInstance","service","opts","ConversationHistory","TaskBuilder"],"mappings":"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"}
@@ -100,7 +100,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
100
100
  return;
101
101
  }
102
102
  }
103
- const getMidsceneVersion = ()=>"1.0.1-beta-20251208071759.0";
103
+ const getMidsceneVersion = ()=>"1.0.1-beta-20251208085324.0";
104
104
  const parsePrompt = (prompt)=>{
105
105
  if ('string' == typeof prompt) return {
106
106
  textPrompt: prompt,
@@ -27,10 +27,27 @@ class ConversationHistory {
27
27
  reset() {
28
28
  this.messages.length = 0;
29
29
  }
30
- snapshot() {
31
- return [
30
+ snapshot(maxImages) {
31
+ if (void 0 === maxImages) return [
32
32
  ...this.messages
33
33
  ];
34
+ const clonedMessages = structuredClone(this.messages);
35
+ let imageCount = 0;
36
+ for(let i = clonedMessages.length - 1; i >= 0; i--){
37
+ const message = clonedMessages[i];
38
+ const content = message.content;
39
+ if (Array.isArray(content)) for(let j = 0; j < content.length; j++){
40
+ const item = content[j];
41
+ if ('image_url' === item.type) {
42
+ imageCount++;
43
+ if (imageCount > maxImages) content[j] = {
44
+ type: 'text',
45
+ text: '(image ignored due to size optimization)'
46
+ };
47
+ }
48
+ }
49
+ }
50
+ return clonedMessages;
34
51
  }
35
52
  get length() {
36
53
  return this.messages.length;
@@ -1 +1 @@
1
- {"version":3,"file":"ai-model/conversation-history.mjs","sources":["../../../src/ai-model/conversation-history.ts"],"sourcesContent":["import type { ChatCompletionMessageParam } from 'openai/resources/index';\n\nexport interface ConversationHistoryOptions {\n initialMessages?: ChatCompletionMessageParam[];\n}\n\nexport class ConversationHistory {\n private readonly messages: ChatCompletionMessageParam[] = [];\n\n public pendingFeedbackMessage: string;\n\n constructor(options?: ConversationHistoryOptions) {\n if (options?.initialMessages?.length) {\n this.seed(options.initialMessages);\n }\n this.pendingFeedbackMessage = '';\n }\n\n resetPendingFeedbackMessageIfExists() {\n if (this.pendingFeedbackMessage) {\n this.pendingFeedbackMessage = '';\n }\n }\n\n append(message: ChatCompletionMessageParam) {\n this.messages.push(message);\n }\n\n seed(messages: ChatCompletionMessageParam[]) {\n this.reset();\n messages.forEach((message) => {\n this.append(message);\n });\n }\n\n reset() {\n this.messages.length = 0;\n }\n\n snapshot(): ChatCompletionMessageParam[] {\n return [...this.messages];\n }\n\n get length(): number {\n return this.messages.length;\n }\n\n [Symbol.iterator](): IterableIterator<ChatCompletionMessageParam> {\n return this.messages[Symbol.iterator]();\n }\n\n toJSON(): ChatCompletionMessageParam[] {\n return this.snapshot();\n }\n}\n"],"names":["Symbol","ConversationHistory","message","messages","options"],"mappings":";;;;;;;;;;;eA+CGA,OAAO,QAAQ;;AAzCX,MAAMC;IAYX,sCAAsC;QACpC,IAAI,IAAI,CAAC,sBAAsB,EAC7B,IAAI,CAAC,sBAAsB,GAAG;IAElC;IAEA,OAAOC,OAAmC,EAAE;QAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAACA;IACrB;IAEA,KAAKC,QAAsC,EAAE;QAC3C,IAAI,CAAC,KAAK;QACVA,SAAS,OAAO,CAAC,CAACD;YAChB,IAAI,CAAC,MAAM,CAACA;QACd;IACF;IAEA,QAAQ;QACN,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG;IACzB;IAEA,WAAyC;QACvC,OAAO;eAAI,IAAI,CAAC,QAAQ;SAAC;IAC3B;IAEA,IAAI,SAAiB;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM;IAC7B;IAEA,CAAC,cAAD,GAAkE;QAChE,OAAO,IAAI,CAAC,QAAQ,CAACF,OAAO,QAAQ,CAAC;IACvC;IAEA,SAAuC;QACrC,OAAO,IAAI,CAAC,QAAQ;IACtB;IA1CA,YAAYI,OAAoC,CAAE;QAJlD,uBAAiB,YAAyC,EAAE;QAE5D,uBAAO,0BAAP;QAGE,IAAIA,SAAS,iBAAiB,QAC5B,IAAI,CAAC,IAAI,CAACA,QAAQ,eAAe;QAEnC,IAAI,CAAC,sBAAsB,GAAG;IAChC;AAsCF"}
1
+ {"version":3,"file":"ai-model/conversation-history.mjs","sources":["../../../src/ai-model/conversation-history.ts"],"sourcesContent":["import type { ChatCompletionMessageParam } from 'openai/resources/index';\n\nexport interface ConversationHistoryOptions {\n initialMessages?: ChatCompletionMessageParam[];\n}\n\nexport class ConversationHistory {\n private readonly messages: ChatCompletionMessageParam[] = [];\n\n public pendingFeedbackMessage: string;\n\n constructor(options?: ConversationHistoryOptions) {\n if (options?.initialMessages?.length) {\n this.seed(options.initialMessages);\n }\n this.pendingFeedbackMessage = '';\n }\n\n resetPendingFeedbackMessageIfExists() {\n if (this.pendingFeedbackMessage) {\n this.pendingFeedbackMessage = '';\n }\n }\n\n append(message: ChatCompletionMessageParam) {\n this.messages.push(message);\n }\n\n seed(messages: ChatCompletionMessageParam[]) {\n this.reset();\n messages.forEach((message) => {\n this.append(message);\n });\n }\n\n reset() {\n this.messages.length = 0;\n }\n\n /**\n * Snapshot the conversation history, and replace the images with text if the number of images exceeds the limit.\n * @param maxImages - The maximum number of images to include in the snapshot. Undefined means no limit.\n * @returns The snapshot of the conversation history.\n */\n snapshot(maxImages?: number): ChatCompletionMessageParam[] {\n if (maxImages === undefined) {\n return [...this.messages];\n }\n\n const clonedMessages = structuredClone(this.messages);\n let imageCount = 0;\n\n // Traverse from the end to the beginning\n for (let i = clonedMessages.length - 1; i >= 0; i--) {\n const message = clonedMessages[i];\n const content = message.content;\n\n // Only process if content is an array\n if (Array.isArray(content)) {\n for (let j = 0; j < content.length; j++) {\n const item = content[j];\n\n // Check if this is an image\n if (item.type === 'image_url') {\n imageCount++;\n\n // If we've exceeded the limit, replace with text\n if (imageCount > maxImages) {\n content[j] = {\n type: 'text',\n text: '(image ignored due to size optimization)',\n };\n }\n }\n }\n }\n }\n\n return clonedMessages;\n }\n\n get length(): number {\n return this.messages.length;\n }\n\n [Symbol.iterator](): IterableIterator<ChatCompletionMessageParam> {\n return this.messages[Symbol.iterator]();\n }\n\n toJSON(): ChatCompletionMessageParam[] {\n return this.snapshot();\n }\n}\n"],"names":["Symbol","ConversationHistory","message","messages","maxImages","undefined","clonedMessages","structuredClone","imageCount","i","content","Array","j","item","options"],"mappings":";;;;;;;;;;;eAqFGA,OAAO,QAAQ;;AA/EX,MAAMC;IAYX,sCAAsC;QACpC,IAAI,IAAI,CAAC,sBAAsB,EAC7B,IAAI,CAAC,sBAAsB,GAAG;IAElC;IAEA,OAAOC,OAAmC,EAAE;QAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAACA;IACrB;IAEA,KAAKC,QAAsC,EAAE;QAC3C,IAAI,CAAC,KAAK;QACVA,SAAS,OAAO,CAAC,CAACD;YAChB,IAAI,CAAC,MAAM,CAACA;QACd;IACF;IAEA,QAAQ;QACN,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG;IACzB;IAOA,SAASE,SAAkB,EAAgC;QACzD,IAAIA,AAAcC,WAAdD,WACF,OAAO;eAAI,IAAI,CAAC,QAAQ;SAAC;QAG3B,MAAME,iBAAiBC,gBAAgB,IAAI,CAAC,QAAQ;QACpD,IAAIC,aAAa;QAGjB,IAAK,IAAIC,IAAIH,eAAe,MAAM,GAAG,GAAGG,KAAK,GAAGA,IAAK;YACnD,MAAMP,UAAUI,cAAc,CAACG,EAAE;YACjC,MAAMC,UAAUR,QAAQ,OAAO;YAG/B,IAAIS,MAAM,OAAO,CAACD,UAChB,IAAK,IAAIE,IAAI,GAAGA,IAAIF,QAAQ,MAAM,EAAEE,IAAK;gBACvC,MAAMC,OAAOH,OAAO,CAACE,EAAE;gBAGvB,IAAIC,AAAc,gBAAdA,KAAK,IAAI,EAAkB;oBAC7BL;oBAGA,IAAIA,aAAaJ,WACfM,OAAO,CAACE,EAAE,GAAG;wBACX,MAAM;wBACN,MAAM;oBACR;gBAEJ;YACF;QAEJ;QAEA,OAAON;IACT;IAEA,IAAI,SAAiB;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM;IAC7B;IAEA,CAAC,cAAD,GAAkE;QAChE,OAAO,IAAI,CAAC,QAAQ,CAACN,OAAO,QAAQ,CAAC;IACvC;IAEA,SAAuC;QACrC,OAAO,IAAI,CAAC,QAAQ;IACtB;IAhFA,YAAYc,OAAoC,CAAE;QAJlD,uBAAiB,YAAyC,EAAE;QAE5D,uBAAO,0BAAP;QAGE,IAAIA,SAAS,iBAAiB,QAC5B,IAAI,CAAC,IAAI,CAACA,QAAQ,eAAe;QAEnC,IAAI,CAAC,sBAAsB,GAAG;IAChC;AA4EF"}
@@ -25,13 +25,14 @@ async function plan(userInstruction, opts) {
25
25
  imageHeight = paddedResult.height;
26
26
  imagePayload = paddedResult.imageBase64;
27
27
  }
28
+ const actionContext = opts.actionContext ? `<high_priority_knowledge>${opts.actionContext}</high_priority_knowledge>\n` : '';
28
29
  const instruction = [
29
30
  {
30
31
  role: 'user',
31
32
  content: [
32
33
  {
33
34
  type: 'text',
34
- text: `<high_priority_knowledge>${opts.actionContext}</high_priority_knowledge>\n<user_instruction>${userInstruction}</user_instruction>`
35
+ text: `${actionContext}<user_instruction>${userInstruction}</user_instruction>`
35
36
  }
36
37
  ]
37
38
  }
@@ -72,7 +73,7 @@ async function plan(userInstruction, opts) {
72
73
  ]
73
74
  };
74
75
  conversationHistory.append(latestFeedbackMessage);
75
- const historyLog = conversationHistory.snapshot();
76
+ const historyLog = conversationHistory.snapshot(opts.imagesIncludeCount);
76
77
  const msgs = [
77
78
  {
78
79
  role: 'system',
@@ -1 +1 @@
1
- {"version":3,"file":"ai-model/llm-planning.mjs","sources":["../../../src/ai-model/llm-planning.ts"],"sourcesContent":["import type {\n DeviceAction,\n InterfaceType,\n PlanningAIResponse,\n RawResponsePlanningAIResponse,\n UIContext,\n} from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport { paddingToMatchBlockByBase64 } from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport {\n AIActionType,\n buildYamlFlowFromPlans,\n fillBboxParam,\n findAllMidsceneLocatorField,\n} from '../common';\nimport type { ConversationHistory } from './conversation-history';\nimport { systemPromptToTaskPlanning } from './prompt/llm-planning';\nimport { callAIWithObjectResponse } from './service-caller/index';\n\nconst debug = getDebug('planning');\n\nexport async function plan(\n userInstruction: string,\n opts: {\n context: UIContext;\n interfaceType: InterfaceType;\n actionSpace: DeviceAction<any>[];\n actionContext?: string;\n modelConfig: IModelConfig;\n conversationHistory: ConversationHistory;\n includeBbox: boolean;\n },\n): Promise<PlanningAIResponse> {\n const { context, modelConfig, conversationHistory } = opts;\n const { screenshotBase64, size } = context;\n\n const { vlMode } = modelConfig;\n\n const systemPrompt = await systemPromptToTaskPlanning({\n actionSpace: opts.actionSpace,\n vlMode,\n includeBbox: opts.includeBbox,\n });\n\n let imagePayload = screenshotBase64;\n let imageWidth = size.width;\n let imageHeight = size.height;\n const rightLimit = imageWidth;\n const bottomLimit = imageHeight;\n\n // Process image based on VL mode requirements\n if (vlMode === 'qwen2.5-vl') {\n const paddedResult = await paddingToMatchBlockByBase64(imagePayload);\n imageWidth = paddedResult.width;\n imageHeight = paddedResult.height;\n imagePayload = paddedResult.imageBase64;\n }\n\n const instruction: ChatCompletionMessageParam[] = [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `<high_priority_knowledge>${opts.actionContext}</high_priority_knowledge>\\n<user_instruction>${userInstruction}</user_instruction>`,\n },\n ],\n },\n ];\n\n let latestFeedbackMessage: ChatCompletionMessageParam;\n\n if (conversationHistory.pendingFeedbackMessage) {\n latestFeedbackMessage = {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `${conversationHistory.pendingFeedbackMessage}. The last screenshot is attached. Please going on according to the instruction.`,\n },\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n };\n\n conversationHistory.resetPendingFeedbackMessageIfExists();\n } else {\n latestFeedbackMessage = {\n role: 'user',\n content: [\n {\n type: 'text',\n text: 'this is the latest screenshot',\n },\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n };\n }\n conversationHistory.append(latestFeedbackMessage);\n const historyLog = conversationHistory.snapshot();\n\n const msgs: ChatCompletionMessageParam[] = [\n { role: 'system', content: systemPrompt },\n ...instruction,\n ...historyLog,\n ];\n\n const {\n content: planFromAI,\n contentString: rawResponse,\n usage,\n } = await callAIWithObjectResponse<RawResponsePlanningAIResponse>(\n msgs,\n AIActionType.PLAN,\n modelConfig,\n );\n\n const actions = planFromAI.action ? [planFromAI.action] : [];\n const returnValue: PlanningAIResponse = {\n ...planFromAI,\n actions,\n rawResponse,\n usage,\n yamlFlow: buildYamlFlowFromPlans(\n actions,\n opts.actionSpace,\n planFromAI.sleep,\n ),\n };\n\n assert(planFromAI, \"can't get plans from AI\");\n\n actions.forEach((action) => {\n const type = action.type;\n const actionInActionSpace = opts.actionSpace.find(\n (action) => action.name === type,\n );\n\n debug('actionInActionSpace matched', actionInActionSpace);\n const locateFields = actionInActionSpace\n ? findAllMidsceneLocatorField(actionInActionSpace.paramSchema)\n : [];\n\n debug('locateFields', locateFields);\n\n locateFields.forEach((field) => {\n const locateResult = action.param[field];\n if (locateResult && vlMode !== undefined) {\n // Always use VL mode to fill bbox parameters\n action.param[field] = fillBboxParam(\n locateResult,\n imageWidth,\n imageHeight,\n rightLimit,\n bottomLimit,\n vlMode,\n );\n }\n });\n });\n\n if (\n actions.length === 0 &&\n returnValue.more_actions_needed_by_instruction &&\n !returnValue.sleep\n ) {\n console.warn(\n 'No actions planned for the prompt, but model said more actions are needed:',\n userInstruction,\n );\n }\n\n conversationHistory.append({\n role: 'assistant',\n content: [\n {\n type: 'text',\n text: rawResponse,\n },\n ],\n });\n\n return returnValue;\n}\n"],"names":["debug","getDebug","plan","userInstruction","opts","context","modelConfig","conversationHistory","screenshotBase64","size","vlMode","systemPrompt","systemPromptToTaskPlanning","imagePayload","imageWidth","imageHeight","rightLimit","bottomLimit","paddedResult","paddingToMatchBlockByBase64","instruction","latestFeedbackMessage","historyLog","msgs","planFromAI","rawResponse","usage","callAIWithObjectResponse","AIActionType","actions","returnValue","buildYamlFlowFromPlans","assert","action","type","actionInActionSpace","locateFields","findAllMidsceneLocatorField","field","locateResult","undefined","fillBboxParam","console"],"mappings":"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"}
1
+ {"version":3,"file":"ai-model/llm-planning.mjs","sources":["../../../src/ai-model/llm-planning.ts"],"sourcesContent":["import type {\n DeviceAction,\n InterfaceType,\n PlanningAIResponse,\n RawResponsePlanningAIResponse,\n UIContext,\n} from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport { paddingToMatchBlockByBase64 } from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport {\n AIActionType,\n buildYamlFlowFromPlans,\n fillBboxParam,\n findAllMidsceneLocatorField,\n} from '../common';\nimport type { ConversationHistory } from './conversation-history';\nimport { systemPromptToTaskPlanning } from './prompt/llm-planning';\nimport { callAIWithObjectResponse } from './service-caller/index';\n\nconst debug = getDebug('planning');\n\nexport async function plan(\n userInstruction: string,\n opts: {\n context: UIContext;\n interfaceType: InterfaceType;\n actionSpace: DeviceAction<any>[];\n actionContext?: string;\n modelConfig: IModelConfig;\n conversationHistory: ConversationHistory;\n includeBbox: boolean;\n imagesIncludeCount?: number;\n },\n): Promise<PlanningAIResponse> {\n const { context, modelConfig, conversationHistory } = opts;\n const { screenshotBase64, size } = context;\n\n const { vlMode } = modelConfig;\n\n const systemPrompt = await systemPromptToTaskPlanning({\n actionSpace: opts.actionSpace,\n vlMode,\n includeBbox: opts.includeBbox,\n });\n\n let imagePayload = screenshotBase64;\n let imageWidth = size.width;\n let imageHeight = size.height;\n const rightLimit = imageWidth;\n const bottomLimit = imageHeight;\n\n // Process image based on VL mode requirements\n if (vlMode === 'qwen2.5-vl') {\n const paddedResult = await paddingToMatchBlockByBase64(imagePayload);\n imageWidth = paddedResult.width;\n imageHeight = paddedResult.height;\n imagePayload = paddedResult.imageBase64;\n }\n\n const actionContext = opts.actionContext\n ? `<high_priority_knowledge>${opts.actionContext}</high_priority_knowledge>\\n`\n : '';\n\n const instruction: ChatCompletionMessageParam[] = [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `${actionContext}<user_instruction>${userInstruction}</user_instruction>`,\n },\n ],\n },\n ];\n\n let latestFeedbackMessage: ChatCompletionMessageParam;\n\n if (conversationHistory.pendingFeedbackMessage) {\n latestFeedbackMessage = {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `${conversationHistory.pendingFeedbackMessage}. The last screenshot is attached. Please going on according to the instruction.`,\n },\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n };\n\n conversationHistory.resetPendingFeedbackMessageIfExists();\n } else {\n latestFeedbackMessage = {\n role: 'user',\n content: [\n {\n type: 'text',\n text: 'this is the latest screenshot',\n },\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n };\n }\n conversationHistory.append(latestFeedbackMessage);\n const historyLog = conversationHistory.snapshot(opts.imagesIncludeCount);\n\n const msgs: ChatCompletionMessageParam[] = [\n { role: 'system', content: systemPrompt },\n ...instruction,\n ...historyLog,\n ];\n\n const {\n content: planFromAI,\n contentString: rawResponse,\n usage,\n } = await callAIWithObjectResponse<RawResponsePlanningAIResponse>(\n msgs,\n AIActionType.PLAN,\n modelConfig,\n );\n\n const actions = planFromAI.action ? [planFromAI.action] : [];\n const returnValue: PlanningAIResponse = {\n ...planFromAI,\n actions,\n rawResponse,\n usage,\n yamlFlow: buildYamlFlowFromPlans(\n actions,\n opts.actionSpace,\n planFromAI.sleep,\n ),\n };\n\n assert(planFromAI, \"can't get plans from AI\");\n\n actions.forEach((action) => {\n const type = action.type;\n const actionInActionSpace = opts.actionSpace.find(\n (action) => action.name === type,\n );\n\n debug('actionInActionSpace matched', actionInActionSpace);\n const locateFields = actionInActionSpace\n ? findAllMidsceneLocatorField(actionInActionSpace.paramSchema)\n : [];\n\n debug('locateFields', locateFields);\n\n locateFields.forEach((field) => {\n const locateResult = action.param[field];\n if (locateResult && vlMode !== undefined) {\n // Always use VL mode to fill bbox parameters\n action.param[field] = fillBboxParam(\n locateResult,\n imageWidth,\n imageHeight,\n rightLimit,\n bottomLimit,\n vlMode,\n );\n }\n });\n });\n\n if (\n actions.length === 0 &&\n returnValue.more_actions_needed_by_instruction &&\n !returnValue.sleep\n ) {\n console.warn(\n 'No actions planned for the prompt, but model said more actions are needed:',\n userInstruction,\n );\n }\n\n conversationHistory.append({\n role: 'assistant',\n content: [\n {\n type: 'text',\n text: rawResponse,\n },\n ],\n });\n\n return returnValue;\n}\n"],"names":["debug","getDebug","plan","userInstruction","opts","context","modelConfig","conversationHistory","screenshotBase64","size","vlMode","systemPrompt","systemPromptToTaskPlanning","imagePayload","imageWidth","imageHeight","rightLimit","bottomLimit","paddedResult","paddingToMatchBlockByBase64","actionContext","instruction","latestFeedbackMessage","historyLog","msgs","planFromAI","rawResponse","usage","callAIWithObjectResponse","AIActionType","actions","returnValue","buildYamlFlowFromPlans","assert","action","type","actionInActionSpace","locateFields","findAllMidsceneLocatorField","field","locateResult","undefined","fillBboxParam","console"],"mappings":"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"}
@@ -134,7 +134,7 @@ Please tell what the next one action is (or null if no action should be done) to
134
134
  - Make sure the previous actions are completed successfully before performing the next step
135
135
  - If there are some error messages reported by the previous actions, don't give up, try parse a new action to recover. If the error persists for more than 5 times, you should think this is an error and set the "error" field to the error message.
136
136
  - If there is nothing to do but waiting, set the "sleep" field to the positive waiting time in milliseconds and null for the "action" field.
137
- - When the next step is to assert something, this is a very important step, you should think about it carefully and give a solid result. Write your result in the "log" field like this: "Assert: <condition>. I think <...>, so the result is <true / false>". You don't need to give the next one action when you are asserting something. If the assertion result is false, think this an fatal error and set the reason into the "error" field. If the assertion result is true, you can continue to the next step.
137
+ - Assertions are also important steps. When getting the assertion instruction, a solid conclusion is required. You should explicitly state your conclusion in the "log" field like this: "Assert: <condition>. I can see <...>, and I think <...>, so the assertion is <truthy / falsy>". If the assertion is falsy, think this an fatal error and set the reason into the "error" field.
138
138
 
139
139
  ## Supporting actions
140
140
  ${actionList}
@@ -149,105 +149,30 @@ Return in JSON format:
149
149
  ${commonOutputFields}
150
150
  "action":
151
151
  {
152
- // one of the supporting actions
152
+ "type": string, // the type of the action
153
+ "param"?: { // The parameter of the action, if any
154
+ // k-v style parameter fields
155
+ },
153
156
  } | null,
154
157
  ,
155
158
  "sleep"?: number, // The sleep time after the action, in milliseconds.
156
159
  }
160
+
161
+ For example, if the instruction is to login and the form has already been filled, this is a valid return value:
162
+
163
+ {
164
+ "log": "Click the login button",
165
+ "more_actions_needed_by_instruction": false,
166
+ "action": {
167
+ "type": "Tap",
168
+ "param": {
169
+ "locate": {
170
+ "prompt": "The login button"${vlMode ? ', "bbox": [100, 200, 300, 400]' : ''}
171
+ }
172
+ }
173
+ }
157
174
  `;
158
175
  }
159
- const planSchema = {
160
- type: 'json_schema',
161
- json_schema: {
162
- name: 'action_items',
163
- strict: false,
164
- schema: {
165
- type: 'object',
166
- strict: false,
167
- properties: {
168
- actions: {
169
- type: 'array',
170
- items: {
171
- type: 'object',
172
- strict: false,
173
- properties: {
174
- thought: {
175
- type: 'string',
176
- description: 'Reasons for generating this task, and why this task is feasible on this page'
177
- },
178
- type: {
179
- type: 'string',
180
- description: 'Type of action'
181
- },
182
- param: {
183
- anyOf: [
184
- {
185
- type: 'null'
186
- },
187
- {
188
- type: 'object',
189
- additionalProperties: true
190
- }
191
- ],
192
- description: 'Parameter of the action'
193
- },
194
- locate: {
195
- type: [
196
- 'object',
197
- 'null'
198
- ],
199
- properties: {
200
- id: {
201
- type: 'string'
202
- },
203
- prompt: {
204
- type: 'string'
205
- }
206
- },
207
- required: [
208
- 'id',
209
- 'prompt'
210
- ],
211
- additionalProperties: false,
212
- description: 'Location information for the target element'
213
- }
214
- },
215
- required: [
216
- 'thought',
217
- 'type',
218
- 'param',
219
- 'locate'
220
- ],
221
- additionalProperties: false
222
- },
223
- description: 'List of actions to be performed'
224
- },
225
- more_actions_needed_by_instruction: {
226
- type: 'boolean',
227
- description: 'If all the actions described in the instruction have been covered by this action and logs, set this field to false.'
228
- },
229
- log: {
230
- type: 'string',
231
- description: 'Log what these planned actions do. Do not include further actions that have not been planned.'
232
- },
233
- error: {
234
- type: [
235
- 'string',
236
- 'null'
237
- ],
238
- description: 'Error messages about unexpected situations'
239
- }
240
- },
241
- required: [
242
- 'actions',
243
- 'more_actions_needed_by_instruction',
244
- 'log',
245
- 'error'
246
- ],
247
- additionalProperties: false
248
- }
249
- }
250
- };
251
- export { descriptionForAction, planSchema, systemPromptToTaskPlanning };
176
+ export { descriptionForAction, systemPromptToTaskPlanning };
252
177
 
253
178
  //# sourceMappingURL=llm-planning.mjs.map