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,709 @@
1
+ /**
2
+ * ============================================================
3
+ * manualSchema.ts —— 应用说明书 schema v2:解析、校验、摘要
4
+ * ------------------------------------------------------------
5
+ * 说明书文件(`plugins/<id>/app/<app>.md`)是**两层**结构:
6
+ *
7
+ * frontmatter 机器层 —— 引擎读它做匹配、不可逆判定、前置条件校验
8
+ * 正文 散文层 —— 给模型看的界面布局、控件语义、坑与恢复出口
9
+ *
10
+ * 一个文件而不是两个,是因为第三件事同样重要:**agent 要能自己改**。
11
+ * 改 md 正文的一节是一次 Edit;改一份深层嵌套的 YAML 缩进是模型的高频
12
+ * 出错点。让它们共处一文件,agent 补充实测发现时不必碰机器层。
13
+ *
14
+ * ★ v2 **向后兼容 v1**:v1 只有 `match` 与 `irreversible`(字符串数组)。
15
+ * 兼容不是好心 —— 它让「搬家」「换解析器」「升级内容」成为三步各自可
16
+ * 验证的动作,出问题时能一眼定位是哪一步。
17
+ *
18
+ * ★ 每个条目自带 `verified`。渲染时**必须**把未验证如实标出来:说明书里
19
+ * 写的坐标和控件多数没实机验证过,丢掉这个限定词,模型会把猜测当事实。
20
+ * ============================================================
21
+ */
22
+ import { parse } from 'yaml';
23
+ import { containsFold, findByElementSpec, flattenAx, matchesRole, } from './axSearch.js';
24
+ /** 「没有任何 v2 声明」的 manual。v1 说明书与合成 guide 都用它。 */
25
+ export const EMPTY_MANUAL = {
26
+ schemaVersion: 1,
27
+ layout: [],
28
+ controls: [],
29
+ flows: [],
30
+ humanBoundary: [],
31
+ };
32
+ // ============================================================
33
+ // 前置条件:只允许引擎能独立验证的谓词
34
+ // ============================================================
35
+ /**
36
+ * 可用谓词。**刻意只有这几个** —— 每一个都能只用「当前这次观测」求值,
37
+ * 不需要任何外部变量绑定。
38
+ *
39
+ * 写不成这几种形状的约束(「确认收件人是本人要发的那个」)请写进
40
+ * `pitfalls` 当散文。把不可验证的东西写成 `precondition:` 会得到一个
41
+ * 永远通过的检查 —— 那比没有检查更糟,因为它看起来像有保护。
42
+ */
43
+ export const PRECONDITION_PREDICATES = [
44
+ /** 前台应用的 id 或名字包含给定文本(大小写不敏感) */
45
+ 'foreground_app_is',
46
+ /** 任一窗口标题包含给定文本 */
47
+ 'window_title_contains',
48
+ /** 当前焦点控件的 role 等于给定值 */
49
+ 'focused_role_is',
50
+ /** 无障碍树可用且不残缺(没有参数) */
51
+ 'ax_available',
52
+ /** 存在 role 为给定值的控件;可写 `role:名字片段` 追加名字约束 */
53
+ 'element_exists',
54
+ ];
55
+ const NO_ARG_PREDICATES = new Set(['ax_available']);
56
+ /** 解析一条前置条件。形状不合法返回 null —— 由调用方按「无效声明」报出。 */
57
+ export function parsePrecondition(raw) {
58
+ const trimmed = raw.trim();
59
+ const spaceIndex = trimmed.indexOf(' ');
60
+ const head = spaceIndex < 0 ? trimmed : trimmed.slice(0, spaceIndex);
61
+ const argument = spaceIndex < 0 ? '' : trimmed.slice(spaceIndex + 1).trim();
62
+ if (!PRECONDITION_PREDICATES.includes(head))
63
+ return null;
64
+ const predicate = head;
65
+ if (NO_ARG_PREDICATES.has(predicate))
66
+ return { predicate, argument: '' };
67
+ if (!argument)
68
+ return null;
69
+ return { predicate, argument };
70
+ }
71
+ export function evaluatePrecondition(raw, state) {
72
+ const parsed = parsePrecondition(raw);
73
+ // 解析不出来的谓词不能当成「不满足」—— 那是说明书写错了,不是现场不对。
74
+ // `validateManual` 会把它报成 error,这里放行。
75
+ if (!parsed || !state)
76
+ return 'unknown';
77
+ const axUsable = state.accessibility !== undefined && state.accessibility.truncated !== true;
78
+ const nodes = axUsable ? flattenAx(state.accessibility?.root) : [];
79
+ switch (parsed.predicate) {
80
+ case 'foreground_app_is': {
81
+ const app = state.activeApp;
82
+ if (!app?.id && !app?.name)
83
+ return 'unknown';
84
+ return containsFold(app.id, parsed.argument) || containsFold(app.name, parsed.argument);
85
+ }
86
+ case 'window_title_contains': {
87
+ const titles = (state.windows ?? []).map((w) => w.title).filter(Boolean);
88
+ if (titles.length === 0)
89
+ return 'unknown';
90
+ return titles.some((title) => containsFold(title, parsed.argument));
91
+ }
92
+ case 'ax_available':
93
+ return axUsable;
94
+ case 'focused_role_is': {
95
+ if (!axUsable)
96
+ return 'unknown';
97
+ const focused = nodes.find((node) => node.focused === true);
98
+ if (!focused)
99
+ return 'unknown';
100
+ return matchesRole(focused, parsed.argument);
101
+ }
102
+ case 'element_exists':
103
+ // ⚠️ AX 不可用时必须是 `unknown` 而不是 `false`。写成
104
+ // `axUsable && find(...)` 会把「看不见」判成「不存在」—— 那正是本函数
105
+ // 三值设计要防的那件事,而且写起来很自然,所以特意留这条注释。
106
+ if (!axUsable)
107
+ return 'unknown';
108
+ // 与 wait_for_element / click-by-text 共用同一套查找(axSearch.ts)——
109
+ // 三者判据分叉会让「前提说成立、等待等不到」这类问题无从排查。
110
+ return findByElementSpec(nodes, parsed.argument).length > 0;
111
+ default:
112
+ return 'unknown';
113
+ }
114
+ }
115
+ /**
116
+ * 收集**指定动作类别**下没能满足的前置条件。
117
+ *
118
+ * ★ 这个函数早先叫 `unmetIrreversiblePreconditions`,扫的是**整份说明书里所有
119
+ * 不可逆步骤**,并在注释里声称「模型正要做一件不可撤销的事,检查所有不可逆
120
+ * 步骤的前置条件,映射是精确的,不是猜的」。**那句话是错的。** 引擎并不知道
121
+ * 模型在执行哪条 flow:微信的「发消息」和「加好友」都是不可逆步骤,模型要发
122
+ * 消息时却会连「加好友」那一步的前提一起检查。今天没爆,只是因为微信四条不
123
+ * 可逆步骤的 precondition 碰巧全都是同一条(`foreground_app_is 微信`)——
124
+ * 任何一份说明书给两类动作写了不同前提,立刻就会出现误拦,而误拦会让模型卡在
125
+ * 一个它无力改变的条件上。
126
+ *
127
+ * 现在的桥梁是 `step.actionClass`:审批时引擎已经算出了这次动作的**有效类别**
128
+ * (见 `computeruse.ts::authorizeIrreversibleAction` 的三级来源),拿它去挑
129
+ * 步骤,映射才真的是精确的。
130
+ *
131
+ * 匹配只看 `actionClass` 相等,不再额外要求 `irreversible === true` —— 「有类别
132
+ * 却不是不可逆步骤」是说明书写错了,由 `validateManual` 报成 error,不在这里
133
+ * 二次判定(两处各判一次,迟早给出不一致的答案)。
134
+ *
135
+ * 同一类别可以有多条 precondition(写在不同 flow 的不同步骤上),**全部检查、
136
+ * 全部必须成立**:它们是同一件事的不同前提,不是备选项。
137
+ *
138
+ * 只返回**明确为 false** 的那些;`unknown` 一律放行(理由见 `evaluatePrecondition`)。
139
+ */
140
+ export function unmetPreconditionsFor(manual, actionClass, state) {
141
+ // 没有有效类别就一条都不查。这不是防御性编程,是语义:类别对不上的时候
142
+ // 我们**确实不知道**作者的哪条前提该管这次动作,凭空拦是猜。
143
+ if (!actionClass)
144
+ return [];
145
+ const unmet = [];
146
+ for (const flow of manual.flows) {
147
+ for (const [index, step] of flow.steps.entries()) {
148
+ if (step.actionClass !== actionClass || !step.precondition)
149
+ continue;
150
+ if (evaluatePrecondition(step.precondition, state) !== false)
151
+ continue;
152
+ unmet.push({
153
+ flowId: flow.id,
154
+ stepIndex: index + 1,
155
+ step: step.do,
156
+ precondition: step.precondition,
157
+ });
158
+ }
159
+ }
160
+ return unmet;
161
+ }
162
+ // ============================================================
163
+ // 解析
164
+ // ============================================================
165
+ const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
166
+ /**
167
+ * 切开 frontmatter 与正文。
168
+ *
169
+ * 解析失败返回空 frontmatter 而**不是**抛错:一份写坏的说明书应该退化成
170
+ * 「这个应用没有说明书」,而不是让整个 ComputerUse 工具挂掉。写坏的事实
171
+ * 由 `validateManual` 报出来(`/computeruse app <name>` 能看到)。
172
+ */
173
+ export function splitManual(content) {
174
+ const match = FRONTMATTER.exec(content);
175
+ if (!match)
176
+ return { raw: {}, body: content.trim() };
177
+ const body = content.slice(match[0].length).trim();
178
+ try {
179
+ const parsed = parse(match[1]);
180
+ return {
181
+ raw: parsed && typeof parsed === 'object' && !Array.isArray(parsed)
182
+ ? parsed
183
+ : {},
184
+ body,
185
+ };
186
+ }
187
+ catch {
188
+ return { raw: {}, body };
189
+ }
190
+ }
191
+ const asString = (value) => typeof value === 'string' && value.trim() ? value.trim() : undefined;
192
+ const asStringList = (value) => {
193
+ if (Array.isArray(value))
194
+ return value.map((item) => String(item).trim()).filter(Boolean);
195
+ const single = asString(value);
196
+ return single ? [single] : [];
197
+ };
198
+ const asBool = (value) => typeof value === 'boolean' ? value : undefined;
199
+ function asRect(value) {
200
+ if (!Array.isArray(value) || value.length !== 4)
201
+ return undefined;
202
+ const nums = value.map((n) => Number(n));
203
+ if (nums.some((n) => !Number.isFinite(n)))
204
+ return undefined;
205
+ return [nums[0], nums[1], nums[2], nums[3]];
206
+ }
207
+ const asRecord = (value) => value && typeof value === 'object' && !Array.isArray(value)
208
+ ? value
209
+ : {};
210
+ /**
211
+ * 归一化不可逆声明。v1 是字符串(`intent:send-message`),v2 允许写成
212
+ * 映射(`- key: Enter`)—— 后者对人更好读,对 YAML 更自然。两种都归一成
213
+ * 引擎既有的 `kind:value` 字符串,因为 `matchIrreversible` 认的是那个形状,
214
+ * 而它不该为了书写风格改判定逻辑。
215
+ */
216
+ export function normalizeIrreversible(value) {
217
+ if (!Array.isArray(value))
218
+ return asStringList(value);
219
+ const out = [];
220
+ for (const entry of value) {
221
+ if (typeof entry === 'string') {
222
+ const trimmed = entry.trim();
223
+ if (trimmed)
224
+ out.push(trimmed);
225
+ continue;
226
+ }
227
+ for (const [kind, raw] of Object.entries(asRecord(entry))) {
228
+ const argument = asString(raw);
229
+ if (argument)
230
+ out.push(`${kind}:${argument}`);
231
+ }
232
+ }
233
+ return out;
234
+ }
235
+ /**
236
+ * 归一化不可逆声明,并提取 critical 级别的条目。
237
+ *
238
+ * 支持三种 frontmatter 写法:
239
+ * 1. 旧格式一维列表 → 全部入 all,critical 为空(由全局黑名单事后兜底)
240
+ * 2. 新格式带分级对象({critical: [...], normal: [...]})
241
+ * 3. 混合:一维列表里既有字符串也有带 critical/normal 键的映射
242
+ *
243
+ * 返回值:
244
+ * all —— 全部不可逆条目(去重)
245
+ * critical —— 应用明确标为高危的条目(all 的子集)
246
+ */
247
+ export function normalizeIrreversibleWithLevels(value) {
248
+ if (value == null)
249
+ return { all: [], critical: [] };
250
+ const allSet = new Set();
251
+ const criticalSet = new Set();
252
+ // 情形 2:值本身是一个带 critical/normal 键的对象
253
+ if (typeof value === 'object' && !Array.isArray(value)) {
254
+ const rec = asRecord(value);
255
+ const criticalRaw = rec.critical;
256
+ const normalRaw = rec.normal;
257
+ for (const entry of normalizeIrreversible(criticalRaw)) {
258
+ allSet.add(entry);
259
+ criticalSet.add(entry);
260
+ }
261
+ for (const entry of normalizeIrreversible(normalRaw)) {
262
+ allSet.add(entry);
263
+ }
264
+ return { all: [...allSet], critical: [...criticalSet] };
265
+ }
266
+ // 情形 1 + 3:值是数组(或能转成数组的字符串)
267
+ const arr = Array.isArray(value) ? value : [value];
268
+ for (const entry of arr) {
269
+ if (typeof entry === 'string') {
270
+ const trimmed = entry.trim();
271
+ if (trimmed)
272
+ allSet.add(trimmed);
273
+ continue;
274
+ }
275
+ if (entry && typeof entry === 'object') {
276
+ const rec = asRecord(entry);
277
+ // 带 critical/normal 键的映射条目
278
+ if ('critical' in rec || 'normal' in rec) {
279
+ for (const e of normalizeIrreversible(rec.critical)) {
280
+ allSet.add(e);
281
+ criticalSet.add(e);
282
+ }
283
+ for (const e of normalizeIrreversible(rec.normal)) {
284
+ allSet.add(e);
285
+ }
286
+ }
287
+ else {
288
+ // 普通的 key:value 映射条目(旧格式的另一种写法)
289
+ for (const e of normalizeIrreversible([entry])) {
290
+ allSet.add(e);
291
+ }
292
+ }
293
+ }
294
+ }
295
+ return { all: [...allSet], critical: [...criticalSet] };
296
+ }
297
+ export function toAppManual(raw) {
298
+ const schemaVersion = Number(raw.schemaVersion) || 1;
299
+ const layout = [];
300
+ for (const entry of Array.isArray(raw.layout) ? raw.layout : []) {
301
+ const record = asRecord(entry);
302
+ const id = asString(record.id);
303
+ if (!id)
304
+ continue;
305
+ layout.push({
306
+ id,
307
+ ...(asRect(record.rect) ? { rect: asRect(record.rect) } : {}),
308
+ ...(asString(record.desc) ? { desc: asString(record.desc) } : {}),
309
+ verified: asBool(record.verified) ?? false,
310
+ });
311
+ }
312
+ const controls = [];
313
+ for (const entry of Array.isArray(raw.controls) ? raw.controls : []) {
314
+ const record = asRecord(entry);
315
+ const id = asString(record.id);
316
+ if (!id)
317
+ continue;
318
+ const ax = asRecord(record.ax);
319
+ const role = asString(ax.role);
320
+ const names = asStringList(ax.name);
321
+ controls.push({
322
+ id,
323
+ ...(role || names.length > 0
324
+ ? { ax: { ...(role ? { role } : {}), ...(names.length > 0 ? { name: names } : {}) } }
325
+ : {}),
326
+ ...(asRect(record.rect) ? { rect: asRect(record.rect) } : {}),
327
+ ...(asString(record.desc) ? { desc: asString(record.desc) } : {}),
328
+ verified: asBool(record.verified) ?? false,
329
+ });
330
+ }
331
+ const flows = [];
332
+ for (const [id, value] of Object.entries(asRecord(raw.flows))) {
333
+ const record = asRecord(value);
334
+ const steps = [];
335
+ for (const step of Array.isArray(record.steps) ? record.steps : []) {
336
+ const stepRecord = asRecord(step);
337
+ const action = asString(stepRecord.do);
338
+ if (!action)
339
+ continue;
340
+ steps.push({
341
+ do: action,
342
+ ...(asString(stepRecord.precondition)
343
+ ? { precondition: asString(stepRecord.precondition) }
344
+ : {}),
345
+ ...(asBool(stepRecord.irreversible) ? { irreversible: true } : {}),
346
+ ...(asString(stepRecord.actionClass)
347
+ ? { actionClass: asString(stepRecord.actionClass) }
348
+ : {}),
349
+ });
350
+ }
351
+ flows.push({
352
+ id,
353
+ ...(asString(record.goal) ? { goal: asString(record.goal) } : {}),
354
+ steps,
355
+ pitfalls: asStringList(record.pitfalls),
356
+ verified: asBool(record.verified) ?? false,
357
+ });
358
+ }
359
+ // 应用专属的人工介入信号。写坏了就跳过(与其它段一致:静默失效比崩掉更糟,
360
+ // 所以 validateManual 会另外把它报成 issue,让作者看得见)。
361
+ // ⚠️ **缺字段的条目不静默丢弃。**
362
+ //
363
+ // 丢掉它等于「作者写了一条,引擎当它不存在,谁都不知道」—— 正是 validateManual
364
+ // 存在要防的那类静默失效。这里原样收下,由 validateManual 报成 error;
365
+ // 运行时那边(humanBoundary.ts)本来就要求 allOf 全部命中,空 allOf 的条目
366
+ // `every` 恒真会误报,所以下面顺带把它挡在 `allOf.length > 0` 之后。
367
+ const humanBoundary = [];
368
+ for (const entry of Array.isArray(raw.humanBoundary) ? raw.humanBoundary : []) {
369
+ const record = asRecord(entry);
370
+ humanBoundary.push({
371
+ reason: asString(record.reason) ?? '',
372
+ summary: asString(record.summary) ?? asString(record.reason) ?? '',
373
+ allOf: asStringList(record.allOf),
374
+ });
375
+ }
376
+ const verificationRaw = asRecord(raw.verification);
377
+ const verification = Object.keys(verificationRaw).length > 0
378
+ ? {
379
+ ...(asString(verificationRaw.environment)
380
+ ? { environment: asString(verificationRaw.environment) }
381
+ : {}),
382
+ ...(asString(verificationRaw.verifiedAt)
383
+ ? { verifiedAt: asString(verificationRaw.verifiedAt) }
384
+ : {}),
385
+ verifiedItems: asStringList(verificationRaw.verifiedItems),
386
+ unverifiedItems: asStringList(verificationRaw.unverifiedItems),
387
+ }
388
+ : undefined;
389
+ return {
390
+ schemaVersion,
391
+ layout,
392
+ controls,
393
+ flows,
394
+ humanBoundary,
395
+ ...(verification ? { verification } : {}),
396
+ };
397
+ }
398
+ // ============================================================
399
+ // 校验
400
+ // ============================================================
401
+ const IRREVERSIBLE_SHAPE = /^(key|region|intent):.+/;
402
+ /**
403
+ * `humanBoundary[].reason` 的合法取值,与 `humanIntervention.ts::HumanInterventionReason` 对齐。
404
+ *
405
+ * ⚠️ 这里是**字符串副本**而不是 import 那个类型:说明书是运行时数据,类型在运行时
406
+ * 不存在,必须有一份能真的拿来比对的值表。两边漂移的代价是「说明书写了个宿主不认识的
407
+ * reason,加载期不报、运行期宿主收到也不认识」——`manualSchema.test.ts` 用一条断言
408
+ * 把两份表钉在一起。
409
+ */
410
+ const HUMAN_INTERVENTION_REASONS = new Set([
411
+ 'password', 'otp', 'captcha', 'qr_login', 'login',
412
+ 'permission', 'security_warning', 'blocked', 'manual',
413
+ ]);
414
+ /**
415
+ * 校验一份说明书。**这是 `/computeruse app <name>` 的产出**,也是防止
416
+ * 「写了声明但引擎认不出」这类静默失效的唯一手段 —— 写错格式不会报错,
417
+ * 只会永远不命中。
418
+ */
419
+ export function validateManual(input) {
420
+ const issues = [];
421
+ if (input.identifiers === 0) {
422
+ issues.push({ level: 'error', message: '没有任何 match 声明,这份说明书永远匹配不上' });
423
+ }
424
+ for (const entry of input.irreversible) {
425
+ if (!IRREVERSIBLE_SHAPE.test(entry)) {
426
+ issues.push({ level: 'error', message: `不可逆声明「${entry}」格式不合法(要 key:/region:/intent: 开头)` });
427
+ }
428
+ }
429
+ // ── 应用专属的人工介入信号 ──
430
+ //
431
+ // 这一段是**引擎自己用**的(不进模型上下文),所以写错了在界面上完全看不出来:
432
+ // 该叫人的时候不叫,任务就一路错下去。三类错都必须在这里拦住。
433
+ for (const [index, boundary] of input.manual.humanBoundary.entries()) {
434
+ const where = `humanBoundary[${index}]`;
435
+ if (!boundary.reason) {
436
+ issues.push({ level: 'error', message: `${where} 缺 reason,这条声明永远不会生效` });
437
+ }
438
+ else if (!HUMAN_INTERVENTION_REASONS.has(boundary.reason)) {
439
+ issues.push({
440
+ level: 'error',
441
+ message: `${where} 的 reason「${boundary.reason}」不是合法取值`
442
+ + `(可选:${[...HUMAN_INTERVENTION_REASONS].join(' / ')})`,
443
+ });
444
+ }
445
+ if (boundary.allOf.length === 0) {
446
+ issues.push({ level: 'error', message: `${where} 的 allOf 为空 —— 空条件恒成立,会把正常界面误报成需要人工介入` });
447
+ }
448
+ for (const pattern of boundary.allOf) {
449
+ try {
450
+ new RegExp(pattern, 'i');
451
+ }
452
+ catch (error) {
453
+ // 坏正则在运行时被 catch 成「不成立」——**永远不触发且不报错**,
454
+ // 是这一段最危险的失效方式,必须在加载期就红。
455
+ issues.push({
456
+ level: 'error',
457
+ message: `${where} 的正则「${pattern}」编译失败(${String(error.message).slice(0, 60)}),这条声明永远不会命中`,
458
+ });
459
+ }
460
+ }
461
+ if (boundary.allOf.length === 1) {
462
+ // 单信号极易误判:微信的「二维码」单独出现可能只是聊天里别人发的图,
463
+ // 正是因此才要求「二维码」+「仅传输文件」两个一起。
464
+ issues.push({
465
+ level: 'warn',
466
+ message: `${where} 只声明了一个信号 —— 单个关键词很容易把正常界面误判成人工介入边界,建议再加一个共现条件`,
467
+ });
468
+ }
469
+ }
470
+ for (const region of input.manual.layout) {
471
+ if (region.rect && region.rect.some((n) => n < 0 || n > 1000)) {
472
+ issues.push({ level: 'error', message: `分区 ${region.id} 的 rect 超出 0–1000 相对坐标范围` });
473
+ }
474
+ }
475
+ for (const control of input.manual.controls) {
476
+ if (!control.ax && !control.rect) {
477
+ issues.push({ level: 'warn', message: `控件 ${control.id} 既没有 ax 锚点也没有 rect,模型无从定位` });
478
+ }
479
+ if (control.rect && !control.ax) {
480
+ issues.push({ level: 'warn', message: `控件 ${control.id} 只有坐标没有 ax 锚点 —— 换版本或换窗口尺寸就会失准` });
481
+ }
482
+ }
483
+ for (const flow of input.manual.flows) {
484
+ if (flow.steps.length === 0) {
485
+ issues.push({ level: 'warn', message: `流程 ${flow.id} 没有任何步骤` });
486
+ }
487
+ for (const [index, step] of flow.steps.entries()) {
488
+ const where = `流程 ${flow.id} 第 ${index + 1} 步`;
489
+ // ① 类别只对不可逆步骤有意义:审批链只在判定「这一步不可撤销」之后才去
490
+ // 挑步骤,可逆步骤上的类别永远不会被读到,写了等于自欺。
491
+ if (step.actionClass && step.irreversible !== true) {
492
+ issues.push({
493
+ level: 'error',
494
+ message: `${where}声明了 actionClass「${step.actionClass}」却没有 irreversible: true`
495
+ + ' —— 动作类别只对不可逆步骤有意义,可逆步骤上的类别引擎永远读不到',
496
+ });
497
+ }
498
+ // ② 不可逆步骤写了 precondition 却没有类别 = **写了但不会生效**。
499
+ // 这正是本次修复要根治的静默失效:作者以为自己加了一道防线,
500
+ // 引擎却因为对不上号而从不检查它。
501
+ if (step.irreversible === true && step.precondition && !step.actionClass) {
502
+ issues.push({
503
+ level: 'warn',
504
+ message: `${where}是不可逆步骤且写了 precondition,但没有 actionClass —— `
505
+ + '引擎无从知道这条前提该管哪一次动作,**这条前置条件不会生效**;'
506
+ + `请补一个来自本说明书 irreversible: 清单的类别名`
507
+ + `(现有:${input.irreversible.join(' / ') || '(清单为空)'})`,
508
+ });
509
+ }
510
+ // ③ 类别必须来自本说明书的 irreversible: 清单。拼错一个字符就永远不命中,
511
+ // 而不命中是**静默**的 —— 与 IRREVERSIBLE_SHAPE 那条校验同一个理由。
512
+ if (step.actionClass && !input.irreversible.includes(step.actionClass)) {
513
+ issues.push({
514
+ level: 'error',
515
+ message: `${where}的 actionClass「${step.actionClass}」不在本说明书的 irreversible: 清单里`
516
+ + `(清单:${input.irreversible.join(' / ') || '空'}),永远不会命中,写了也是静默失效`,
517
+ });
518
+ }
519
+ // ④ 同一 actionClass 出现多条 precondition 是**允许**的,不报。
520
+ // 它们是同一类动作的不同前提(`unmetPreconditionsFor` 全部检查、
521
+ // 全部必须成立),不是重复声明。
522
+ if (!step.precondition)
523
+ continue;
524
+ if (!parsePrecondition(step.precondition)) {
525
+ issues.push({
526
+ level: 'error',
527
+ message: `${where}的 precondition「${step.precondition}」不是引擎能验证的谓词`
528
+ + `(可用:${PRECONDITION_PREDICATES.join(' / ')})`,
529
+ });
530
+ }
531
+ }
532
+ }
533
+ return issues;
534
+ }
535
+ // ============================================================
536
+ // 实测回写(`/computeruse verify` 用)
537
+ // ============================================================
538
+ /**
539
+ * 把某条 flow 的 `verified` 翻成 true。**行级编辑,不重新序列化 YAML。**
540
+ *
541
+ * 用 `yaml` 库 parse → 改 → stringify 会把 frontmatter 里的注释全部抹掉,而
542
+ * 那些注释恰恰是说明书里最难重建的部分(「为什么这里不写坐标」「这条声明
543
+ * 是干什么的」)。只改目标那一行,其余字节不动。
544
+ *
545
+ * 找不到目标(没有这条 flow / 它没有 verified 行 / 已经是 true)时返回 null,
546
+ * 由调用方如实报告 —— 静默返回原文会让人以为写成功了。
547
+ */
548
+ export function markFlowVerified(markdown, flowId) {
549
+ const lines = markdown.split('\n');
550
+ const fenceEnds = [];
551
+ for (const [index, line] of lines.entries()) {
552
+ if (line.trim() === '---')
553
+ fenceEnds.push(index);
554
+ if (fenceEnds.length === 2)
555
+ break;
556
+ }
557
+ if (fenceEnds.length < 2 || fenceEnds[0] !== 0)
558
+ return null;
559
+ const [, end] = fenceEnds;
560
+ const flowsAt = lines.findIndex((line, index) => index < end && /^flows:\s*$/.test(line));
561
+ if (flowsAt < 0)
562
+ return null;
563
+ const flowAt = lines.findIndex((line, index) => index > flowsAt && index < end && line.trimEnd() === ` ${flowId}:`);
564
+ if (flowAt < 0)
565
+ return null;
566
+ for (let index = flowAt + 1; index < end; index += 1) {
567
+ const line = lines[index];
568
+ // 缩进回到 2 格或更少 = 这条 flow 的块结束了
569
+ if (line.trim() && !/^\s{3,}/.test(line))
570
+ break;
571
+ const match = /^(\s+)verified:\s*false\s*$/.exec(line);
572
+ if (match) {
573
+ lines[index] = `${match[1]}verified: true`;
574
+ return lines.join('\n');
575
+ }
576
+ }
577
+ return null;
578
+ }
579
+ /** 实测记录小节的标题。写死是因为两端(capture 模板与 verify 回写)必须对齐。 */
580
+ export const FIELD_NOTES_HEADING = '## 实测记录';
581
+ /**
582
+ * 往 `## 实测记录` 里追加几行。
583
+ *
584
+ * 追加而不是覆盖:这一节是**时间序列**,「上次能用这次不能用」本身就是信息。
585
+ * 占位行 `- (暂无)` 会被第一次真实记录顶掉。
586
+ */
587
+ export function appendFieldNotes(markdown, notes) {
588
+ if (notes.length === 0)
589
+ return markdown;
590
+ const lines = markdown.split('\n');
591
+ const headingAt = lines.findIndex((line) => line.trim() === FIELD_NOTES_HEADING);
592
+ if (headingAt < 0) {
593
+ return [markdown.trimEnd(), '', FIELD_NOTES_HEADING, '', ...notes, ''].join('\n');
594
+ }
595
+ // 找这一节的末尾:下一个 `## ` 标题,或文件结束。
596
+ let end = lines.length;
597
+ for (let index = headingAt + 1; index < lines.length; index += 1) {
598
+ if (/^## /.test(lines[index])) {
599
+ end = index;
600
+ break;
601
+ }
602
+ }
603
+ const section = lines.slice(headingAt, end).filter((line) => line.trim() !== '- (暂无)');
604
+ while (section.length > 0 && section[section.length - 1].trim() === '')
605
+ section.pop();
606
+ return [...lines.slice(0, headingAt), ...section, ...notes, '', ...lines.slice(end)].join('\n');
607
+ }
608
+ // ============================================================
609
+ // 摘要渲染(注入用)
610
+ // ============================================================
611
+ /** 未验证条目的统一标注。短,但不能省 —— 省了就等于把猜测说成事实。 */
612
+ const UNVERIFIED = '(未实机验证)';
613
+ function renderIrreversible(entries) {
614
+ if (entries.length === 0)
615
+ return [];
616
+ const checkable = entries.filter((e) => e.startsWith('key:') || e.startsWith('region:'));
617
+ return [
618
+ '不可逆动作:' + entries.join('、'),
619
+ checkable.length === 0
620
+ ? ' ⚠️ 以上全靠你自报 `intent:"submit"` 才能触发确认 —— 你不报,引擎就拦不住。'
621
+ : ` 其中 ${checkable.length} 条引擎可独立验证。`,
622
+ ];
623
+ }
624
+ function renderLayout(regions) {
625
+ if (regions.length === 0)
626
+ return [];
627
+ return ['界面分区:' + regions
628
+ .map((r) => `${r.id}${r.rect ? `(${r.rect.join(',')})` : ''}${r.desc ? `=${r.desc}` : ''}${r.verified ? '' : UNVERIFIED}`)
629
+ .join('、')];
630
+ }
631
+ function renderControls(controls) {
632
+ if (controls.length === 0)
633
+ return [];
634
+ return ['关键控件:', ...controls.map((c) => {
635
+ const anchor = c.ax
636
+ ? `AX ${c.ax.role ?? '?'}${c.ax.name?.length ? ` 名称∈[${c.ax.name.join('|')}]` : ''}`
637
+ : c.rect ? `坐标(${c.rect.join(',')})` : '无锚点';
638
+ return ` - ${c.id}:${c.desc ?? ''} → ${anchor}${c.verified ? '' : UNVERIFIED}`;
639
+ })];
640
+ }
641
+ function renderFlow(flow, withPitfalls) {
642
+ const lines = [` ▸ ${flow.id}${flow.goal ? `:${flow.goal}` : ''}${flow.verified ? '' : UNVERIFIED}`];
643
+ for (const [index, step] of flow.steps.entries()) {
644
+ const marks = [
645
+ step.irreversible ? '【不可逆】' : '',
646
+ // 类别必须渲染出来:模型得知道这一步该自报哪个 actionClass,才能拿到
647
+ // 精确到这一类的授权与前置条件检查。不给它看,它只能瞎猜或干脆不报。
648
+ step.actionClass ? `【类别:${step.actionClass}】` : '',
649
+ step.precondition ? `【前置:${step.precondition}】` : '',
650
+ ].join('');
651
+ lines.push(` ${index + 1}. ${step.do}${marks}`);
652
+ }
653
+ if (withPitfalls) {
654
+ for (const pitfall of flow.pitfalls)
655
+ lines.push(` ⚠ ${pitfall}`);
656
+ }
657
+ return lines;
658
+ }
659
+ /**
660
+ * 渲染注入用摘要,带确定性的降级阶梯。
661
+ *
662
+ * 阶梯顺序照抄 `prompts/skillList.ts` 的语义:**先丢细节、再丢条目**。
663
+ * 理由相同 —— 一个只剩名字的条目仍然完成了「让模型知道它存在」的使命,
664
+ * 整条丢弃则让它彻底隐身。丢了什么必须说出来,不静默截断。
665
+ */
666
+ export function renderManualDigest(input) {
667
+ const budget = input.budgetChars ?? 4_000;
668
+ const header = [
669
+ `已匹配应用说明书 **${input.name}**(依据 ${input.matchedBy}):${input.path}`,
670
+ ];
671
+ const verification = input.manual.verification;
672
+ if (verification) {
673
+ header.push(` 验证状态:已验证 ${verification.verifiedItems.join('/') || '无'}`
674
+ + ` · 未验证 ${verification.unverifiedItems.join('/') || '无'}`
675
+ + (verification.environment ? ` · 环境 ${verification.environment}` : ''));
676
+ }
677
+ const irreversible = renderIrreversible(input.irreversible);
678
+ const flowsFull = input.manual.flows.length > 0
679
+ ? ['典型流程:', ...input.manual.flows.flatMap((f) => renderFlow(f, true))]
680
+ : [];
681
+ const flowsNoPitfalls = input.manual.flows.length > 0
682
+ ? ['典型流程:', ...input.manual.flows.flatMap((f) => renderFlow(f, false))]
683
+ : [];
684
+ const flowsNamesOnly = input.manual.flows.length > 0
685
+ ? ['典型流程(仅名录,细节见文件):'
686
+ + input.manual.flows.map((f) => f.id).join('、')]
687
+ : [];
688
+ const layout = renderLayout(input.manual.layout);
689
+ const controls = renderControls(input.manual.controls);
690
+ const ladder = [
691
+ { lines: [...header, ...irreversible, ...layout, ...controls, ...flowsFull], dropped: [] },
692
+ { lines: [...header, ...irreversible, ...layout, ...controls, ...flowsNoPitfalls], dropped: ['流程中的坑'] },
693
+ { lines: [...header, ...irreversible, ...controls, ...flowsNoPitfalls], dropped: ['流程中的坑', '界面分区'] },
694
+ { lines: [...header, ...irreversible, ...flowsNamesOnly], dropped: ['流程中的坑', '界面分区', '关键控件', '流程步骤'] },
695
+ { lines: [...header, ...irreversible], dropped: ['界面分区', '关键控件', '典型流程'] },
696
+ ];
697
+ for (const level of ladder) {
698
+ // 省略说明本身也算进预算 —— 否则「降级」会在最后一档反而把总长度顶回去。
699
+ const notice = level.dropped.length > 0
700
+ ? `\n (受预算限制,本次省略:${level.dropped.join('、')}。完整内容 Read 上面那个文件。)`
701
+ : '';
702
+ const text = level.lines.join('\n') + notice;
703
+ if (text.length <= budget || level === ladder[ladder.length - 1]) {
704
+ return { text, dropped: level.dropped };
705
+ }
706
+ }
707
+ // 不可达:上面的循环在最后一档必定返回。
708
+ return { text: header.join('\n'), dropped: [] };
709
+ }