minimal-agent 0.8.2 → 0.10.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 (484) hide show
  1. package/README.md +14 -12
  2. package/package.json +34 -1
  3. package/plugins/cron-runner/.claude-plugin/plugin.json +5 -0
  4. package/plugins/cron-runner/commands/cron.md +26 -0
  5. package/plugins/cron-runner/commands/crons.md +7 -0
  6. package/plugins/cron-runner/plugin.js +34 -0
  7. package/plugins/cron-runner/src/commands.js +348 -0
  8. package/plugins/cron-runner/src/cronTime.js +148 -0
  9. package/plugins/cron-runner/src/lock.js +68 -0
  10. package/plugins/cron-runner/src/runs.js +89 -0
  11. package/plugins/cron-runner/src/scheduleEngine.js +72 -0
  12. package/plugins/cron-runner/src/scheduler.js +355 -0
  13. package/plugins/cron-runner/src/schema.js +140 -0
  14. package/plugins/cron-runner/src/service.js +167 -0
  15. package/plugins/cron-runner/src/store.js +298 -0
  16. package/plugins/cron-runner/src/types.js +1 -0
  17. package/plugins/workflow-runner/commands/workflow.md +4 -4
  18. package/plugins/workflow-runner/commands/workflows.md +1 -1
  19. package/plugins/workflow-runner/plugin.js +3 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +249 -51
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +198 -15
  23. package/plugins/workflow-runner/src/index.js +40 -8
  24. package/plugins/workflow-runner/src/jsonPayload.js +1 -1
  25. package/plugins/workflow-runner/src/loader.js +286 -72
  26. package/plugins/workflow-runner/src/multimodal.js +37 -0
  27. package/plugins/workflow-runner/src/recovery.js +49 -39
  28. package/plugins/workflow-runner/src/runner.js +802 -287
  29. package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
  30. package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
  31. package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
  32. package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
  33. package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
  34. package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
  35. package/plugins/workflow-runner/src/taskIdentity.js +296 -0
  36. package/plugins/workflow-runner/src/types.js +19 -9
  37. package/plugins/workflow-runner/src/workflowState.js +516 -33
  38. package/skills/adversarial-review/SKILL.md +0 -1
  39. package/skills/ai-image/SKILL.md +89 -0
  40. package/skills/ai-image/scripts/generate.mjs +497 -0
  41. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  42. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  43. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  44. package/skills/browser-automation/SKILL.md +47 -0
  45. package/skills/docx/SKILL.md +25 -3
  46. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  53. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  54. package/skills/docx/scripts/opc_check.py +253 -0
  55. package/skills/image-gen-openrouter/SKILL.md +10 -2
  56. package/skills/image-stats/SKILL.md +65 -0
  57. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  58. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  59. package/skills/image-stats/scripts/image_stats.py +368 -0
  60. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  61. package/skills/pptx/SKILL.md +123 -95
  62. package/skills/pptx/design.md +82 -0
  63. package/skills/pptx/examples/README.md +29 -0
  64. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  65. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  66. package/skills/pptx/pptxgenjs.md +41 -0
  67. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  68. package/skills/pptx/scripts/layout_lint.py +293 -0
  69. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  70. package/skills/pptx/scripts/opc_check.py +339 -0
  71. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  72. package/skills/pptx/svg.md +67 -0
  73. package/skills/tts-zh/SKILL.md +19 -2
  74. package/skills/ui-design/SKILL.md +158 -0
  75. package/skills/ui-design/claude_design.md +309 -0
  76. package/skills/ui-design/evals/evals.json +254 -0
  77. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  78. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  79. package/skills/ui-design/references/design-context.md +53 -0
  80. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  81. package/skills/ui-design/references/export-pptx.md +124 -0
  82. package/skills/ui-design/references/layout-composition.md +654 -0
  83. package/skills/ui-design/references/preview-feedback.md +37 -0
  84. package/skills/ui-design/references/source-image-layout.md +189 -0
  85. package/skills/ui-design/references/starter-components.md +78 -0
  86. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  87. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  88. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  89. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  90. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  91. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  92. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  93. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  94. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  95. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  96. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  97. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  98. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  99. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  100. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  140. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  141. package/skills/ui-design/scripts/assemble_material.py +217 -0
  142. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  143. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  144. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  145. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  146. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  147. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  148. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  149. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  150. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  151. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  152. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  153. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  154. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  155. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  156. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  157. package/skills/ui-design/scripts/design_style_check.py +397 -0
  158. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  159. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  160. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  161. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  162. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  163. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  164. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  165. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  166. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  167. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  168. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  169. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  170. package/skills/ui-design/scripts/layout_balance.py +881 -0
  171. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  172. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  173. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  174. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  175. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  176. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  177. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  178. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  179. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  180. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  181. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  182. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  183. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  184. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  185. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  186. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  187. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  188. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  195. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  196. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  205. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  206. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  221. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  222. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  225. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  226. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  239. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  240. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  248. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  249. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  261. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  262. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  275. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  292. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  293. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  316. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  324. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  325. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  328. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  329. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  337. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  338. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  339. package/skills/video-compose/SKILL.md +8 -1
  340. package/skills/web-crawler/SKILL.md +70 -0
  341. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  342. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  343. package/skills/xlsx/SKILL.md +12 -0
  344. package/skills/xlsx/scripts/opc_check.py +253 -0
  345. package/src/advisorState.js +22 -0
  346. package/src/askBridge.js +97 -3
  347. package/src/bootstrap/workingDir.js +12 -1
  348. package/src/cli/args.js +17 -1
  349. package/src/cli/configMenu.js +236 -0
  350. package/src/cli/configWizard.js +7 -5
  351. package/src/cli/print.js +37 -4
  352. package/src/cli/streamJson.js +77 -24
  353. package/src/cli/wizards/advisorWizard.js +147 -0
  354. package/src/cli/wizards/fieldForm.js +149 -0
  355. package/src/cli/wizards/imageWizard.js +60 -0
  356. package/src/cli/wizards/openRouterWizard.js +23 -0
  357. package/src/cli/wizards/providerPresets.js +95 -0
  358. package/src/cli/wizards/providerWizard.js +219 -0
  359. package/src/cli/wizards/searchWizard.js +23 -0
  360. package/src/cli/wizards/ttsWizard.js +34 -0
  361. package/src/config/configFile.js +441 -83
  362. package/src/config/configService.js +446 -0
  363. package/src/config.js +24 -64
  364. package/src/context/agentHome.js +9 -0
  365. package/src/context/archive.js +7 -2
  366. package/src/context/compact.js +8 -5
  367. package/src/context/contextLock.js +81 -0
  368. package/src/context/persistContext.js +30 -11
  369. package/src/context/reactiveCompact.js +4 -2
  370. package/src/context/recentDirs.js +21 -61
  371. package/src/context/sessionPath.js +97 -23
  372. package/src/context/sessionRegistry.js +303 -24
  373. package/src/context/tokenCounter.js +18 -0
  374. package/src/context/workspaceIndex.js +731 -0
  375. package/src/infra/verification.js +28 -15
  376. package/src/llm/client.js +394 -159
  377. package/src/llm/providers/anthropicFormat.js +241 -0
  378. package/src/llm/providers/openaiFormat.js +190 -0
  379. package/src/loop.js +219 -32
  380. package/src/main.js +61 -11
  381. package/src/messageQueue.js +97 -0
  382. package/src/multimodal.js +203 -0
  383. package/src/plugin-sdk.js +15 -0
  384. package/src/plugins/commandRouter.js +3 -0
  385. package/src/plugins/hookEngine.js +5 -1
  386. package/src/plugins/pluginLifecycle.js +28 -0
  387. package/src/plugins/pluginLoader.js +2 -1
  388. package/src/plugins/pluginRunner.js +49 -21
  389. package/src/plugins/transcript.js +4 -3
  390. package/src/prompts/pluginList.js +5 -5
  391. package/src/prompts/projectInstructions.js +3 -3
  392. package/src/prompts/promptAssembly.js +133 -144
  393. package/src/prompts/skillList.js +12 -22
  394. package/src/prompts/system.js +0 -1
  395. package/src/session/runAgentTurn.js +70 -0
  396. package/src/session/runTurn.js +41 -6
  397. package/src/toolGate.js +17 -0
  398. package/src/tools/advisor/advisor.js +379 -0
  399. package/src/tools/agent/agent.js +555 -0
  400. package/src/tools/agent/agentTypes.js +59 -0
  401. package/src/tools/agent/coordinator.js +93 -0
  402. package/src/tools/agent/worktree.js +270 -0
  403. package/src/tools/ask/ask.js +7 -2
  404. package/src/tools/bash/bash.js +112 -18
  405. package/src/tools/download/download.js +144 -0
  406. package/src/tools/edit/edit.js +1 -0
  407. package/src/tools/edit/multi-edit.js +1 -0
  408. package/src/tools/genimage/genimage.js +242 -0
  409. package/src/tools/genspeech/genspeech.js +67 -0
  410. package/src/tools/glob/glob.js +1 -0
  411. package/src/tools/grep/grep.js +3 -0
  412. package/src/tools/grep/rgPath.js +2 -0
  413. package/src/tools/index.js +25 -2
  414. package/src/tools/read/read.js +1 -0
  415. package/src/tools/shared/killTree.js +52 -0
  416. package/src/tools/toolProgressBridge.js +48 -0
  417. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  418. package/src/tools/webbrowser/browser.js +59 -42
  419. package/src/tools/webbrowser/browserControl.js +118 -0
  420. package/src/tools/webbrowser/browserDaemon.js +97 -0
  421. package/src/tools/webbrowser/browserService.js +27 -0
  422. package/src/tools/webbrowser/browserStream.js +119 -0
  423. package/src/tools/webbrowser/webbrowser.js +699 -130
  424. package/src/tools/webfetch/webfetch.js +1 -0
  425. package/src/tools/websearch/websearch.js +134 -51
  426. package/src/tools/write/write.js +1 -0
  427. package/src/ui/App.js +249 -22
  428. package/src/ui/AskQuestion.js +29 -10
  429. package/src/ui/InputBox.js +104 -12
  430. package/src/ui/LiveArea.js +112 -19
  431. package/src/ui/MessageList.js +100 -14
  432. package/src/ui/ModelPicker.js +56 -0
  433. package/src/ui/QueuedMessagesBanner.js +14 -0
  434. package/src/ui/Root.js +18 -10
  435. package/src/ui/SessionPicker.js +102 -0
  436. package/src/ui/StatusLine.js +13 -4
  437. package/src/ui/WorkspacePicker.js +55 -0
  438. package/src/ui/figures.js +44 -0
  439. package/src/ui/hooks/askSlot.js +82 -0
  440. package/src/ui/hooks/useChat.js +358 -64
  441. package/src/ui/hooks/usePasteHandler.js +11 -1
  442. package/src/ui/hooks/useTextBuffer.js +1 -3
  443. package/src/ui/liveViewport.js +5 -1
  444. package/src/ui/markdown.js +206 -0
  445. package/src/ui/textBuffer.js +0 -7
  446. package/src/ui/theme.js +32 -0
  447. package/src/utils/debugLog.js +0 -4
  448. package/src/utils/resourcePaths.js +2 -2
  449. package/src/utils/stripThink.js +50 -0
  450. package/src/web/browserBridge.js +447 -0
  451. package/src/web/protocol.js +19 -0
  452. package/workflows/adaptive-research.yaml +13 -5
  453. package/workflows/book-review-short.yaml +36 -10
  454. package/workflows/e2e-write-greet.yaml +6 -2
  455. package/workflows/illustrate.yaml +227 -0
  456. package/workflows/plan.yaml +460 -179
  457. package/workflows/ppt.yaml +1659 -0
  458. package/workflows/ralph-loop.yaml +199 -0
  459. package/workflows/schema.json +81 -23
  460. package/workflows/webppt.yaml +496 -0
  461. package/workflows/word.yaml +710 -0
  462. package/workflows/youtube-shorts.yaml +46 -15
  463. package/MINIMAL-TRACE.md +0 -77
  464. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
  465. package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
  466. package/plugins/ralph-wiggum/README.md +0 -197
  467. package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
  468. package/plugins/ralph-wiggum/commands/help.md +0 -137
  469. package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
  470. package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
  471. package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
  472. package/plugins/ralph-wiggum/plugin.js +0 -280
  473. package/plugins/ralph-wiggum/src/goalState.js +0 -258
  474. package/plugins/ralph-wiggum/src/sentinels.js +0 -76
  475. package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
  476. package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
  477. package/plugins/workflow-runner/src/miniReAct.js +0 -223
  478. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  479. package/skills/config/SKILL.md +0 -123
  480. package/skills/frontend-design/LICENSE.txt +0 -177
  481. package/skills/frontend-design/SKILL.md +0 -42
  482. package/src/ui/ProgressPanel.js +0 -54
  483. package/src/ui/StreamingBlock.js +0 -11
  484. package/src/ui/ToolStatus.js +0 -11
package/src/loop.js CHANGED
@@ -27,19 +27,31 @@
27
27
  * ============================================================
28
28
  */
29
29
  import crypto from 'node:crypto';
30
- import { autoCompactIfNeeded } from './context/compact.js';
30
+ import { autoCompactIfNeeded, getCompactThreshold } from './context/compact.js';
31
31
  import { microCompact, incrementTurn, expireOldEntries } from './context/microCompactLite.js';
32
32
  import { isPromptTooLongError, reactiveCompactIfApplicable, } from './context/reactiveCompact.js';
33
33
  import { snipCompactIfNeeded } from './context/snipCompact.js';
34
34
  import { countMessagesTokens, estimateToolsTokens } from './context/tokenCounter.js';
35
35
  import { chat as defaultChat } from './llm/client.js';
36
+ import { buildUserContent } from './multimodal.js';
36
37
  import { ALL_TOOLS, executeTool as defaultExecuteTool, getToolByName } from './tools/index.js';
38
+ import { configService } from './config/configService.js';
37
39
  /**
38
- * 工具 schema 的估算 token 数(ALL_TOOLS 固定,模块加载时算一次即可)。
40
+ * 工具 schema 的估算 token 数(ALL_TOOLS 固定,首次使用时算一次并缓存)。
39
41
  * 每轮 LLM 请求都会带上整套工具 schema,但 countMessagesTokens 只数历史消息、
40
42
  * 漏了这部分 → 系统性低估。autoCompact 判定阈值时加上它才准。
43
+ *
44
+ * ⚠️ 必须懒求值而非模块顶层求值:Agent 工具引入了 tools/index → agent → loop →
45
+ * tools/index 的模块环,若从 tools/index 一侧先进入,顶层求值 ALL_TOOLS 时它还在
46
+ * TDZ(临时死区),启动即 ReferenceError。函数体内访问时模块图早已初始化完毕。
41
47
  */
42
- const TOOLS_SCHEMA_TOKENS = estimateToolsTokens(ALL_TOOLS);
48
+ let toolsSchemaTokensCache = null;
49
+ function getToolsSchemaTokens() {
50
+ if (toolsSchemaTokensCache === null) {
51
+ toolsSchemaTokensCache = estimateToolsTokens(ALL_TOOLS);
52
+ }
53
+ return toolsSchemaTokensCache;
54
+ }
43
55
  /**
44
56
  * 执行一次"用户输入 → 模型回答完成"的完整流程。
45
57
  *
@@ -51,11 +63,50 @@ export async function* runQuery(userInput, options) {
51
63
  // DI fallback:测试可传 stub,生产走真实模块单例
52
64
  const chatFn = options.chat ?? defaultChat;
53
65
  const executeToolFn = options.executeTool ?? defaultExecuteTool;
66
+ const subagents = await configService.subagents();
67
+ const toolCallContext = {
68
+ history,
69
+ advisorCalls: { used: 0, max: 1 },
70
+ advisorInvocation: 'agent',
71
+ // Agent 等派生工具起嵌套子查询所需的运行态:与主循环同一 provider、
72
+ // 同一审批闸门(子 agent 不绕过宿主策略)。agentCalls 与 advisorCalls 同构,
73
+ // 限同一回合内的子任务派生总数,防失控 fan-out。
74
+ provider,
75
+ toolGate: options.toolGate,
76
+ agentCalls: { used: 0, max: subagents.enabled ? subagents.maxCallsPerTurn : 0 },
77
+ subagentConfig: subagents,
78
+ browserRun: options.browserRun,
79
+ };
80
+ // 实际下发给模型的工具集:allowedTools 未传时给全量;
81
+ // 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
82
+ // 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
83
+ const activeTools = options.allowedTools !== undefined
84
+ ? ALL_TOOLS.filter((t) => options.allowedTools.includes(t.name))
85
+ : ALL_TOOLS.filter((tool) => tool.name !== 'Agent' || subagents.enabled);
86
+ let userContent;
87
+ try {
88
+ userContent = await buildUserContent({
89
+ text: userInput,
90
+ images: options.imageInputs,
91
+ parseInlineImages: options.parseInlineImages,
92
+ detail: options.imageDetail,
93
+ maxLongSidePixel: options.imageMaxLongSidePixel,
94
+ });
95
+ }
96
+ catch (e) {
97
+ yield {
98
+ type: 'error',
99
+ error: `图片输入处理失败:${e.message}`,
100
+ code: 'llm_error',
101
+ };
102
+ return;
103
+ }
54
104
  // 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
55
105
  // 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
56
106
  history.push({
57
107
  role: 'user',
58
- content: userInput,
108
+ content: userContent,
109
+ ...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
59
110
  timestamp: Date.now(),
60
111
  id: crypto.randomUUID(),
61
112
  });
@@ -70,20 +121,29 @@ export async function* runQuery(userInput, options) {
70
121
  // 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
71
122
  let lastPromptTokens;
72
123
  let turn = 0;
124
+ let workStarted = false;
73
125
  while (turn < maxTurns) {
74
126
  turn++;
75
127
  incrementTurn(sessionState?.microCompact);
76
128
  expireOldEntries(sessionState?.microCompact);
77
129
  if (signal?.aborted) {
78
- // 中断标记帮助模型理解上下文
79
- history.push({
80
- role: 'user',
81
- content: '(用户按下了 ESC/Ctrl+C 中断了任务)',
82
- timestamp: Date.now(),
83
- id: crypto.randomUUID(),
84
- });
85
- // code='aborted':让 `-p --output-format json` 把终结原因映射成 stop_reason='interrupted'
86
- yield { type: 'error', error: '已被用户中断', code: 'aborted' };
130
+ pushManualInterruptMarker(history);
131
+ if (!workStarted) {
132
+ yield { type: 'error', error: '用户中断了任务', code: 'aborted' };
133
+ }
134
+ else {
135
+ yield { type: 'interrupted', reason: 'manual' };
136
+ }
137
+ return;
138
+ }
139
+ if (options.hasQueuedInput?.()) {
140
+ pushQueuedInputMarker(history);
141
+ if (!workStarted) {
142
+ yield { type: 'error', error: '收到新输入,取消当前任务', code: 'aborted' };
143
+ }
144
+ else {
145
+ yield { type: 'interrupted', reason: 'queued_input' };
146
+ }
87
147
  return;
88
148
  }
89
149
  // 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
@@ -91,7 +151,8 @@ export async function* runQuery(userInput, options) {
91
151
  try {
92
152
  const compact = await autoCompactIfNeeded(history, provider, {
93
153
  actualPromptTokens: lastPromptTokens,
94
- toolsTokens: TOOLS_SCHEMA_TOKENS,
154
+ toolsTokens: getToolsSchemaTokens(),
155
+ signal,
95
156
  });
96
157
  if (compact.compacted) {
97
158
  yield { type: 'compact_start' };
@@ -149,11 +210,18 @@ export async function* runQuery(userInput, options) {
149
210
  let lastFinishReason = 'unknown';
150
211
  // 进入 LLM 流前:等首 token / reasoning,本质是 thinking
151
212
  yield { type: 'stage_change', stage: 'thinking' };
213
+ workStarted = true;
152
214
  try {
215
+ const modelMessages = options.transientContext?.trim()
216
+ ? [
217
+ { role: 'system', content: options.transientContext.trim() },
218
+ ...history,
219
+ ]
220
+ : history;
153
221
  for await (const ev of chatFn({
154
222
  provider,
155
- messages: history,
156
- tools: ALL_TOOLS,
223
+ messages: modelMessages,
224
+ tools: activeTools,
157
225
  signal,
158
226
  })) {
159
227
  if (ev.type === 'text_delta') {
@@ -192,14 +260,14 @@ export async function* runQuery(userInput, options) {
192
260
  }
193
261
  catch (e) {
194
262
  if (e.name === 'AbortError') {
195
- // 用户主动中断:记录标记消息,不报红色错误
196
- history.push({
197
- role: 'user',
198
- content: '(用户按下了 ESC/Ctrl+C 中断了任务)',
199
- timestamp: Date.now(),
200
- id: crypto.randomUUID(),
201
- });
202
- yield { type: 'interrupted' };
263
+ if (options.hasQueuedInput?.()) {
264
+ pushQueuedInputMarker(history);
265
+ yield { type: 'interrupted', reason: 'queued_input' };
266
+ }
267
+ else {
268
+ pushManualInterruptMarker(history);
269
+ yield { type: 'interrupted', reason: 'manual' };
270
+ }
203
271
  return;
204
272
  }
205
273
  // prompt_too_long 反应式自救:压缩历史 → 同 turn 重发 LLM
@@ -209,7 +277,7 @@ export async function* runQuery(userInput, options) {
209
277
  reactiveCount++;
210
278
  yield { type: 'compact_start' };
211
279
  yield { type: 'stage_change', stage: 'compacting' };
212
- const result = await reactiveCompactIfApplicable(history, provider, e, sessionState?.reactive);
280
+ const result = await reactiveCompactIfApplicable(history, provider, e, sessionState?.reactive, signal);
213
281
  // 仅当确实压下去了(after < before)才重试;否则(没压动 / 未恢复)退回错误路径,杜绝死循环。
214
282
  const shrank = (result.after ?? 0) < (result.before ?? 0);
215
283
  if (result.recovered && shrank) {
@@ -236,6 +304,17 @@ export async function* runQuery(userInput, options) {
236
304
  };
237
305
  return;
238
306
  }
307
+ if (lastFinishReason === 'aborted' || signal?.aborted || options.hasQueuedInput?.()) {
308
+ if (options.hasQueuedInput?.() && !signal?.aborted) {
309
+ pushQueuedInputMarker(history);
310
+ yield { type: 'interrupted', reason: 'queued_input' };
311
+ }
312
+ else {
313
+ pushManualInterruptMarker(history);
314
+ yield { type: 'interrupted', reason: 'manual' };
315
+ }
316
+ return;
317
+ }
239
318
  // S2 防御:OpenAI 协议保证 tool_call 的 index 连续;万一某 provider 跳号产生稀疏数组,
240
319
  // 这里压实掉空洞,避免后续遍历 / 回填遇到 undefined(assistantMsg.tool_calls 与执行都用它)。
241
320
  {
@@ -269,6 +348,16 @@ export async function* runQuery(userInput, options) {
269
348
  '可调大模型的 max_tokens 上限,或让模型分段输出。',
270
349
  };
271
350
  }
351
+ const estimatedTokens = countMessagesTokens(history) + getToolsSchemaTokens();
352
+ const usedTokens = Math.min(provider.contextWindow, Math.max(lastPromptTokens ?? 0, estimatedTokens));
353
+ yield {
354
+ type: 'context_usage',
355
+ usedTokens,
356
+ contextWindow: provider.contextWindow,
357
+ remainingTokens: Math.max(0, provider.contextWindow - usedTokens),
358
+ compactThreshold: getCompactThreshold(provider),
359
+ source: lastPromptTokens === undefined ? 'estimated' : 'actual',
360
+ };
272
361
  yield { type: 'turn_done' };
273
362
  return;
274
363
  }
@@ -284,13 +373,34 @@ export async function* runQuery(userInput, options) {
284
373
  // (OpenAI 协议要求 tool 消息与上一条 assistant 的 tool_calls 一一对应)。
285
374
  yield { type: 'stage_change', stage: 'tool_executing' };
286
375
  // 执行单个 tool call → WorkerOutcome(内部吞掉 abort/异常,永不 reject)。
287
- const runOne = async (tc) => {
376
+ const runOne = async (tc, reportProgress) => {
288
377
  try {
289
- const result = await executeToolFn(tc.function.name, tc.function.arguments, signal);
378
+ // 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
379
+ // tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
380
+ const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
381
+ if (options.toolGate) {
382
+ let decision;
383
+ try {
384
+ decision = await options.toolGate({ name: tc.function.name, argsJson: tc.function.arguments, isReadOnly, toolCallId: tc.id }, signal);
385
+ }
386
+ catch {
387
+ decision = { allow: false, reason: '工具执行审批失败(宿主异常)' };
388
+ }
389
+ if (!decision.allow) {
390
+ return { tc, result: { ok: false, error: decision.reason } };
391
+ }
392
+ }
393
+ options.onToolStart?.(tc.function.name, tc.id);
394
+ // 浅拷贝填入本次调用的 toolCallId:进度按调用实例路由(并行同名工具不串扰)。
395
+ // advisorCalls/agentCalls 是共享对象引用,计数仍全轮共享,不受拷贝影响。
396
+ const result = await executeToolFn(tc.function.name, tc.function.arguments, signal, { ...toolCallContext, toolCallId: tc.id, reportProgress });
397
+ options.onToolEnd?.(tc.id, false);
290
398
  return { tc, result };
291
399
  }
292
400
  catch (e) {
293
- if (e.name === 'AbortError') {
401
+ const wasAborted = e.name === 'AbortError';
402
+ options.onToolEnd?.(tc.id, wasAborted);
403
+ if (wasAborted) {
294
404
  return { tc, result: { ok: false, error: 'aborted' } };
295
405
  }
296
406
  return { tc, result: { ok: false, error: e.message } };
@@ -313,6 +423,7 @@ export async function* runQuery(userInput, options) {
313
423
  : o.result.error === 'aborted'
314
424
  ? '(已中断)'
315
425
  : `Error: ${o.result.error}`,
426
+ metadata: o.result.metadata,
316
427
  });
317
428
  const allConcurrencySafe = toolCallsByIndex.every((tc) => {
318
429
  const tool = getToolByName(tc.function.name);
@@ -330,7 +441,13 @@ export async function* runQuery(userInput, options) {
330
441
  };
331
442
  const workers = toolCallsByIndex.map(async (tc) => {
332
443
  enqueue(startEvent(tc));
333
- const o = await runOne(tc);
444
+ const o = await runOne(tc, (message, detail) => enqueue({
445
+ type: 'tool_progress',
446
+ toolCallId: tc.id,
447
+ toolName: tc.function.name,
448
+ message,
449
+ detail,
450
+ }));
334
451
  enqueue(endEvent(o));
335
452
  return o;
336
453
  });
@@ -357,7 +474,32 @@ export async function* runQuery(userInput, options) {
357
474
  settled = [];
358
475
  for (const tc of toolCallsByIndex) {
359
476
  yield startEvent(tc);
360
- const o = await runOne(tc);
477
+ const queue = [];
478
+ let wake = null;
479
+ let finished = false;
480
+ const worker = runOne(tc, (message, detail) => {
481
+ queue.push({
482
+ type: 'tool_progress',
483
+ toolCallId: tc.id,
484
+ toolName: tc.function.name,
485
+ message,
486
+ detail,
487
+ });
488
+ wake?.();
489
+ wake = null;
490
+ }).finally(() => {
491
+ finished = true;
492
+ wake?.();
493
+ wake = null;
494
+ });
495
+ while (!finished || queue.length > 0) {
496
+ while (queue.length > 0)
497
+ yield queue.shift();
498
+ if (finished)
499
+ break;
500
+ await new Promise((resolve) => { wake = resolve; });
501
+ }
502
+ const o = await worker;
361
503
  yield endEvent(o);
362
504
  settled.push(o);
363
505
  }
@@ -376,13 +518,25 @@ export async function* runQuery(userInput, options) {
376
518
  role: 'tool',
377
519
  content,
378
520
  tool_call_id: tc.id,
521
+ ...(result.metadata ? { display_metadata: result.metadata } : {}),
379
522
  timestamp: Date.now(),
380
523
  id: crypto.randomUUID(),
381
524
  });
382
525
  }
383
526
  if (anyAborted || signal?.aborted) {
384
- // 工具被中断时,外层 yield interrupted 并 return
385
- yield { type: 'interrupted' };
527
+ if (options.hasQueuedInput?.()) {
528
+ pushQueuedInputMarker(history);
529
+ yield { type: 'interrupted', reason: 'queued_input' };
530
+ }
531
+ else {
532
+ pushManualInterruptMarker(history);
533
+ yield { type: 'interrupted', reason: 'manual' };
534
+ }
535
+ return;
536
+ }
537
+ if (options.hasQueuedInput?.()) {
538
+ pushQueuedInputMarker(history);
539
+ yield { type: 'interrupted', reason: 'queued_input' };
386
540
  return;
387
541
  }
388
542
  // 6. 继续 while 让模型看到 tool_result 后继续推理
@@ -395,6 +549,35 @@ export async function* runQuery(userInput, options) {
395
549
  };
396
550
  }
397
551
  // ---------------- 辅助函数 ----------------
552
+ /**
553
+ * 用户按ESC手动中断
554
+ */
555
+ function pushManualInterruptMarker(history) {
556
+ history.push({
557
+ role: 'user',
558
+ content: '(用户按下了 ESC/Ctrl+C 中断了任务。等待用户新指令。)',
559
+ timestamp: Date.now(),
560
+ id: crypto.randomUUID(),
561
+ });
562
+ }
563
+ /**
564
+ * 有新的用户输入在队列中,当前安全停靠
565
+ */
566
+ function pushQueuedInputMarker(history) {
567
+ history.push({
568
+ role: 'user',
569
+ content: [
570
+ '(系统提示:用户在工作过程中发送了新的消息。',
571
+ '你现在看到的是上一轮工作在被打断前已完成的部分。',
572
+ '请阅读用户的新消息,判断:',
573
+ '1. 如果是不相关的新任务:简要确认已收到,转向新任务,可在新任务完成后询问是否回到之前工作',
574
+ '2. 如果是补充信息或前置依赖:把新信息纳入考虑,判断是否需要调整已做的工作,然后继续',
575
+ '3. 如果是改变方向或取消:评估已完成的部分,说明你对变更的理解,按新方向推进)',
576
+ ].join('\n'),
577
+ timestamp: Date.now(),
578
+ id: crypto.randomUUID(),
579
+ });
580
+ }
398
581
  /** 把一条 tool_call_delta 合并到累积数组上 */
399
582
  function mergeToolCallDelta(acc, ev) {
400
583
  let slot = acc[ev.index];
@@ -463,6 +646,10 @@ function friendlyToolDescription(toolName, rawArgsJson) {
463
646
  }
464
647
  }
465
648
  case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
649
+ case 'Agent': {
650
+ const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';
651
+ return `子任务[${t}] ${truncate(args.description, 50)}`;
652
+ }
466
653
  default: return `${toolName}(...)`;
467
654
  }
468
655
  }
package/src/main.js CHANGED
@@ -19,9 +19,12 @@ const pkg = (() => {
19
19
  })();
20
20
  import { extractCwdArg } from './bootstrap/cwdArg.js';
21
21
  import { initWorkingDir, getWorkingDir } from './bootstrap/workingDir.js';
22
+ import { ensureAgentBrowserOnPath } from './tools/webbrowser/agentBrowserPath.js';
22
23
  import { applyToolKeysToEnv, loadProviderLayered } from './config.js';
23
24
  import { clearContext, loadContext } from './context/persistContext.js';
24
- import { migrateLegacyContext } from './context/sessionPath.js';
25
+ import { validateSessionId } from './context/sessionRegistry.js';
26
+ import { workspaceIndexService } from './context/workspaceIndex.js';
27
+ import { migrateLegacyContext, sessionFileFor } from './context/sessionPath.js';
25
28
  import { buildFullSystemPrompt } from './prompts/system.js';
26
29
  import { ALL_TOOLS } from './tools/index.js';
27
30
  import { Root } from './ui/Root.js';
@@ -60,12 +63,25 @@ async function main() {
60
63
  console.log(`minimal-agent v${pkg.version}`);
61
64
  return;
62
65
  }
66
+ if (args.includes('--workspace-doctor')) {
67
+ const snapshot = await workspaceIndexService.snapshot();
68
+ console.log(`Workspace Index v${snapshot.version} · revision ${snapshot.revision}`);
69
+ for (const workspace of snapshot.workspaces) {
70
+ console.log(`${workspace.available ? '✓' : '○'} ${workspace.displayPath} · ${workspace.sessions.length} sessions`);
71
+ }
72
+ console.log(`可恢复/已过滤历史:${snapshot.recoverable.length}`);
73
+ return;
74
+ }
63
75
  // --clear:清空当前工作目录的会话(归档 + 清空)后退出。供 webchat 的 /new 调用。
64
76
  // 无需 provider;上面 -d/--cwd 已 chdir + initWorkingDir,clearContext() 作用于该目录。
65
77
  if (args.includes('--clear')) {
66
78
  await clearContext();
67
79
  return;
68
80
  }
81
+ // 内置 agent 浏览器:把已 vendored/绿色版同级的 agent-browser 目录前置进 PATH,
82
+ // 让 Bash 工具 / skill 脚本 / 工作流 bash 步也能直接调(tools/skills/工作流共用底座)。
83
+ // dev/npm 无 vendored 二进制时 no-op;不污染 stdout,-p json 契约安全。
84
+ await ensureAgentBrowserOnPath();
69
85
  const isPrintMode = args.includes('-p') || args.includes('--print');
70
86
  if (isPrintMode) {
71
87
  // 先把输出格式解析出来——provider 缺失分支也要按 json 契约给信号。
@@ -81,6 +97,20 @@ async function main() {
81
97
  const rawMaxTurns = extractFlagValue(args, ['--max-turns']);
82
98
  const parsedMaxTurns = rawMaxTurns !== undefined ? Number.parseInt(rawMaxTurns, 10) : Number.NaN;
83
99
  const maxTurns = Number.isNaN(parsedMaxTurns) ? undefined : parsedMaxTurns;
100
+ // -s/--session <id>:选定同 cwd 下的哪个 session;缺省 'default'(向后兼容,
101
+ // 逐字节保持旧行为)。读、写必须用同一份解析结果——不能只让加载感知这个 flag,
102
+ // 落盘时各自重新计算,否则出现"从 session A 读、写回 default"的读写分裂。
103
+ let sessionId;
104
+ try {
105
+ sessionId = validateSessionId(extractFlagValue(args, ['-s', '--session']) ?? 'default');
106
+ }
107
+ catch (error) {
108
+ console.error(`无效的 --session:${error.message}`);
109
+ process.exitCode = 2;
110
+ return;
111
+ }
112
+ await workspaceIndexService.register(getWorkingDir()).catch(() => { });
113
+ const sessionFile = sessionFileFor(getWorkingDir(), sessionId);
84
114
  // CLI 非交互模式:先 env,再 ~/.minimal-agent/config.json fallback;都没就退出
85
115
  // (宿主 spawn 子进程时通常没 export env,靠 TUI 向导写出的 config.json 持久化)
86
116
  const provider = await loadProviderLayered();
@@ -104,24 +134,29 @@ async function main() {
104
134
  process.stderr.write(`\n未找到 provider 配置。\n\n` +
105
135
  `请二选一:\n` +
106
136
  ` 1. 设置环境变量 MINIMAL_AGENT_BASE_URL / MINIMAL_AGENT_API_KEY / MINIMAL_AGENT_MODEL\n` +
107
- ` 2. 先直接运行 \`minimal-agent\`(TUI)完成首次配置向导;向导会写出 ~/.minimal-agent/config.json,之后 -p 模式自动复用\n\n`);
137
+ ` 2. 先直接运行 \`minimal-agent\`(TUI)在首次配置菜单里配好基座模型;菜单会写出 ~/.minimal-agent/config.json,之后 -p 模式自动复用\n\n`);
108
138
  process.exit(1);
109
139
  }
110
- const initialHistory = await buildInitialHistory();
140
+ const initialHistory = await buildInitialHistory(sessionFile);
111
141
  const verbose = args.includes('--verbose') || args.includes('-v');
112
142
  // stream-json 走独立流式模式(webchat broker / 网关用);text/json 仍走原 runPrintMode(契约不变)。
113
143
  if (outputFormat === 'stream-json') {
114
144
  await runStreamJsonMode(provider, args, initialHistory, {
115
145
  maxTurns,
116
146
  compact: args.includes('--compact'),
147
+ sessionFile,
148
+ workspace: { cwd: getWorkingDir(), sessionId },
117
149
  });
118
150
  return;
119
151
  }
120
- await runPrintMode(provider, args, initialHistory, { verbose, outputFormat, maxTurns });
152
+ await runPrintMode(provider, args, initialHistory, {
153
+ verbose, outputFormat, maxTurns, sessionFile,
154
+ workspace: { cwd: getWorkingDir(), sessionId },
155
+ });
121
156
  return;
122
157
  }
123
- // TUI 交互模式:env 不全则 fallback 到 ~/.minimal-agent/config.json;
124
- // 都没有则 Root 渲染配置向导。
158
+ // TUI 交互模式:优先读 JSON 配置(home → exe 同级),没有再用 env 兜底;
159
+ // 都没有则 Root 渲染首次配置菜单。
125
160
  const initialProvider = await loadProviderLayered();
126
161
  const { waitUntilExit } = render(_jsx(Root, { initialProvider: initialProvider }));
127
162
  await waitUntilExit();
@@ -133,10 +168,14 @@ async function main() {
133
168
  * 总是用 fresh system prompt(即便 last-context.json 里也存了一份),
134
169
  * 这样换 cwd 或工具后也不会沿用错的环境上下文。
135
170
  */
136
- async function buildInitialHistory() {
171
+ /**
172
+ * @param sessionFile 显式会话文件路径(`-p`/stream-json 模式下由 `--session` 解析而来);
173
+ * 缺省时走 `loadContext()` 的默认路径(= default session,向后兼容)。
174
+ */
175
+ async function buildInitialHistory(sessionFile) {
137
176
  const content = await buildFullSystemPrompt(getWorkingDir(), ALL_TOOLS);
138
177
  const fresh = { role: 'system', content };
139
- const persisted = await loadContext();
178
+ const persisted = await loadContext(sessionFile);
140
179
  if (!persisted || persisted.length === 0)
141
180
  return [fresh];
142
181
  const rest = persisted[0]?.role === 'system' ? persisted.slice(1) : persisted;
@@ -155,24 +194,35 @@ minimal-agent - 轻量级 AI 编程助手
155
194
  -v, --verbose 显示详细输出(工具调用、压缩信息)
156
195
  -d, --cwd <dir> 指定工作目录(不存在自动创建);启动时 chdir 到这里,
157
196
  上下文文件、工具相对路径、.env 加载都以此为基准
197
+ -s, --session <id> 选定同一工作目录下的哪个会话(缺省 'default');配合 TUI 的
198
+ /session /sessions /resume 是同一套内核会话,互不冲突
158
199
  --output-format <fmt> text(默认)= 纯文本答案;json = 一行结局契约
159
200
  (ok / result / stop_reason / num_turns / error);
160
201
  stream-json = 逐行 LoopEvent(webchat / 网关 broker 用)
161
202
  --max-turns <n> 最大工具循环轮数(防失控;缺省 50)
203
+ --image <path|url> 附加图片(可重复;JPEG/PNG/GIF/WEBP,本地单图≤10MB)
204
+ --image-detail <d> 图片细节级别:low / default / high(默认 default)
205
+ --image-max-long-side-pixel <n>
206
+ OpenAI 兼容图片参数:限制最长边像素
162
207
  --clear 清空当前工作目录的会话(归档 + 清空)后退出
163
208
  --compact 与 -p --output-format stream-json 连用:压缩当前会话
209
+ --workspace-doctor 只读诊断全局工作区索引、Session 数量和孤立历史
164
210
  -h, --help 显示帮助信息
165
211
 
166
212
  会话记忆:
167
- 按当前工作目录隔离,自动加载 / 保存到
168
- ~/.minimal-agent/sessions/<目录哈希>.json
169
- TUI 与 -p 共享同一目录的上下文;TUI 中输入 /new 仅清当前目录的会话
213
+ 按当前工作目录隔离,同一工作目录下可以有多个会话(session),
214
+ 自动加载 / 保存到 ~/.minimal-agent/sessions/<目录哈希>--<sessionId>.json
215
+ (不传 --session 时用 'default')。TUI 与 -p 共享同一套会话;
216
+ TUI 里用 /session /sessions /resume 列出并切换会话、/session new [标题] 新建;
217
+ -p 用 -s/--session <id> 选定;TUI 中输入 /new 仅清当前会话(不影响其它会话)
170
218
 
171
219
  示例:
172
220
  minimal-agent -p "帮我写一个 hello world"
173
221
  echo "解释代码" | minimal-agent -p
174
222
  minimal-agent -p --verbose "运行测试并报告结果"
175
223
  minimal-agent -p "处理资料" -d /tmp/job-123 # 工作目录隔离
224
+ minimal-agent -p --image ./ui.png "评价这个界面"
225
+ minimal-agent -p "@/tmp/a.png 请识别图片内容" # TUI / -p 均支持 @图片路径
176
226
  minimal-agent -p --output-format json "做点事" # 宿主程序 spawn:一行 JSON 结局
177
227
  `);
178
228
  }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * ============================================================
3
+ * src/messageQueue.ts —— 用户输入队列(支持优先级)
4
+ * ------------------------------------------------------------
5
+ * 实现对话进行中用户插话的排队管理。
6
+ *
7
+ * 设计原则:
8
+ * - 极简:只保留 minimal-agent 需要的核心功能
9
+ * - 发布订阅:subscribe() 订阅队列变化,用于UI更新
10
+ * - 工厂函数:createInputQueue() 创建独立实例,支持多session/测试
11
+ * - 两级优先级:'now'(立即处理)> 'next'(排队等待)
12
+ * ============================================================
13
+ */
14
+ function randomUUID() {
15
+ if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
16
+ return crypto.randomUUID();
17
+ }
18
+ return `q_${Date.now()}_${Math.random().toString(36).slice(2)}`;
19
+ }
20
+ export function createInputQueue() {
21
+ const queue = [];
22
+ const subscribers = new Set();
23
+ function notify() {
24
+ for (const cb of subscribers) {
25
+ cb();
26
+ }
27
+ }
28
+ function findBestIndex() {
29
+ let bestIdx = 0;
30
+ let bestPrio = 99;
31
+ for (let i = 0; i < queue.length; i++) {
32
+ const p = queue[i].priority === 'now' ? 0 : 1;
33
+ if (p < bestPrio) {
34
+ bestPrio = p;
35
+ bestIdx = i;
36
+ }
37
+ }
38
+ return bestIdx;
39
+ }
40
+ return {
41
+ enqueue(value, priority = 'next') {
42
+ queue.push({
43
+ id: randomUUID(),
44
+ value,
45
+ priority,
46
+ timestamp: Date.now(),
47
+ });
48
+ notify();
49
+ },
50
+ dequeue() {
51
+ if (queue.length === 0)
52
+ return undefined;
53
+ const bestIdx = findBestIndex();
54
+ const item = queue.splice(bestIdx, 1)[0];
55
+ if (item)
56
+ notify();
57
+ return item;
58
+ },
59
+ dequeueAll() {
60
+ if (queue.length === 0)
61
+ return [];
62
+ const sorted = [...queue].sort((a, b) => {
63
+ const pa = a.priority === 'now' ? 0 : 1;
64
+ const pb = b.priority === 'now' ? 0 : 1;
65
+ if (pa !== pb)
66
+ return pa - pb;
67
+ return a.timestamp - b.timestamp;
68
+ });
69
+ queue.length = 0;
70
+ notify();
71
+ return sorted;
72
+ },
73
+ peek() {
74
+ if (queue.length === 0)
75
+ return undefined;
76
+ return queue[findBestIndex()];
77
+ },
78
+ clear() {
79
+ if (queue.length === 0)
80
+ return;
81
+ queue.length = 0;
82
+ notify();
83
+ },
84
+ size() {
85
+ return queue.length;
86
+ },
87
+ subscribe(callback) {
88
+ subscribers.add(callback);
89
+ return () => {
90
+ subscribers.delete(callback);
91
+ };
92
+ },
93
+ getSnapshot() {
94
+ return Object.freeze([...queue]);
95
+ },
96
+ };
97
+ }