minimal-agent 0.12.0 → 0.14.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 (472) hide show
  1. package/README.md +6 -5
  2. package/package.json +62 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
  4. package/plugins/cron-runner/src/commands.js +4 -4
  5. package/plugins/cron-runner/src/commands.ts +4 -4
  6. package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
  7. package/plugins/workflow-runner/commands/workflow.md +57 -90
  8. package/plugins/workflow-runner/commands/workflows.md +4 -4
  9. package/plugins/workflow-runner/plugin.js +24 -13
  10. package/plugins/workflow-runner/plugin.ts +28 -13
  11. package/plugins/workflow-runner/src/discovery.js +160 -0
  12. package/plugins/workflow-runner/src/discovery.ts +194 -0
  13. package/plugins/workflow-runner/src/goalAnchor.js +53 -0
  14. package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
  15. package/plugins/workflow-runner/src/index.js +204 -185
  16. package/plugins/workflow-runner/src/index.ts +249 -224
  17. package/plugins/workflow-runner/src/inputCoercion.js +40 -0
  18. package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
  19. package/plugins/workflow-runner/src/journal.js +224 -0
  20. package/plugins/workflow-runner/src/journal.ts +274 -0
  21. package/plugins/workflow-runner/src/jsonSchema.js +132 -0
  22. package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
  23. package/plugins/workflow-runner/src/moduleResolution.js +64 -0
  24. package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
  25. package/plugins/workflow-runner/src/resourceInputs.js +220 -0
  26. package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
  27. package/plugins/workflow-runner/src/runner.js +404 -467
  28. package/plugins/workflow-runner/src/runner.ts +491 -535
  29. package/plugins/workflow-runner/src/runtime/config.js +46 -0
  30. package/plugins/workflow-runner/src/runtime/config.ts +69 -0
  31. package/plugins/workflow-runner/src/runtime/context.js +1304 -0
  32. package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
  33. package/plugins/workflow-runner/src/runtime/exec.js +34 -0
  34. package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
  35. package/plugins/workflow-runner/src/runtime/flow.js +8 -0
  36. package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
  37. package/plugins/workflow-runner/src/runtime/fs.js +27 -0
  38. package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
  39. package/plugins/workflow-runner/src/runtime/model.js +6 -0
  40. package/plugins/workflow-runner/src/runtime/model.ts +8 -0
  41. package/plugins/workflow-runner/src/runtime/output.js +7 -0
  42. package/plugins/workflow-runner/src/runtime/output.ts +21 -0
  43. package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
  44. package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
  45. package/plugins/workflow-runner/src/runtime/state.js +16 -0
  46. package/plugins/workflow-runner/src/runtime/state.ts +19 -0
  47. package/plugins/workflow-runner/src/runtime/tools.js +3 -0
  48. package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
  49. package/plugins/workflow-runner/src/sandbox.js +141 -0
  50. package/plugins/workflow-runner/src/sandbox.ts +182 -0
  51. package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
  52. package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
  53. package/plugins/workflow-runner/src/taskIdentity.js +172 -152
  54. package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
  55. package/plugins/workflow-runner/src/toolRuntime.js +29 -8
  56. package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
  57. package/plugins/workflow-runner/src/types.js +7 -105
  58. package/plugins/workflow-runner/src/types.ts +155 -520
  59. package/plugins/workflow-runner/src/util.js +47 -0
  60. package/plugins/workflow-runner/src/util.ts +51 -0
  61. package/plugins/workflow-runner/src/workflowContext.js +20 -0
  62. package/plugins/workflow-runner/src/workflowContext.ts +58 -0
  63. package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
  64. package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
  65. package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
  66. package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
  67. package/plugins/workflow-runner/src/workflowState.js +542 -469
  68. package/plugins/workflow-runner/src/workflowState.ts +639 -589
  69. package/plugins/workflow-runner/test/api.test.ts +223 -0
  70. package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
  71. package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
  72. package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
  73. package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
  74. package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
  75. package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
  76. package/plugins/workflow-runner/test/discovery.test.ts +234 -0
  77. package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
  78. package/plugins/workflow-runner/test/goalController.test.ts +317 -0
  79. package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
  80. package/plugins/workflow-runner/test/listResources.test.ts +106 -0
  81. package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
  82. package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
  83. package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
  84. package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
  85. package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
  86. package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
  87. package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
  88. package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
  89. package/skills/pptx/examples/assets/cover-bg.svg +23 -0
  90. package/skills/pptx/examples/assets/icon-spark.svg +5 -0
  91. package/skills/skill-creator/scripts/run_eval.py +3 -2
  92. package/skills/ui-design/SKILL.md +5 -2
  93. package/skills/ui-design/references/layout-composition.md +1 -1
  94. package/skills/workflow-creator/SKILL.md +344 -0
  95. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  96. package/skills/workflow-creator/references/api.md +216 -0
  97. package/skills/workflow-creator/references/migration.md +103 -0
  98. package/skills/workflow-creator/references/patterns.md +779 -0
  99. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  100. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  101. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  102. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  103. package/src/advisor/advisorCoordinator.js +503 -0
  104. package/src/advisor/advisorCoordinator.ts +657 -0
  105. package/src/cli/configMenu.tsx +0 -1
  106. package/src/cli/print.js +44 -6
  107. package/src/cli/print.ts +73 -4
  108. package/src/cli/streamJson.js +5 -6
  109. package/src/cli/streamJson.ts +8 -4
  110. package/src/cli/wizards/advisorWizard.js +4 -2
  111. package/src/cli/wizards/advisorWizard.tsx +8 -2
  112. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  113. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  114. package/src/collaboration/taskFlow.js +115 -0
  115. package/src/collaboration/taskFlow.ts +169 -0
  116. package/src/config/configDefaults.js +41 -1
  117. package/src/config/configDefaults.ts +43 -1
  118. package/src/config/configDiagnostics.ts +2 -2
  119. package/src/config/configFile.js +40 -9
  120. package/src/config/configFile.ts +60 -11
  121. package/src/config/configService.js +443 -74
  122. package/src/config/configService.ts +723 -142
  123. package/src/config/numericLimits.js +22 -0
  124. package/src/config/numericLimits.ts +24 -0
  125. package/src/config.js +1 -0
  126. package/src/config.ts +3 -2
  127. package/src/context/budgetPlan.js +1 -1
  128. package/src/context/budgetPlan.ts +1 -1
  129. package/src/context/compact.js +7 -2
  130. package/src/context/compact.ts +9 -2
  131. package/src/context/microCompactLite.js +212 -13
  132. package/src/context/microCompactLite.ts +261 -12
  133. package/src/context/sessionRegistry.js +8 -1
  134. package/src/context/sessionRegistry.ts +18 -5
  135. package/src/context/toolResultArchive.js +72 -0
  136. package/src/context/toolResultArchive.ts +95 -0
  137. package/src/goal/errors.js +8 -0
  138. package/src/goal/errors.ts +18 -0
  139. package/src/goal/goalRuntime.js +344 -0
  140. package/src/goal/goalRuntime.ts +466 -0
  141. package/src/goal/goalStore.js +200 -0
  142. package/src/goal/goalStore.ts +219 -0
  143. package/src/goal/index.js +4 -0
  144. package/src/goal/index.ts +4 -0
  145. package/src/goal/statusMapping.js +71 -0
  146. package/src/goal/statusMapping.ts +95 -0
  147. package/src/goal/types.js +7 -0
  148. package/src/goal/types.ts +179 -0
  149. package/src/goal/workflowController.js +306 -0
  150. package/src/goal/workflowController.ts +360 -0
  151. package/src/harness/evidenceLedger.js +2 -0
  152. package/src/harness/evidenceLedger.ts +1 -0
  153. package/src/harness/stallGuard.js +9 -3
  154. package/src/harness/stallGuard.ts +7 -3
  155. package/src/images/imageResolver.js +161 -0
  156. package/src/images/imageResolver.ts +189 -0
  157. package/src/infra/verification.js +56 -20
  158. package/src/infra/verification.ts +87 -20
  159. package/src/llm/client.js +79 -21
  160. package/src/llm/client.ts +182 -35
  161. package/src/llm/gatewayClient.js +201 -0
  162. package/src/llm/gatewayClient.ts +299 -0
  163. package/src/llm/messageProvenance.js +46 -0
  164. package/src/llm/messageProvenance.ts +58 -0
  165. package/src/llm/modelProvider.js +1 -0
  166. package/src/llm/modelProvider.ts +22 -0
  167. package/src/llm/providers/anthropicFormat.js +108 -1
  168. package/src/llm/providers/anthropicFormat.ts +115 -4
  169. package/src/llm/providers/openaiFormat.js +9 -0
  170. package/src/llm/providers/openaiFormat.ts +12 -3
  171. package/src/loop.js +319 -54
  172. package/src/loop.ts +373 -57
  173. package/src/main.js +4 -1
  174. package/src/main.tsx +4 -1
  175. package/src/managedExecution/index.js +2 -0
  176. package/src/managedExecution/index.ts +2 -0
  177. package/src/managedExecution/registry.js +15 -0
  178. package/src/managedExecution/registry.ts +20 -0
  179. package/src/managedExecution/types.js +1 -0
  180. package/src/managedExecution/types.ts +86 -0
  181. package/src/modelRef.js +9 -0
  182. package/src/modelRef.ts +31 -0
  183. package/src/multimodal.js +14 -28
  184. package/src/multimodal.ts +17 -33
  185. package/src/net/lanDiscovery.js +65 -0
  186. package/src/net/lanDiscovery.ts +82 -0
  187. package/src/plugin-sdk.js +28 -3
  188. package/src/plugin-sdk.ts +39 -6
  189. package/src/plugins/pluginApi.ts +16 -1
  190. package/src/plugins/pluginLoader.js +4 -0
  191. package/src/plugins/pluginLoader.ts +6 -0
  192. package/src/plugins/pluginRunner.js +152 -9
  193. package/src/plugins/pluginRunner.ts +186 -8
  194. package/src/plugins/slashFallback.js +113 -0
  195. package/src/plugins/slashFallback.ts +137 -0
  196. package/src/prompts/policyRegistry.js +15 -8
  197. package/src/prompts/policyRegistry.ts +18 -8
  198. package/src/prompts/promptAssembly.js +103 -12
  199. package/src/prompts/promptAssembly.ts +114 -12
  200. package/src/prompts/skillList.js +21 -3
  201. package/src/prompts/skillList.ts +27 -2
  202. package/src/prompts/stableSystemPrompt.js +18 -0
  203. package/src/prompts/stableSystemPrompt.ts +33 -0
  204. package/src/runtime/agentRuntimeContext.js +8 -2
  205. package/src/runtime/agentRuntimeContext.ts +17 -4
  206. package/src/session/runAgentTurn.js +78 -12
  207. package/src/session/runAgentTurn.ts +105 -23
  208. package/src/session/runTurn.js +10 -9
  209. package/src/session/runTurn.ts +17 -8
  210. package/src/session/sessionState.ts +2 -0
  211. package/src/team/auth.js +186 -0
  212. package/src/team/auth.ts +298 -0
  213. package/src/team/cli.js +105 -0
  214. package/src/team/cli.ts +122 -0
  215. package/src/team/onlineToolContract.js +64 -0
  216. package/src/team/onlineToolContract.ts +109 -0
  217. package/src/team/toolPolicy.js +36 -0
  218. package/src/team/toolPolicy.ts +75 -0
  219. package/src/tools/advisor/advisor.js +148 -37
  220. package/src/tools/advisor/advisor.ts +161 -38
  221. package/src/tools/advisor/advisorDecision.js +71 -0
  222. package/src/tools/advisor/advisorDecision.ts +85 -0
  223. package/src/tools/agent/agent.js +18 -5
  224. package/src/tools/agent/agent.ts +21 -7
  225. package/src/tools/agent/agentControl.js +246 -0
  226. package/src/tools/agent/agentControl.ts +284 -0
  227. package/src/tools/agent/agentTaskService.js +40 -1
  228. package/src/tools/agent/agentTaskService.ts +44 -1
  229. package/src/tools/agent/agentTypes.js +14 -2
  230. package/src/tools/agent/agentTypes.ts +15 -2
  231. package/src/tools/applypatch/applyPatch.js +362 -0
  232. package/src/tools/applypatch/applyPatch.ts +428 -0
  233. package/src/tools/bash/bash.js +70 -18
  234. package/src/tools/bash/bash.ts +75 -20
  235. package/src/tools/capabilities.js +10 -5
  236. package/src/tools/capabilities.ts +10 -5
  237. package/src/tools/computeruse/computerIntent.js +32 -0
  238. package/src/tools/computeruse/computerIntent.ts +46 -0
  239. package/src/tools/computeruse/telemetryLog.js +12 -0
  240. package/src/tools/computeruse/telemetryLog.ts +16 -0
  241. package/src/tools/computeruse/vision/telemetry.js +1 -0
  242. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  243. package/src/tools/effectiveToolSet.js +4 -0
  244. package/src/tools/effectiveToolSet.ts +4 -0
  245. package/src/tools/index.js +21 -2
  246. package/src/tools/index.ts +33 -2
  247. package/src/tools/powershell/powershell.js +18 -1
  248. package/src/tools/powershell/powershell.ts +18 -2
  249. package/src/tools/presentresult/presentresult.js +6 -15
  250. package/src/tools/presentresult/presentresult.ts +6 -15
  251. package/src/tools/read/read.js +69 -11
  252. package/src/tools/read/read.ts +70 -11
  253. package/src/tools/shared/fileUtils.js +1 -2
  254. package/src/tools/shared/fileUtils.ts +1 -2
  255. package/src/tools/shared/permanentDeleteGuard.js +34 -3
  256. package/src/tools/shared/permanentDeleteGuard.ts +43 -6
  257. package/src/tools/shared/toolOutputStore.js +23 -1
  258. package/src/tools/shared/toolOutputStore.ts +22 -1
  259. package/src/tools/skill/skill.js +9 -3
  260. package/src/tools/skill/skill.ts +8 -4
  261. package/src/tools/toolPromptGuides.js +117 -0
  262. package/src/tools/toolPromptGuides.ts +137 -0
  263. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  264. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  265. package/src/tools/webbrowser/browser.js +27 -11
  266. package/src/tools/webbrowser/browser.ts +68 -24
  267. package/src/tools/webbrowser/browserAutomation.js +329 -0
  268. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  269. package/src/tools/webbrowser/browserAxState.js +97 -0
  270. package/src/tools/webbrowser/browserAxState.ts +142 -0
  271. package/src/tools/webbrowser/browserBatch.js +1578 -0
  272. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  273. package/src/tools/webbrowser/browserCdp.js +208 -0
  274. package/src/tools/webbrowser/browserCdp.ts +255 -0
  275. package/src/tools/webbrowser/browserConditions.js +24 -0
  276. package/src/tools/webbrowser/browserConditions.ts +45 -0
  277. package/src/tools/webbrowser/browserContent.js +281 -0
  278. package/src/tools/webbrowser/browserContent.ts +438 -0
  279. package/src/tools/webbrowser/browserControl.js +131 -30
  280. package/src/tools/webbrowser/browserControl.ts +210 -50
  281. package/src/tools/webbrowser/browserDaemon.js +22 -14
  282. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  283. package/src/tools/webbrowser/browserFrames.js +47 -0
  284. package/src/tools/webbrowser/browserFrames.ts +54 -0
  285. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  286. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  287. package/src/tools/webbrowser/browserLocator.js +140 -0
  288. package/src/tools/webbrowser/browserLocator.ts +194 -0
  289. package/src/tools/webbrowser/browserOutcome.js +10 -17
  290. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  291. package/src/tools/webbrowser/browserRecovery.js +58 -0
  292. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  293. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  294. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  295. package/src/tools/webbrowser/browserService.js +28 -2
  296. package/src/tools/webbrowser/browserService.ts +49 -9
  297. package/src/tools/webbrowser/browserStream.js +132 -33
  298. package/src/tools/webbrowser/browserStream.ts +224 -50
  299. package/src/tools/webbrowser/browserTabs.js +48 -15
  300. package/src/tools/webbrowser/browserTabs.ts +178 -96
  301. package/src/tools/webbrowser/webbrowser.js +683 -144
  302. package/src/tools/webbrowser/webbrowser.ts +979 -236
  303. package/src/tools/webfetch/preapproved.js +0 -2
  304. package/src/tools/webfetch/preapproved.ts +0 -2
  305. package/src/tools/websearch/websearch.js +129 -8
  306. package/src/tools/websearch/websearch.ts +138 -6
  307. package/src/types.ts +450 -42
  308. package/src/ui/App.js +377 -58
  309. package/src/ui/App.tsx +576 -111
  310. package/src/ui/LiveArea.js +14 -8
  311. package/src/ui/LiveArea.tsx +14 -10
  312. package/src/ui/MessageList.js +63 -20
  313. package/src/ui/MessageList.tsx +101 -16
  314. package/src/ui/ModelPicker.js +7 -4
  315. package/src/ui/ModelPicker.tsx +55 -8
  316. package/src/ui/hooks/useChat.js +152 -163
  317. package/src/ui/hooks/useChat.ts +396 -334
  318. package/src/ui/loginCommand.js +56 -0
  319. package/src/ui/loginCommand.ts +63 -0
  320. package/src/ui/toolProgress.js +41 -0
  321. package/src/ui/toolProgress.ts +44 -0
  322. package/src/web/browserBridge.js +20 -2
  323. package/src/web/browserBridge.ts +20 -2
  324. package/src/web/protocol.ts +1897 -194
  325. package/workflows/adaptive-research/workflow.mjs +86 -0
  326. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  327. package/workflows/e2e-llm/workflow.mjs +52 -0
  328. package/workflows/e2e-smoke/workflow.mjs +59 -0
  329. package/workflows/e2e-vision-schema/README.md +48 -0
  330. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  331. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  332. package/workflows/file-census/README.md +14 -0
  333. package/workflows/file-census/scripts/.gitkeep +2 -0
  334. package/workflows/file-census/workflow.mjs +110 -0
  335. package/workflows/goal/README.md +166 -0
  336. package/workflows/goal/prompts/audit.md +57 -0
  337. package/workflows/goal/prompts/contract.md +56 -0
  338. package/workflows/goal/prompts/iterate.md +37 -0
  339. package/workflows/goal/workflow.mjs +501 -0
  340. package/workflows/illustrate/README.md +90 -0
  341. package/workflows/illustrate/assets/engines.json +20 -0
  342. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  343. package/workflows/illustrate/assets/extract-bing.js +33 -0
  344. package/workflows/illustrate/assets/extract-google.js +31 -0
  345. package/workflows/illustrate/workflow.mjs +301 -0
  346. package/workflows/plan/workflow.mjs +389 -0
  347. package/workflows/question-crops/README.md +35 -0
  348. package/workflows/question-crops/prompts/inventory.md +23 -0
  349. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  350. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  351. package/workflows/question-crops/workflow.mjs +172 -0
  352. package/workflows/ralph-loop/workflow.mjs +162 -0
  353. package/workflows/research/DESIGN.md +449 -0
  354. package/workflows/research/README.md +74 -0
  355. package/workflows/research/prompts/s0-goal.md +43 -0
  356. package/workflows/research/prompts/s1-early-exit.md +22 -0
  357. package/workflows/research/prompts/s1-map.md +58 -0
  358. package/workflows/research/prompts/s2-options.md +53 -0
  359. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  360. package/workflows/research/prompts/s4-verify.md +74 -0
  361. package/workflows/research/prompts/s5-synthesis.md +81 -0
  362. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  363. package/workflows/research/prompts/s6-package.md +32 -0
  364. package/workflows/research/scripts/build-report.mjs +98 -0
  365. package/workflows/research/workflow.mjs +288 -0
  366. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  367. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  368. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  369. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  370. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  371. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  372. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  373. package/workflows/wrong-question-docx/README.md +55 -0
  374. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  375. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  376. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  377. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  378. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  379. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  380. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  381. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  382. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  383. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  384. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  385. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  386. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  387. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  388. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  389. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  390. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  391. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  392. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  393. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  394. package/plugins/workflow-runner/src/expressions.js +0 -618
  395. package/plugins/workflow-runner/src/expressions.ts +0 -622
  396. package/plugins/workflow-runner/src/loader.js +0 -573
  397. package/plugins/workflow-runner/src/loader.ts +0 -721
  398. package/plugins/workflow-runner/src/multimodal.js +0 -38
  399. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  400. package/plugins/workflow-runner/src/recovery.js +0 -119
  401. package/plugins/workflow-runner/src/recovery.ts +0 -149
  402. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  403. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  404. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
  405. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
  406. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  407. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  408. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  409. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  410. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  411. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  412. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  413. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  414. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
  415. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
  416. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
  417. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
  418. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  419. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  420. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  421. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  422. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  423. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  424. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
  425. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  426. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  427. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
  428. package/plugins/workflow-runner/test/expressions.test.ts +0 -397
  429. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  430. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  431. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  432. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  433. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  434. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  435. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  436. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  437. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  438. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  439. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
  440. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  441. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  442. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
  443. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  444. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  445. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
  446. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  447. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
  448. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  449. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
  450. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
  451. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  452. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  453. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  454. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  455. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  456. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  457. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  458. package/src/advisorState.js +0 -22
  459. package/src/cli/configWizard.js +0 -293
  460. package/src/tools/toolProgressBridge.js +0 -48
  461. package/workflows/adaptive-research.yaml +0 -50
  462. package/workflows/book-review-short.yaml +0 -125
  463. package/workflows/e2e-write-greet.yaml +0 -31
  464. package/workflows/illustrate.yaml +0 -227
  465. package/workflows/plan.yaml +0 -470
  466. package/workflows/ppt.yaml +0 -1670
  467. package/workflows/ralph-loop.yaml +0 -199
  468. package/workflows/schema.json +0 -176
  469. package/workflows/webppt.yaml +0 -496
  470. package/workflows/wechat-reading-shots.yaml +0 -315
  471. package/workflows/word.yaml +0 -720
  472. package/workflows/youtube-shorts.yaml +0 -202
@@ -1,618 +0,0 @@
1
- /**
2
- * ============================================================
3
- * plugins/workflow-runner/src/expressions.ts —— workflow 表达式引擎(mini)
4
- * ------------------------------------------------------------
5
- * WHY 不直接 eval / Function?
6
- * Workflow 来源不可信(用户编辑器生成 / 别人贡献的 yaml),允许任意
7
- * JS 执行 = 任意远程代码执行。所以我们手写一个**只支持必要语法**的
8
- * 递归下降解析器:常量、变量引用、点路径、字符串/数字字面量、数组、
9
- * 比较、布尔运算、白名单内置函数。
10
- *
11
- * 支持的语法:
12
- * expr := or
13
- * or := and ( '||' and )*
14
- * and := not ( '&&' not )*
15
- * not := '!' not | comparison
16
- * comparison := additive ( ('==' | '!=' | '>=' | '<=' | '>' | '<') additive )?
17
- * additive := primary ( '+' primary )* // number 相加 / 否则 stringify 后拼接
18
- * primary := number | string | array | call | path | interp | '(' expr ')'
19
- * array := '[' expr (',' expr)* ']'
20
- * call := IDENT '(' expr (',' expr)* ')' // 白名单
21
- * path := IDENT ('.' IDENT)* // 变量 + 点路径
22
- * interp := '${' EXPR '}' // 取 EXPR 的"值"(任意类型,不字符串化)
23
- * string := "…" | '…' // 内嵌 ${EXPR} 走 template-literal 插值
24
- *
25
- * 白名单函数:fileExists / readJson / parseJson / asArray / length / lower / upper / includes /
26
- * startsWith / endsWith / match / split / extract / join / json
27
- *
28
- * ★ P2 根治:表达式上下文(when/condition/over/assert/vote.input)直接 evalExpr,
29
- * 不再"先 interpolate 再 eval"(旧双求值的 footgun 源头)。四种写法都 OK:
30
- * 裸 ${x} == 'y' → ${x} 取值后比较(修掉旧 footgun:旧实现把 ${x} 替成裸字面量被当变量名)
31
- * "a/${x}/b" → 字符串字面量内插值拼路径(向后兼容旧 fileExists("…${x}…") 写法)
32
- * "a/" + x + "/b" → + 拼接(新增)
33
- * x == 'y' → 裸变量名(向后兼容)
34
- * 安全性也更好:变量的字符串内容不再被当作表达式二次执行(旧实现若插值结果落在引号外会被 eval)。
35
- *
36
- * 插值函数 interpolate(${EXPR}) 仍用于**模板上下文**(prompt / args / file_path)——
37
- * 把 EXPR 求值后 toString 替换;对象/数组用 JSON.stringify。interpolateDeep 递归处理对象/数组/字符串。
38
- * ============================================================
39
- */
40
- import { existsSync, readFileSync } from 'node:fs';
41
- import { join, isAbsolute } from 'node:path';
42
- import { getWorkingDir } from '../../../src/plugin-sdk.js';
43
- const PUNCT2 = new Set(['==', '!=', '>=', '<=', '&&', '||']);
44
- const PUNCT1 = new Set(['(', ')', '[', ']', ',', '.', '!', '>', '<', '+', '?', ':']);
45
- function tokenize(src) {
46
- const out = [];
47
- let i = 0;
48
- while (i < src.length) {
49
- const c = src[i];
50
- if (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
51
- i++;
52
- continue;
53
- }
54
- // 独立 ${...} 取值 token。注意:字符串字面量内部的 ${} 不会到这里——
55
- // 会被下面的字符串分支整体读进 str token,由 parsePrimary 在 eval 时插值。
56
- if (c === '$' && src[i + 1] === '{') {
57
- let depth = 1;
58
- let j = i + 2;
59
- while (j < src.length && depth > 0) {
60
- if (src[j] === '{')
61
- depth++;
62
- else if (src[j] === '}')
63
- depth--;
64
- if (depth > 0)
65
- j++;
66
- }
67
- if (depth !== 0)
68
- throw new Error(`未闭合的 \${ 在表达式 "${src}"`);
69
- out.push({ kind: 'interp', value: src.slice(i + 2, j) });
70
- i = j + 1;
71
- continue;
72
- }
73
- // 字符串
74
- if (c === '"' || c === "'") {
75
- const quote = c;
76
- let s = '';
77
- i++;
78
- while (i < src.length && src[i] !== quote) {
79
- if (src[i] === '\\' && i + 1 < src.length) {
80
- const nx = src[i + 1];
81
- s += nx === 'n' ? '\n' : nx === 't' ? '\t' : nx;
82
- i += 2;
83
- }
84
- else {
85
- s += src[i++];
86
- }
87
- }
88
- if (src[i] !== quote)
89
- throw new Error(`未闭合的字符串:${src}`);
90
- i++;
91
- out.push({ kind: 'str', value: s });
92
- continue;
93
- }
94
- // 数字
95
- if ((c >= '0' && c <= '9') || (c === '-' && src[i + 1] >= '0' && src[i + 1] <= '9')) {
96
- let j = i;
97
- if (src[j] === '-')
98
- j++;
99
- while (j < src.length && ((src[j] >= '0' && src[j] <= '9') || src[j] === '.'))
100
- j++;
101
- out.push({ kind: 'num', value: parseFloat(src.slice(i, j)) });
102
- i = j;
103
- continue;
104
- }
105
- // 双字符标点
106
- const two = src.slice(i, i + 2);
107
- if (PUNCT2.has(two)) {
108
- out.push({ kind: 'punct', value: two });
109
- i += 2;
110
- continue;
111
- }
112
- // 单字符标点
113
- if (PUNCT1.has(c)) {
114
- out.push({ kind: 'punct', value: c });
115
- i++;
116
- continue;
117
- }
118
- // 标识符
119
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '_') {
120
- let j = i;
121
- while (j < src.length &&
122
- ((src[j] >= 'a' && src[j] <= 'z') ||
123
- (src[j] >= 'A' && src[j] <= 'Z') ||
124
- (src[j] >= '0' && src[j] <= '9') ||
125
- src[j] === '_')) {
126
- j++;
127
- }
128
- out.push({ kind: 'ident', value: src.slice(i, j) });
129
- i = j;
130
- continue;
131
- }
132
- throw new Error(`非法字符 '${c}' 在表达式 "${src}"`);
133
- }
134
- return out;
135
- }
136
- function peek(c) {
137
- return c.pos < c.toks.length ? c.toks[c.pos] : null;
138
- }
139
- function eat(c) {
140
- return c.toks[c.pos++];
141
- }
142
- function isPunct(c, v) {
143
- const t = peek(c);
144
- return !!t && t.kind === 'punct' && t.value === v;
145
- }
146
- const BUILTINS = {
147
- fileExists(args) {
148
- const p = String(args[0] ?? '');
149
- if (!p)
150
- return false;
151
- const abs = isAbsolute(p) ? p : join(getWorkingDir(), p);
152
- return existsSync(abs);
153
- },
154
- length(args) {
155
- const v = args[0];
156
- if (typeof v === 'string' || Array.isArray(v))
157
- return v.length;
158
- if (v && typeof v === 'object')
159
- return Object.keys(v).length;
160
- return 0;
161
- },
162
- // 把可选 JSON 数组标准化为 for-each loop 可消费的数组。用于
163
- // over: "${asArray(readJson('manifest.json', 'images'))}" 这类场景:
164
- // 缺文件/缺字段/null 表示 0 轮;但对象/字符串等结构错误要响亮失败,
165
- // 避免把坏 manifest 静默吞掉。
166
- asArray(args) {
167
- const v = args[0];
168
- if (Array.isArray(v))
169
- return v;
170
- if (v === null || v === undefined)
171
- return [];
172
- throw new Error(`asArray 期望数组/null/undefined,得到 ${Array.isArray(v) ? 'array' : typeof v}`);
173
- },
174
- lower(args) {
175
- return String(args[0] ?? '').toLowerCase();
176
- },
177
- upper(args) {
178
- return String(args[0] ?? '').toUpperCase();
179
- },
180
- includes(args) {
181
- const haystack = String(args[0] ?? '');
182
- const needle = String(args[1] ?? '');
183
- return haystack.includes(needle);
184
- },
185
- startsWith(args) {
186
- const s = String(args[0] ?? '');
187
- const prefix = String(args[1] ?? '');
188
- return s.startsWith(prefix);
189
- },
190
- endsWith(args) {
191
- const s = String(args[0] ?? '');
192
- const suffix = String(args[1] ?? '');
193
- return s.endsWith(suffix);
194
- },
195
- match(args) {
196
- const s = String(args[0] ?? '');
197
- const pattern = String(args[1] ?? '');
198
- try {
199
- const re = new RegExp(pattern);
200
- return re.test(s);
201
- }
202
- catch {
203
- return false; // 非法正则 → 不匹配
204
- }
205
- },
206
- split(args) {
207
- const s = String(args[0] ?? '');
208
- const delim = args[1] !== undefined ? String(args[1]) : ',';
209
- return s.split(delim);
210
- },
211
- // 全局正则提取:返回匹配数组。有捕获组取 group 1(典型:提取 <tag>(.*?)</tag> 内容),
212
- // 否则取全匹配。flags 固定 gs(跨行 . 匹配);非法正则返回 [](与 match 同哲学:不抛)。
213
- // 动机:从 agent/llm 输出里确定性抽取协议标签(如 <learning>…</learning>),
214
- // 替代「再开一个 LLM 步去提取」的幻觉/改写风险。
215
- extract(args) {
216
- const s = String(args[0] ?? '');
217
- const pattern = String(args[1] ?? '');
218
- if (!pattern)
219
- return [];
220
- try {
221
- const re = new RegExp(pattern, 'gs');
222
- const out = [];
223
- for (const m of s.matchAll(re)) {
224
- out.push(m[1] !== undefined ? m[1] : m[0]);
225
- }
226
- return out;
227
- }
228
- catch {
229
- return [];
230
- }
231
- },
232
- // 数组拼接成字符串;非数组按 stringify 兜底。与 extract 配对使用。
233
- join(args) {
234
- const v = args[0];
235
- const sep = args[1] !== undefined ? String(args[1]) : ',';
236
- if (Array.isArray(v))
237
- return v.map((x) => stringify(x)).join(sep);
238
- return stringify(v);
239
- },
240
- // JSON.stringify 安全嵌入:值含引号/换行时拼 JSON 文件不产坏文档
241
- // (模板里写 "k": ${json(v)},外层引号由 json() 自己生成)。
242
- json(args) {
243
- try {
244
- return JSON.stringify(args[0] ?? null);
245
- }
246
- catch {
247
- return 'null';
248
- }
249
- },
250
- // 读磁盘 JSON 文件并取值:path 相对 getWorkingDir()(绝对路径原样);给了 key
251
- // 则返回顶层 obj[key],否则返回整值。缺文件 / 坏 JSON / 键不存在 → null
252
- //(与 match/extract 同「不抛」哲学)。动机:让确定性脚本产出的 manifest
253
- //(如 source_assets.json)直接驱动工作流 for-each / branch —— 填补「磁盘
254
- // 结构化数据 → 控制流」的空白(与 fileExists 平级的基础能力)。
255
- readJson(args) {
256
- const p = String(args[0] ?? '');
257
- if (!p)
258
- return null;
259
- const abs = isAbsolute(p) ? p : join(getWorkingDir(), p);
260
- try {
261
- // 剥前导 BOM:某些工具/重定向写出的 JSON 带 BOM,JSON.parse 会因此抛错。
262
- let raw = readFileSync(abs, 'utf8');
263
- if (raw.charCodeAt(0) === 0xfeff)
264
- raw = raw.slice(1);
265
- const val = JSON.parse(raw);
266
- if (args.length >= 2) {
267
- const key = String(args[1] ?? '');
268
- return val != null && typeof val === 'object' ? (val[key] ?? null) : null;
269
- }
270
- return val;
271
- }
272
- catch {
273
- return null;
274
- }
275
- },
276
- // 解析 JSON 字符串成值(与 readJson 平级,但源是**字符串**而非文件):剥前导 BOM、
277
- // trim、JSON.parse;非字符串 / 空 / 坏 JSON → null(同 match/extract/readJson「不抛」哲学)。
278
- // 动机:把 WebBrowser eval / agent / 工具 capture 到的 JSON 文本变成数组/对象,
279
- // 直接驱动 loop over / branch —— 填补「字符串结构化数据 → 控制流」的空白
280
- //(配 asArray 用:over: "${asArray(parseJson(urls_json))}")。
281
- parseJson(args) {
282
- const v = args[0];
283
- if (typeof v !== 'string')
284
- return null;
285
- let raw = v.charCodeAt(0) === 0xfeff ? v.slice(1) : v;
286
- raw = raw.trim();
287
- if (!raw)
288
- return null;
289
- try {
290
- return JSON.parse(raw);
291
- }
292
- catch {
293
- return null;
294
- }
295
- },
296
- };
297
- function parseOr(c, vars) {
298
- let left = parseAnd(c, vars);
299
- while (isPunct(c, '||')) {
300
- eat(c);
301
- const right = parseAnd(c, vars);
302
- left = truthy(left) || truthy(right);
303
- }
304
- return left;
305
- }
306
- // 三元表达式:cond ? true_expr : false_expr
307
- // 优先级低于 ||(即先算完 || 再判断 ? :)
308
- // 右结合:a ? b ? c : d : e 等价于 a ? (b ? c : d) : e
309
- function parseTernary(c, vars) {
310
- const cond = parseOr(c, vars);
311
- if (!isPunct(c, '?'))
312
- return cond;
313
- eat(c); // 吃掉 '?'
314
- const consequent = parseTernary(c, vars); // 右结合:true 分支可以是嵌套三元
315
- if (!isPunct(c, ':'))
316
- throw new Error("三元表达式缺少 ':'");
317
- eat(c); // 吃掉 ':'
318
- const alternate = parseTernary(c, vars); // 右结合:false 分支也可以是嵌套三元
319
- return truthy(cond) ? consequent : alternate;
320
- }
321
- function parseAnd(c, vars) {
322
- let left = parseNot(c, vars);
323
- while (isPunct(c, '&&')) {
324
- eat(c);
325
- const right = parseNot(c, vars);
326
- left = truthy(left) && truthy(right);
327
- }
328
- return left;
329
- }
330
- function parseNot(c, vars) {
331
- if (isPunct(c, '!')) {
332
- eat(c);
333
- return !truthy(parseNot(c, vars));
334
- }
335
- return parseComparison(c, vars);
336
- }
337
- function parseComparison(c, vars) {
338
- const left = parseAdditive(c, vars);
339
- const t = peek(c);
340
- if (t && t.kind === 'punct' && ['==', '!=', '>=', '<=', '>', '<'].includes(t.value)) {
341
- eat(c);
342
- const right = parseAdditive(c, vars);
343
- switch (t.value) {
344
- case '==':
345
- // eslint-disable-next-line eqeqeq
346
- return left == right;
347
- case '!=':
348
- // eslint-disable-next-line eqeqeq
349
- return left != right;
350
- case '>':
351
- return left > right;
352
- case '<':
353
- return left < right;
354
- case '>=':
355
- return left >= right;
356
- case '<=':
357
- return left <= right;
358
- }
359
- }
360
- return left;
361
- }
362
- // 加法/拼接层:number + number → 相加;否则两边 stringify 后字符串拼接。
363
- // 表达式语言原本无拼接符,导致拼路径只能"先插值再 eval"(footgun 源头);
364
- // 有了 + 之后可写 fileExists("videos/" + inputs.book + "/x") 这种纯表达式。
365
- function parseAdditive(c, vars) {
366
- let left = parsePrimary(c, vars);
367
- while (isPunct(c, '+')) {
368
- eat(c);
369
- const right = parsePrimary(c, vars);
370
- left = addValues(left, right);
371
- }
372
- return left;
373
- }
374
- function addValues(l, r) {
375
- if (typeof l === 'number' && typeof r === 'number')
376
- return l + r;
377
- return stringify(l) + stringify(r);
378
- }
379
- function parsePrimary(c, vars) {
380
- const t = peek(c);
381
- if (!t)
382
- throw new Error('表达式意外结束');
383
- if (t.kind === 'punct' && t.value === '(') {
384
- eat(c);
385
- // 括号内是完整表达式(含三元)——曾只递归 parseOr,(a ? b : c) 报「缺少 ')'」
386
- const v = parseTernary(c, vars);
387
- if (!isPunct(c, ')'))
388
- throw new Error("缺少 ')'");
389
- eat(c);
390
- return v;
391
- }
392
- if (t.kind === 'punct' && t.value === '[') {
393
- eat(c);
394
- const arr = [];
395
- if (!isPunct(c, ']')) {
396
- arr.push(parseTernary(c, vars));
397
- while (isPunct(c, ',')) {
398
- eat(c);
399
- arr.push(parseTernary(c, vars));
400
- }
401
- }
402
- if (!isPunct(c, ']'))
403
- throw new Error("缺少 ']'");
404
- eat(c);
405
- return arr;
406
- }
407
- if (t.kind === 'num') {
408
- eat(c);
409
- return t.value;
410
- }
411
- if (t.kind === 'str') {
412
- eat(c);
413
- // 字符串字面量支持内嵌 ${...} 插值(template-literal 语义):
414
- // fileExists("videos/${inputs.book}/x") 不必预插值也能工作。无 ${} 时原样返回。
415
- return interpolate(t.value, vars);
416
- }
417
- if (t.kind === 'interp') {
418
- eat(c);
419
- // 独立 ${EXPR}:求值为 EXPR 的"值"(任意类型,不字符串化)。
420
- // 修掉"裸 ${x} == 'y' 被当成变量名 x"的 footgun。
421
- return evalExpr(t.value, vars);
422
- }
423
- if (t.kind === 'ident') {
424
- eat(c);
425
- // 函数调用?
426
- if (isPunct(c, '(')) {
427
- eat(c);
428
- const argv = [];
429
- if (!isPunct(c, ')')) {
430
- argv.push(parseTernary(c, vars));
431
- while (isPunct(c, ',')) {
432
- eat(c);
433
- argv.push(parseTernary(c, vars));
434
- }
435
- }
436
- if (!isPunct(c, ')'))
437
- throw new Error("缺少 ')'");
438
- eat(c);
439
- const fn = BUILTINS[t.value];
440
- if (!fn)
441
- throw new Error(`未知函数 "${t.value}"。白名单:${Object.keys(BUILTINS).join(', ')}`);
442
- return fn(argv);
443
- }
444
- // 字面常量
445
- if (t.value === 'true')
446
- return true;
447
- if (t.value === 'false')
448
- return false;
449
- if (t.value === 'null')
450
- return null;
451
- // 变量 + 点路径
452
- let cur = vars.get(t.value);
453
- while (isPunct(c, '.')) {
454
- eat(c);
455
- const next = eat(c);
456
- if (next.kind !== 'ident')
457
- throw new Error('点号后必须是标识符');
458
- // S1:禁止经点路径访问原型链(__proto__ / constructor / prototype),防原型污染 / 函数对象逃逸。
459
- if (next.value === '__proto__' ||
460
- next.value === 'constructor' ||
461
- next.value === 'prototype') {
462
- cur = undefined;
463
- }
464
- else {
465
- cur =
466
- cur != null && typeof cur === 'object'
467
- ? cur[next.value]
468
- : undefined;
469
- }
470
- }
471
- return cur;
472
- }
473
- throw new Error(`无法解析 token: ${JSON.stringify(t)}`);
474
- }
475
- function truthy(v) {
476
- if (v === null || v === undefined)
477
- return false;
478
- if (typeof v === 'boolean')
479
- return v;
480
- if (typeof v === 'number')
481
- return v !== 0 && !Number.isNaN(v);
482
- if (typeof v === 'string')
483
- return v.length > 0;
484
- if (Array.isArray(v))
485
- return v.length > 0;
486
- if (typeof v === 'object')
487
- return Object.keys(v).length > 0;
488
- return true;
489
- }
490
- // ---------------- 对外 API ----------------
491
- /** 计算表达式(裸的,不带 ${} 包裹)。失败抛错。 */
492
- export function evalExpr(expr, vars) {
493
- const toks = tokenize(expr);
494
- if (toks.length === 0)
495
- return undefined;
496
- const cursor = { toks, pos: 0 };
497
- const v = parseTernary(cursor, vars);
498
- if (cursor.pos !== toks.length) {
499
- throw new Error(`表达式多余 token: "${expr}"`);
500
- }
501
- return v;
502
- }
503
- /**
504
- * 字符串插值:把 "${EXPR}" 替换成 EXPR 求值结果。
505
- * 多个 ${} 串联也支持。括号配对识别 —— "${ ${nested} }" 会被当成一个外层
506
- * EXPR(内部 ${nested} 文本原样,但表达式里不允许 ${} 语法,所以会报错)。
507
- *
508
- * 转义:`\$` → 字面 $,跳过插值。
509
- */
510
- export function interpolate(template, vars, opts) {
511
- let out = '';
512
- let i = 0;
513
- while (i < template.length) {
514
- const c = template[i];
515
- if (c === '\\' && template[i + 1] === '$') {
516
- out += '$';
517
- i += 2;
518
- continue;
519
- }
520
- if (c === '$' && template[i + 1] === '{') {
521
- // 找匹配的 '}'
522
- let depth = 1;
523
- let j = i + 2;
524
- while (j < template.length && depth > 0) {
525
- if (template[j] === '{')
526
- depth++;
527
- else if (template[j] === '}')
528
- depth--;
529
- if (depth > 0)
530
- j++;
531
- }
532
- if (depth !== 0) {
533
- // 不匹配;原样输出
534
- out += template.slice(i);
535
- return out;
536
- }
537
- const expr = template.slice(i + 2, j);
538
- const v = evalExpr(expr, vars);
539
- if (v === undefined)
540
- opts?.onMissing?.(expr);
541
- out += stringify(v);
542
- i = j + 1;
543
- continue;
544
- }
545
- out += c;
546
- i++;
547
- }
548
- return out;
549
- }
550
- /**
551
- * 类型保留插值:模板整串恰好是单一 "${EXPR}" 时直接返回 EXPR 的值
552
- * (布尔/数字/数组原样,不字符串化);多片段模板退回普通 interpolate。
553
- * 非字符串值原样透传(与 interpolateDeep 对非字符串的行为一致)。
554
- *
555
- * 动机:set 步写 `flag: "${qualified}"` 曾被字符串化成 "true",逼出下游
556
- * 满篇 `== true || == 'true'` 防御——单表达式赋值就该保住类型。
557
- */
558
- export function interpolateTyped(value, vars, opts) {
559
- if (typeof value !== 'string')
560
- return value;
561
- const expr = singleExprOf(value);
562
- if (expr === null)
563
- return interpolate(value, vars, opts);
564
- const v = evalExpr(expr, vars);
565
- if (v === undefined)
566
- opts?.onMissing?.(expr);
567
- return v;
568
- }
569
- /** 整串恰好是一个 "${EXPR}"(括号配对到串尾)时返回内层 EXPR,否则 null。 */
570
- function singleExprOf(template) {
571
- if (!template.startsWith('${') || !template.endsWith('}'))
572
- return null;
573
- let depth = 1;
574
- for (let j = 2; j < template.length; j++) {
575
- if (template[j] === '{')
576
- depth++;
577
- else if (template[j] === '}') {
578
- depth--;
579
- // 提前归零 = 中途闭合(如 "${a}-${b}"),不是单一表达式
580
- if (depth === 0)
581
- return j === template.length - 1 ? template.slice(2, j) : null;
582
- }
583
- }
584
- return null;
585
- }
586
- function stringify(v) {
587
- if (v === null || v === undefined)
588
- return '';
589
- if (typeof v === 'string')
590
- return v;
591
- if (typeof v === 'number' || typeof v === 'boolean')
592
- return String(v);
593
- try {
594
- return JSON.stringify(v);
595
- }
596
- catch {
597
- return String(v);
598
- }
599
- }
600
- /** 递归对对象/数组/字符串做插值;其它原样返回。 */
601
- export function interpolateDeep(value, vars, opts) {
602
- if (typeof value === 'string') {
603
- return interpolate(value, vars, opts);
604
- }
605
- if (Array.isArray(value)) {
606
- return value.map((v) => interpolateDeep(v, vars, opts));
607
- }
608
- if (value && typeof value === 'object') {
609
- const out = {};
610
- for (const [k, v] of Object.entries(value)) {
611
- // 注意:key 也插值(loop 里 `hook_${idx}` 这种),但只对字符串 key 有意义
612
- const newKey = interpolate(k, vars, opts);
613
- out[newKey] = interpolateDeep(v, vars, opts);
614
- }
615
- return out;
616
- }
617
- return value;
618
- }