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
@@ -15,25 +15,30 @@
15
15
  * ============================================================
16
16
  */
17
17
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
- import { getWorkingDir } from '../../bootstrap/workingDir.js';
18
+ import { stat } from 'node:fs/promises';
19
+ import { getWorkingDir, setWorkingDir } from '../../bootstrap/workingDir.js';
19
20
  import { forceCompact } from '../../context/compact.js';
20
21
  import { clearContext, loadContext, saveContext, } from '../../context/persistContext.js';
21
22
  import { resetReactiveCompactState } from '../../context/reactiveCompact.js';
22
- import { getActiveSession, setActiveSession, } from '../../context/sessionRegistry.js';
23
+ import { acquireContextLock } from '../../context/contextLock.js';
24
+ import { getActiveSession, setActiveSession, updateSessionMetadata, } from '../../context/sessionRegistry.js';
23
25
  import { resolveSessionFile, sessionFileFor, } from '../../context/sessionPath.js';
24
26
  import { buildFullSystemPrompt } from '../../prompts/system.js';
25
- import { ALL_TOOLS } from '../../tools/index.js';
26
- import { setAskHandler, } from '../../askBridge.js';
27
+ import { ALL_TOOLS, getToolByName } from '../../tools/index.js';
28
+ import { createInputQueue } from '../../messageQueue.js';
29
+ import { createAskCoordinator, setAskHandler, } from '../../askBridge.js';
30
+ import { createAskSlot } from './askSlot.js';
31
+ import { figures } from '../figures.js';
27
32
  import { clearFileState } from '../../tools/shared/fileState.js';
28
33
  import { runWithPlugins } from '../../plugins/pluginRunner.js';
29
34
  import { dlog } from '../../utils/debugLog.js';
30
35
  /**
31
36
  * reasoningPreview 环形 buffer 容量(字符)。
32
37
  * LiveArea 现在多行显示思维链尾部(最近 N 行),需要喂饱足够文本;
33
- * 1200 字符约 15~20 行中文,LiveArea wrapToDisplayLines 后再取尾部最近 N 行。
38
+ * 2400 字符约 30~40 行中文,LiveArea wrapToDisplayLines 后再取尾部最近 N 行。
34
39
  * 上界由 LiveArea 的 contentMaxLines 兜底,这里只管"留够素材"。
35
40
  */
36
- const REASONING_PREVIEW_MAX_CHARS = 1200;
41
+ const REASONING_PREVIEW_MAX_CHARS = 2400;
37
42
  /**
38
43
  * streamingText 环形 buffer 容量(字符)—— 仅用于 live 区"答案流式预览"。
39
44
  *
@@ -108,15 +113,37 @@ export function useChat(args) {
108
113
  const historyRef = useRef(args.initialHistory.slice());
109
114
  const [version, setVersion] = useState(0);
110
115
  const bump = useCallback(() => setVersion((v) => v + 1), []);
116
+ // provider 用 ref 存储,支持运行时切换(/model 命令切换基座模型)
117
+ const providerRef = useRef(args.provider);
111
118
  const [streamingText, setStreamingText] = useState('');
112
119
  const [isLoading, setIsLoading] = useState(false);
113
120
  const [error, setError] = useState(null);
121
+ const [transientError, setTransientError] = useState(null);
114
122
  const [interrupted, setInterrupted] = useState(false);
123
+ /**
124
+ * 暂态错误状态机的跨事件可变上下文(用 ref,不需要为它单独重渲染)。
125
+ * - activeStepId:当前正在执行的 workflow step id(workflow_step_start 记下,
126
+ * 该 step 的 workflow_step_end 清除);null = 不在任何 step 内。
127
+ * - pendingLlmError:该 step 执行中捕获到的最近一次 llm_error 文案(暂存,
128
+ * 等 step 结束才决定丢弃 or 提升为永久红字)。
129
+ * handleEvent 通过 setters.workflowCtx 同步读写这个对象(pure 逻辑,测试可注入 stub)。
130
+ */
131
+ const workflowCtxRef = useRef({ activeStepId: null, pendingLlmError: null });
132
+ // 工作流步骤 id → 人话进度名(title)映射:start 记入、end/skipped 回查,
133
+ // 让 scrollback 三种进度行(▶/✓/⊘)都显示同一人话名而非 id。跨事件持 ref。
134
+ const stepTitlesRef = useRef(new Map());
115
135
  const [pluginProgress, setPluginProgress] = useState(null);
116
136
  // ---- 工作过程显示相关 state ----
117
137
  const [compactGeneration, setCompactGeneration] = useState(0);
118
138
  const [progressStage, setProgressStage] = useState('idle');
119
139
  const [reasoningPreview, setReasoningPreview] = useState('');
140
+ /**
141
+ * 内核每轮 yield 的真实上下文用量(含 tools-schema 开销 + 真实 provider usage,
142
+ * 见 loop.ts 的 context_usage 事件)。收到过至少一次前 StatusLine 用它渲染,
143
+ * 比 useTokenUsage 的本地估算更准;未收到过(如刚启动、纯只读问答走了只读快速
144
+ * 路径)时该值仍是 null,StatusLine 降级回 useTokenUsage 估算。
145
+ */
146
+ const [contextUsage, setContextUsage] = useState(null);
120
147
  /**
121
148
  * 工具并行执行:以 callId 为 key 保存所有运行中工具。
122
149
  * 用 Map 是为了 O(1) 增删;对外 memo 物化成稳定数组(Map 迭代顺序 = 插入顺序,
@@ -125,12 +152,28 @@ export function useChat(args) {
125
152
  const [runningToolsMap, setRunningToolsMap] = useState(new Map());
126
153
  const runningTools = useMemo(() => Array.from(runningToolsMap.values()), [runningToolsMap]);
127
154
  const abortRef = useRef(null);
155
+ const [inputQueue] = useState(() => createInputQueue());
156
+ const [queuedCount, setQueuedCount] = useState(0);
157
+ const [queuedItems, setQueuedItems] = useState([]);
158
+ const runningToolsRef = useRef(new Map());
159
+ useEffect(() => {
160
+ return inputQueue.subscribe(() => {
161
+ setQueuedCount(inputQueue.size());
162
+ setQueuedItems(inputQueue.getSnapshot());
163
+ });
164
+ }, [inputQueue]);
128
165
  // ---- AskUserQuestion:执行中向用户提问 ----
129
166
  // pendingAsk 非 null 时 App 渲染 <AskQuestion> 独占输入(不渲染 InputBox)。
130
- // askResolverRef 持当前提问的 resolve/reject —— 用户答完 answerAsk 调 resolve,
131
- // 让 askBridge.requestUserChoice 的 Promise 落定、工具 call() 返回、执行继续。
167
+ // 单槽状态机(resolve/reject 持有、abort 归属校验、悬挂防护)抽在
168
+ // askSlot.ts(纯函数可单测);这里只桥接 React 状态。
132
169
  const [pendingAsk, setPendingAsk] = useState(null);
133
- const askResolverRef = useRef(null);
170
+ // 提问来源:子任务提问时 kind:'subagent' + description → AskQuestion 显示徽标。
171
+ const [pendingAskSource, setPendingAskSource] = useState(null);
172
+ const [pendingAskQueueCount, setPendingAskQueueCount] = useState(0);
173
+ const [askSlot] = useState(() => createAskSlot((questions, source) => {
174
+ setPendingAsk(questions);
175
+ setPendingAskSource(questions ? source ?? null : null);
176
+ }));
134
177
  const streamingFlush = useThrottledFlush({
135
178
  delayMs: STREAM_FLUSH_MS,
136
179
  maxChars: STREAM_FLUSH_MAX_CHARS,
@@ -171,6 +214,9 @@ export function useChat(args) {
171
214
  setProgressStage('idle');
172
215
  setReasoningPreview('');
173
216
  setRunningToolsMap(new Map());
217
+ // 暂态错误是"过程级"提示,turn 结束 / 切换时一并清掉,不残留到下一轮
218
+ setTransientError(null);
219
+ workflowCtxRef.current = { activeStepId: null, pendingLlmError: null };
174
220
  }, []);
175
221
  /** turn 结束 / 中断 / 切换时一并取消所有节流 buffer 的 pending timer。 */
176
222
  const cancelAllThrottles = useCallback(() => {
@@ -195,48 +241,26 @@ export function useChat(args) {
195
241
  };
196
242
  }, []);
197
243
  // mount 时把「弹选择界面」的处理器注册到 askBridge;unmount 注销。
198
- // 工具 isConcurrencySafe:false + workflow 串行 → 同一时刻至多一个 pending,单槽即可。
199
- // signal abort(用户 ESC / 中断)时 reject 并清掉 pending,让工具按「失败/取消」收尾。
244
+ // 两层组合:createAskCoordinator(FIFO 队列——主循环与并行 general 子 Agent 都可能
245
+ // 提问,用户一次只看一个问题,其余排队;实例按 TUI 挂载创建,不同进程互不阻塞)
246
+ // 包住 askSlot.handler(呈现层单槽 + abort 归属校验——同轮多次提问共享 turn signal,
247
+ // 归属校验保证已答完提问的 abort 监听器不会清掉后来提问的槽,否则后者 promise
248
+ // 永不落定 → 整轮死挂,见 askSlot.ts 头注)。
200
249
  useEffect(() => {
201
- setAskHandler((questions, signal) => {
202
- return new Promise((resolve, reject) => {
203
- askResolverRef.current?.reject(new Error('被新的提问取代'));
204
- askResolverRef.current = { resolve, reject };
205
- dlog('ui', 'ask handler INVOKED (等待用户答题)', {
206
- n: questions.length,
207
- headers: questions.map((q) => q.header),
208
- });
209
- setPendingAsk(questions);
210
- const onAbort = () => {
211
- if (!askResolverRef.current)
212
- return;
213
- askResolverRef.current = null;
214
- setPendingAsk(null);
215
- reject(new Error('已被用户中断'));
216
- };
217
- if (signal?.aborted) {
218
- onAbort();
219
- return;
220
- }
221
- signal?.addEventListener('abort', onAbort, { once: true });
222
- });
223
- });
250
+ setAskHandler(createAskCoordinator(askSlot.handler, (status) => {
251
+ setPendingAskQueueCount(status.queuedCount ?? 0);
252
+ }));
224
253
  return () => setAskHandler(null);
225
- }, []);
254
+ }, [askSlot]);
226
255
  /** 用户答完当前提问:清 pending + resolve 工具侧 Promise,执行继续。 */
227
- const answerAsk = useCallback((answers) => {
228
- const r = askResolverRef.current;
229
- askResolverRef.current = null;
230
- setPendingAsk(null);
231
- dlog('ui', 'ask ANSWERED (继续执行)', { selected: answers.map((a) => a.selected) });
232
- r?.resolve(answers);
233
- }, []);
234
- // mount 时异步读 active session(默认 'default',注册表里有就用注册值)。
235
- // 启动时初始 history 已经由 main.tsx/Root 走默认路径加载完了;
236
- // 如果注册表说 active 不是 default,理论上 main.tsx 应该按 active 加载——
237
- // 但当前 main.tsx 用的是 getContextPath() 走 default。为了兼容这个不一致,
238
- // 此处把 ref/state 设为 active id 但不重新 loadContext —— 由用户手动 /session
239
- // 切回正确 session(极少数 edge case,避免启动时异步加载导致界面闪烁)。
256
+ const answerAsk = useCallback((answers) => askSlot.answer(answers), [askSlot]);
257
+ const cancelAsk = useCallback(() => {
258
+ if (pendingAskSource?.kind === 'subagent')
259
+ askSlot.cancel();
260
+ else
261
+ abortRef.current?.abort();
262
+ }, [askSlot, pendingAskSource]);
263
+ // Root 已按 active session 加载初始历史;这里同步同一 active id 给交互状态。
240
264
  useEffect(() => {
241
265
  let cancelled = false;
242
266
  void (async () => {
@@ -256,13 +280,22 @@ export function useChat(args) {
256
280
  };
257
281
  }, []);
258
282
  const submit = useCallback(async (input) => {
259
- if (isLoading)
260
- return;
261
283
  const trimmed = input.trim();
262
284
  if (trimmed.length === 0)
263
285
  return;
286
+ if (isLoading) {
287
+ inputQueue.enqueue(trimmed, 'next');
288
+ const running = Array.from(runningToolsRef.current.values());
289
+ const allCancelable = running.length > 0 && running.every(t => t.isCancelable);
290
+ if (allCancelable) {
291
+ abortRef.current?.abort('interrupt');
292
+ }
293
+ bump();
294
+ return;
295
+ }
264
296
  setIsLoading(true);
265
297
  setError(null);
298
+ setTransientError(null);
266
299
  setInterrupted(false);
267
300
  cancelAllThrottles();
268
301
  setStreamingText('');
@@ -271,14 +304,48 @@ export function useChat(args) {
271
304
  setProgressStage('idle');
272
305
  setReasoningPreview('');
273
306
  setRunningToolsMap(new Map());
307
+ // 重置暂态错误状态机(防上一轮工作流残留)
308
+ workflowCtxRef.current = { activeStepId: null, pendingLlmError: null };
274
309
  const ac = new AbortController();
275
310
  abortRef.current = ac;
311
+ let releaseContextLock = null;
276
312
  dlog('ui', 'turn START', { input: trimmed.slice(0, 80) });
313
+ // 长时间运行时(如工作流 agent 步骤)的定时 token 刷新:
314
+ // runQuery 的 text/reasoning 事件不触发 bump,导致 StatusLine token 数
315
+ // 可能冻结数十秒。此定时器每 10s 强制刷新一次,确保用户看到上下文增长。
316
+ const tokenRefreshTimer = setInterval(() => {
317
+ bump();
318
+ }, 10_000);
277
319
  try {
320
+ await updateSessionMetadata(getWorkingDir(), activeSessionIdRef.current, {
321
+ lastMessagePreview: trimmed,
322
+ }).catch(() => { });
323
+ const activeFile = sessionFileFor(getWorkingDir(), activeSessionIdRef.current);
324
+ releaseContextLock = await acquireContextLock(activeFile, ac.signal);
325
+ // Gateway/Cron may have written this cwd since the TUI rendered its last frame.
326
+ // Reload after acquiring the cross-process lock so this turn extends the newest history.
327
+ const latest = await loadContext(activeFile);
328
+ if (latest) {
329
+ const ephemeral = historyRef.current.filter((message) => message.role === 'system' && message._ephemeral);
330
+ historyRef.current.length = 0;
331
+ historyRef.current.push(...latest, ...ephemeral);
332
+ }
278
333
  for await (const ev of runWithPlugins(trimmed, {
279
- provider: args.provider,
334
+ provider: providerRef.current,
280
335
  history: historyRef.current,
281
336
  signal: ac.signal,
337
+ parseInlineImages: true,
338
+ hasQueuedInput: () => inputQueue.size() > 0,
339
+ onToolStart: (name, id) => {
340
+ const tool = getToolByName(name);
341
+ runningToolsRef.current.set(id, {
342
+ toolName: name,
343
+ isCancelable: tool?.interruptBehavior?.() === 'cancel',
344
+ });
345
+ },
346
+ onToolEnd: (id) => {
347
+ runningToolsRef.current.delete(id);
348
+ },
282
349
  })) {
283
350
  handleEvent(ev, {
284
351
  setStreamingText,
@@ -289,6 +356,12 @@ export function useChat(args) {
289
356
  setProgressStage,
290
357
  setReasoningPreview,
291
358
  setRunningToolsMap,
359
+ setContextUsage,
360
+ // 暂态错误(工作流步内 llm_error / 重试进度)→ LiveArea 黄字
361
+ setTransientError,
362
+ workflowCtx: workflowCtxRef.current,
363
+ // 步骤 id→人话名映射:scrollback 进度行三态(▶/✓/⊘)统一显示 title
364
+ stepTitles: stepTitlesRef.current,
292
365
  // 节流相关 —— buffer 累积 + 100ms timer flush
293
366
  appendStreamingDelta: streamingFlush.append,
294
367
  flushStreamingBuffer: streamingFlush.flush,
@@ -311,16 +384,31 @@ export function useChat(args) {
311
384
  }
312
385
  finally {
313
386
  dlog('ui', 'turn END (finally)');
387
+ clearInterval(tokenRefreshTimer);
314
388
  setIsLoading(false);
315
389
  cancelAllThrottles();
316
390
  resetTurnState();
317
391
  setPluginProgress(null);
318
392
  abortRef.current = null;
393
+ runningToolsRef.current.clear();
319
394
  // v2 multi-session:持久化到当前 active session 文件。
320
- saveContext(historyRef.current, sessionFile()).catch(() => { });
395
+ try {
396
+ await saveContext(historyRef.current, sessionFileFor(getWorkingDir(), activeSessionIdRef.current));
397
+ }
398
+ finally {
399
+ await releaseContextLock?.();
400
+ }
401
+ const consumeQueued = () => {
402
+ const next = inputQueue.dequeue();
403
+ if (!next)
404
+ return;
405
+ queueMicrotask(() => {
406
+ void submit(next.value);
407
+ });
408
+ };
409
+ consumeQueued();
321
410
  }
322
411
  }, [
323
- args.provider,
324
412
  bump,
325
413
  isLoading,
326
414
  sessionFile,
@@ -329,10 +417,17 @@ export function useChat(args) {
329
417
  reasoningFlush,
330
418
  toolBumpFlush,
331
419
  resetTurnState,
420
+ inputQueue,
332
421
  ]);
333
422
  const abort = useCallback(() => {
423
+ inputQueue.clear();
424
+ runningToolsRef.current.clear();
334
425
  abortRef.current?.abort();
426
+ }, [inputQueue]);
427
+ const setProvider = useCallback((next) => {
428
+ providerRef.current = next;
335
429
  }, []);
430
+ const getCurrentProvider = useCallback(() => providerRef.current, []);
336
431
  /**
337
432
  * v2.x 新增:把一段临时提示文本作为 _ephemeral 的 system 消息插入 history。
338
433
  *
@@ -496,6 +591,75 @@ export function useChat(args) {
496
591
  setCompactGeneration((g) => g + 1);
497
592
  bump();
498
593
  }, [bump, isLoading, cancelAllThrottles, resetTurnState]);
594
+ const switchWorkspace = useCallback(async (cwd, sessionId) => {
595
+ if (isLoading)
596
+ return false;
597
+ const targetCwd = cwd.trim();
598
+ const targetSessionId = sessionId.trim();
599
+ if (!targetCwd || !targetSessionId)
600
+ return false;
601
+ let loaded = null;
602
+ let targetExists = false;
603
+ try {
604
+ const targetFile = await resolveSessionFile(targetCwd, targetSessionId);
605
+ try {
606
+ targetExists = (await stat(targetFile)).isFile();
607
+ }
608
+ catch {
609
+ targetExists = false;
610
+ }
611
+ loaded = await loadContext(targetFile);
612
+ if (targetExists && loaded === null)
613
+ throw new Error('目标会话历史损坏或无法读取');
614
+ }
615
+ catch (error) {
616
+ setError(error instanceof Error ? error.message : String(error));
617
+ bump();
618
+ return false;
619
+ }
620
+ let systemPrompt;
621
+ try {
622
+ systemPrompt = await buildFullSystemPrompt(targetCwd, ALL_TOOLS);
623
+ }
624
+ catch (error) {
625
+ setError(`无法打开工作区:${error instanceof Error ? error.message : String(error)}`);
626
+ bump();
627
+ return false;
628
+ }
629
+ const previousCwd = getWorkingDir();
630
+ try {
631
+ await saveContext(historyRef.current, sessionFileFor(previousCwd, activeSessionIdRef.current));
632
+ }
633
+ catch {
634
+ // A damaged old workspace must not trap the user in it.
635
+ }
636
+ try {
637
+ await setActiveSession(targetCwd, targetSessionId);
638
+ }
639
+ catch (error) {
640
+ setError(`无法切换 Session:${error instanceof Error ? error.message : String(error)}`);
641
+ bump();
642
+ return false;
643
+ }
644
+ setWorkingDir(targetCwd);
645
+ activeSessionIdRef.current = targetSessionId;
646
+ setActiveSessionId(targetSessionId);
647
+ historyRef.current.length = 0;
648
+ historyRef.current.push({ role: 'system', content: systemPrompt });
649
+ if (loaded?.length) {
650
+ historyRef.current.push(...(loaded[0]?.role === 'system' ? loaded.slice(1) : loaded));
651
+ }
652
+ clearFileState();
653
+ resetReactiveCompactState();
654
+ cancelAllThrottles();
655
+ setContextUsage(null);
656
+ setError(null);
657
+ setInterrupted(false);
658
+ resetTurnState();
659
+ setCompactGeneration((generation) => generation + 1);
660
+ bump();
661
+ return true;
662
+ }, [bump, isLoading, cancelAllThrottles, resetTurnState]);
499
663
  // history 用 useMemo 托管:version 变化时返回 historyRef.current 的浅拷贝。
500
664
  // 为什么必须 .slice():runQuery 内部 mutate 同一个数组(push 新消息),如果这里
501
665
  // 返回同一引用,Ink <Static items={history}> 浅比较看不到 prop 变化 → 不 commit
@@ -507,21 +671,31 @@ export function useChat(args) {
507
671
  streamingText,
508
672
  isLoading,
509
673
  error,
674
+ transientError,
510
675
  interrupted,
511
676
  pluginProgress,
512
677
  compactGeneration,
513
678
  progressStage,
514
679
  reasoningPreview,
515
680
  runningTools,
681
+ contextUsage,
516
682
  activeSessionId,
683
+ queuedCount,
684
+ queuedItems,
517
685
  submit,
518
686
  abort,
519
687
  clearHistory,
520
688
  compactNow,
521
689
  switchSession,
690
+ switchWorkspace,
522
691
  ephemeralLine,
523
692
  pendingAsk,
693
+ pendingAskSource,
694
+ pendingAskQueueCount,
524
695
  answerAsk,
696
+ cancelAsk,
697
+ setProvider,
698
+ getCurrentProvider,
525
699
  };
526
700
  }
527
701
  export function handleEvent(ev, setters) {
@@ -529,21 +703,73 @@ export function handleEvent(ev, setters) {
529
703
  // 渲染成持久的暗色行落进 scrollback:跑到哪一步、成功/失败/跳过都看得见,
530
704
  // 且出错后仍停留在屏幕上 —— 解决"工作流执行过程不可见、不知道停在哪"。
531
705
  const wf = ev;
706
+ // 据 id 回查人话进度名:声明了 title 的步走 title,否则回落 id(end/skipped 用)。
707
+ const titleOrId = (id) => (id && setters.stepTitles?.get(id)) || id || '?';
532
708
  switch (wf.type) {
533
709
  case 'workflow_start':
534
- setters.emitProgressLine?.(`▶ 工作流 ${wf.name} 开始(共 ${wf.totalSteps} 步)`);
710
+ setters.emitProgressLine?.(`${figures.step} 工作流 ${wf.name} 开始(共 ${wf.totalSteps} 步)`);
711
+ // 新工作流开始:清空上一条工作流残留的 title 映射,避免同名步串味。
712
+ setters.stepTitles?.clear();
713
+ // 工作流开始:立即刷新 token(可能已积累大量前置对话上下文)
714
+ setters.bump();
535
715
  return;
536
716
  case 'workflow_step_start':
537
- setters.emitProgressLine?.(` ▶ ${(wf.index ?? 0) + 1}/${wf.total ?? '?'} ${wf.id} (${wf.kind})`);
717
+ // 声明了 title 的步显示人话名(与状态行一致、不显示块内相对序号——嵌套块
718
+ // 的 i/total 拼在一起会被误读成"跳步");未声明 title 的步保持 legacy 格式。
719
+ // ⎿ 前缀:step 行挂在「▶ 工作流 x 开始」下的层次连接符(figures.elbow 语汇,
720
+ // 与工具结果/蜂群树统一)。ephemeral 是尾部追加消息,不触任何既有前缀字节。
721
+ if (wf.title) {
722
+ if (wf.id)
723
+ setters.stepTitles?.set(wf.id, wf.title);
724
+ setters.emitProgressLine?.(` ${figures.elbow} ${figures.step} ${wf.title}`);
725
+ }
726
+ else {
727
+ setters.emitProgressLine?.(` ${figures.elbow} ${figures.step} ${(wf.index ?? 0) + 1}/${wf.total ?? '?'} ${wf.id} (${wf.kind})`);
728
+ }
729
+ // 暂态错误状态机:记下当前正在执行的 step id,进入"该步内的 llm_error 暂态化"窗口。
730
+ // 进新步先清掉上一步可能残留的暂态错误(上一步已 end、不应跨步显示)。
731
+ if (setters.workflowCtx) {
732
+ setters.workflowCtx.activeStepId = wf.id ?? null;
733
+ setters.workflowCtx.pendingLlmError = null;
734
+ }
735
+ setters.setTransientError?.(null);
538
736
  return;
539
737
  case 'workflow_step_end':
540
- setters.emitProgressLine?.(` ${wf.ok ? '✓' : '✗'} ${wf.id}${wf.error ? `:${wf.error}` : ''}`);
738
+ setters.emitProgressLine?.(` ${figures.elbow} ${wf.ok ? figures.ok : figures.fail} ${titleOrId(wf.id)}${wf.error ? `:${wf.error}` : ''}`);
739
+ // 暂态错误裁决(仅当 end 的是当前活跃步):
740
+ // ok:true → 重试/恢复成功 → 丢弃 pending llm_error,短暂显示"已恢复"后随刷新消失。
741
+ // ok:false → 该步彻底失败 → 把暂态错误提升为永久红字(chat.error,进 scrollback 上方)。
742
+ if (setters.workflowCtx && setters.workflowCtx.activeStepId === wf.id) {
743
+ const pending = setters.workflowCtx.pendingLlmError;
744
+ if (wf.ok) {
745
+ if (pending) {
746
+ // 成功收回:留一条灰底"已恢复"提示,下一次状态行刷新自然覆盖消失
747
+ setters.setTransientError?.(`${figures.ok} ${wf.id} 已恢复(此前 LLM 调用失败已重试成功)`);
748
+ }
749
+ else {
750
+ setters.setTransientError?.(null);
751
+ }
752
+ }
753
+ else {
754
+ // 彻底失败:暂态升永久红字。优先用 step.error,回落到 pending llm_error。
755
+ const finalMsg = wf.error ?? pending;
756
+ if (finalMsg)
757
+ setters.setError(finalMsg);
758
+ setters.setTransientError?.(null);
759
+ }
760
+ setters.workflowCtx.activeStepId = null;
761
+ setters.workflowCtx.pendingLlmError = null;
762
+ }
763
+ // 步骤完成:强制刷新 token(agent/llm 步骤可能写入大量上下文)
764
+ setters.bump();
541
765
  return;
542
766
  case 'workflow_step_skipped':
543
- setters.emitProgressLine?.(` ⊘ ${wf.id}:${wf.reason ?? ''}`);
767
+ setters.emitProgressLine?.(` ${figures.elbow} ${figures.skip} ${titleOrId(wf.id)}:${wf.reason ?? ''}`);
544
768
  return;
545
769
  case 'workflow_done':
546
- setters.emitProgressLine?.(`✓ 工作流 ${wf.name} 完成`);
770
+ setters.emitProgressLine?.(`${figures.ok} 工作流 ${wf.name} 完成`);
771
+ // 工作流结束:最终刷新(反映完整执行后的上下文量)
772
+ setters.bump();
547
773
  return;
548
774
  }
549
775
  // 收口到 LoopEvent:未识别的 PluginEvent type 走 default no-op
@@ -592,6 +818,17 @@ export function handleEvent(ev, setters) {
592
818
  // 压垮 Ink ANSI 重绘。
593
819
  setters.scheduleToolBump();
594
820
  break;
821
+ case 'tool_progress':
822
+ setters.setRunningToolsMap((prev) => {
823
+ const current = prev.get(e.toolCallId);
824
+ if (!current)
825
+ return prev;
826
+ const next = new Map(prev);
827
+ next.set(e.toolCallId, { ...current, progress: e.message, progressDetail: e.detail });
828
+ return next;
829
+ });
830
+ setters.scheduleToolBump();
831
+ break;
595
832
  case 'tool_end':
596
833
  // 仅删除对应 callId 的 entry;其余并行工具继续
597
834
  setters.setRunningToolsMap((prev) => {
@@ -636,22 +873,68 @@ export function handleEvent(ev, setters) {
636
873
  setters.setInterrupted(true);
637
874
  setters.bump();
638
875
  break;
639
- case 'error':
876
+ case 'error': {
877
+ const code = e.code;
878
+ // compact_failed:非致命压缩失败(loop 会继续硬上)。不当红字,作为黄字暂态提示。
879
+ if (code === 'compact_failed') {
880
+ setters.setTransientError?.(`压缩失败(已忽略继续):${e.error}`);
881
+ setters.bump();
882
+ break;
883
+ }
884
+ // 工作流步执行中的 max_turns 是可恢复截断信号:runner 会在共享 history 上续跑。
885
+ // 这里不要落入永久红字错误区;顶层非工作流 max_turns 仍保持原有错误展示。
886
+ if (code === 'max_turns' &&
887
+ setters.workflowCtx &&
888
+ setters.workflowCtx.activeStepId !== null &&
889
+ setters.setTransientError) {
890
+ setters.setTransientError('↻ 当前步骤达到轮数预算,正在续跑…');
891
+ setters.bump();
892
+ break;
893
+ }
894
+ // 工作流步执行中的 llm_error:暂态而非永久红字 ——
895
+ // 该步通常会被重试,这里只暂存 + 喂 LiveArea 黄字,等 step_end 裁决去留。
896
+ // 非工作流场景(无活跃 step,或缺 setTransientError 降级)走原有永久红字路径。
897
+ if (code === 'llm_error' &&
898
+ setters.workflowCtx &&
899
+ setters.workflowCtx.activeStepId !== null &&
900
+ setters.setTransientError) {
901
+ setters.workflowCtx.pendingLlmError = e.error;
902
+ setters.setTransientError(`⚠ LLM 调用失败(重试中):${e.error}`);
903
+ setters.bump();
904
+ break;
905
+ }
906
+ // 其余:顶层 halt(无 code)、非工作流 llm_error、aborted 等 → 永久红字。
640
907
  setters.setError(e.error);
641
908
  setters.bump();
642
909
  break;
643
- case 'warning':
644
- // 通用非致命警告。TUI 不把它当红色 error 渲染;workflow 这类插件已另发
645
- // plugin_progress 把同一文案镜像到状态行(LiveArea)。这里显式 no-op 让它
646
- // 成为"已识别事件",不触发 default 分支对未知事件的 dev 告警(见 default)。
910
+ }
911
+ case 'warning': {
912
+ // 通用非致命警告。workflow 这类插件已另发 plugin_progress 把同文案镜像到状态行。
913
+ // 这里额外把"重试 N/M"类警告聚合到 LiveArea 黄色暂态行(覆盖刷新,不 append),
914
+ // 让用户一眼看出"正在第 N/M 次重试"而非"卡住"。其余 warning 仍 no-op。
915
+ const msg = e.message;
916
+ if (setters.setTransientError &&
917
+ typeof msg === 'string' &&
918
+ msg.includes('重试')) {
919
+ setters.setTransientError(`↻ ${msg}`);
920
+ setters.bump();
921
+ }
647
922
  break;
923
+ }
648
924
  case 'plugin_progress':
649
925
  setters.setPluginProgress({
650
926
  pluginId: e.pluginId,
651
927
  current: e.current,
652
- max: e.max ?? 0,
928
+ max: e.max,
653
929
  message: e.message,
654
930
  });
931
+ // 重试进度也镜像到暂态行(覆盖刷新):runner 在重试时发 "↻ 重试 x N/M…"。
932
+ // 与 warning 分支互为补充(plugin_progress 文案更短,适合状态行下方一行)。
933
+ if (setters.setTransientError &&
934
+ typeof e.message === 'string' &&
935
+ e.message.includes('↻ 重试')) {
936
+ setters.setTransientError(e.message);
937
+ }
655
938
  setters.bump();
656
939
  break;
657
940
  case 'reasoning':
@@ -666,6 +949,17 @@ export function handleEvent(ev, setters) {
666
949
  // 工具集合的清空时机是各自 tool_end
667
950
  setters.setProgressStage(e.stage);
668
951
  break;
952
+ case 'context_usage':
953
+ // 内核每轮算出的真实用量(含 tools-schema 开销 + 真实 provider usage)——
954
+ // 比 StatusLine 本地 useTokenUsage 估算更准,落地后 StatusLine 优先用它。
955
+ setters.setContextUsage?.({
956
+ usedTokens: e.usedTokens,
957
+ contextWindow: e.contextWindow,
958
+ remainingTokens: e.remainingTokens,
959
+ compactThreshold: e.compactThreshold,
960
+ source: e.source,
961
+ });
962
+ break;
669
963
  default:
670
964
  // 落到这里 = 插件私有事件(PluginEvent,type 任意字符串),TUI 不识别即忽略。
671
965
  // 这里**故意不**写 console.warn —— Ink 渲染期间写 stdout/stderr 会撕裂终端画面。
@@ -20,7 +20,7 @@
20
20
  * 导致只有最后一个换行之前的内容被保留,或者直接触发了发送。
21
21
  * ============================================================
22
22
  */
23
- import { useCallback, useRef, useState } from 'react';
23
+ import { useCallback, useEffect, useRef, useState } from 'react';
24
24
  // 粘贴检测阈值:超过此长度视为粘贴输入
25
25
  const PASTE_THRESHOLD = 20;
26
26
  // 多 Chunk 等待超时:100ms 内收到的内容视为同一批粘贴
@@ -58,6 +58,16 @@ export function usePasteHandler({ onPasteComplete, multiline = false, }) {
58
58
  chunks: [],
59
59
  timeoutId: null,
60
60
  });
61
+ // 卸载清理:粘贴中途组件被切走(SessionPicker/ConfigMenu/AskQuestion 顶替)时
62
+ // 取消挂起的 flush 定时器,防止回调打到已卸载组件。
63
+ useEffect(() => {
64
+ return () => {
65
+ if (pasteStateRef.current.timeoutId) {
66
+ clearTimeout(pasteStateRef.current.timeoutId);
67
+ pasteStateRef.current.timeoutId = null;
68
+ }
69
+ };
70
+ }, []);
61
71
  /**
62
72
  * 刷新 UI 的引用(避免闭包问题)
63
73
  */
@@ -8,14 +8,13 @@
8
8
  * ============================================================
9
9
  */
10
10
  import { useCallback, useMemo, useState } from 'react';
11
- import { bufClear, bufDeleteAt, bufDeleteBefore, bufDeleteWordBefore, bufInsert, bufKillToLineEnd, bufKillToLineStart, bufMoveBufferEnd, bufMoveBufferStart, bufMoveDown, bufMoveLeft, bufMoveLineEnd, bufMoveLineStart, bufMoveRight, bufMoveUp, bufMoveWordLeft, bufMoveWordRight, bufSetText, EMPTY_BUFFER, layoutBuffer, } from '../textBuffer.js';
11
+ import { bufClear, bufDeleteBefore, bufDeleteWordBefore, bufInsert, bufKillToLineEnd, bufKillToLineStart, bufMoveBufferEnd, bufMoveBufferStart, bufMoveDown, bufMoveLeft, bufMoveLineEnd, bufMoveLineStart, bufMoveRight, bufMoveUp, bufMoveWordLeft, bufMoveWordRight, bufSetText, EMPTY_BUFFER, layoutBuffer, } from '../textBuffer.js';
12
12
  export function useTextBuffer() {
13
13
  const [state, setState] = useState(EMPTY_BUFFER);
14
14
  const insert = useCallback((s) => setState((p) => bufInsert(p, s)), []);
15
15
  const setText = useCallback((s) => setState(bufSetText(s)), []);
16
16
  const clear = useCallback(() => setState(bufClear()), []);
17
17
  const deleteBefore = useCallback(() => setState(bufDeleteBefore), []);
18
- const deleteAt = useCallback(() => setState(bufDeleteAt), []);
19
18
  const deleteWordBefore = useCallback(() => setState(bufDeleteWordBefore), []);
20
19
  const killToLineStart = useCallback(() => setState(bufKillToLineStart), []);
21
20
  const killToLineEnd = useCallback(() => setState(bufKillToLineEnd), []);
@@ -37,7 +36,6 @@ export function useTextBuffer() {
37
36
  setText,
38
37
  clear,
39
38
  deleteBefore,
40
- deleteAt,
41
39
  deleteWordBefore,
42
40
  killToLineStart,
43
41
  killToLineEnd,