agentworkshop 0.8.8 → 0.9.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 (589) hide show
  1. package/.AgentWorkShop/prompts/host-tools.json +68 -0
  2. package/.output/.AgentWorkShop/prompts/host-tools.json +68 -0
  3. package/.output/.AgentWorkShop/schema.json +2 -0
  4. package/.output/nitro.json +1 -1
  5. package/.output/public/_nuxt/{Cdi9qfTr.js → CDO7Zey1.js} +2 -2
  6. package/.output/public/_nuxt/builds/latest.json +1 -1
  7. package/.output/public/_nuxt/builds/meta/f507c33b-4ecc-4e32-af88-2e3736b443e9.json +1 -0
  8. package/.output/server/chunks/_/access.mjs +1 -1
  9. package/.output/server/chunks/_/data-export.mjs +5 -5
  10. package/.output/server/chunks/_/data-query.mjs +2 -2
  11. package/.output/server/chunks/_/eda-run.repo.mjs +3 -3
  12. package/.output/server/chunks/_/features.mjs +5 -5
  13. package/.output/server/chunks/_/golden.mjs +2 -2
  14. package/.output/server/chunks/_/host-bindings.mjs +1 -1
  15. package/.output/server/chunks/_/index.mjs +2 -2
  16. package/.output/server/chunks/_/mes-secrets.repo.mjs +1 -1
  17. package/.output/server/chunks/_/nitro.mjs +2556 -1314
  18. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  19. package/.output/server/chunks/_/spc.mjs +2 -2
  20. package/.output/server/chunks/build/_id_-CsCtnbwe.mjs +1 -1
  21. package/.output/server/chunks/build/_id_-P1W47Gqq.mjs +1 -1
  22. package/.output/server/chunks/build/_wsId_-DpOErFj6.mjs +1 -1
  23. package/.output/server/chunks/build/agents-D7CdEUTv.mjs +1 -1
  24. package/.output/server/chunks/build/aml-DRAFRcGU.mjs +1 -1
  25. package/.output/server/chunks/build/analysis-J9axLuRt.mjs +1 -1
  26. package/.output/server/chunks/build/api-B4Om0mEc.mjs +1 -1
  27. package/.output/server/chunks/build/api-BEW_ebY_.mjs +1 -1
  28. package/.output/server/chunks/build/api-BsAgXWxi.mjs +1 -1
  29. package/.output/server/chunks/build/board-CJUc032b.mjs +1 -1
  30. package/.output/server/chunks/build/channel-templates-ggsBacZF.mjs +1 -1
  31. package/.output/server/chunks/build/constants-HPHJvjne.mjs +1 -1
  32. package/.output/server/chunks/build/daq-B_dRMDw4.mjs +1 -1
  33. package/.output/server/chunks/build/data-D8uX1dnm.mjs +1 -1
  34. package/.output/server/chunks/build/dcw-D_HD_H_2.mjs +1 -1
  35. package/.output/server/chunks/build/default-CNWS6NUw.mjs +1 -1
  36. package/.output/server/chunks/build/es-Bxu10NJ2.mjs +1 -1
  37. package/.output/server/chunks/build/logs-BnYSMRXM.mjs +1 -1
  38. package/.output/server/chunks/build/monitor-Cq6hRFeQ.mjs +1 -1
  39. package/.output/server/chunks/build/ontology-ohICfVVb.mjs +1 -1
  40. package/.output/server/chunks/build/operations-4zOVhW7o.mjs +1 -1
  41. package/.output/server/chunks/build/optimizer-BlWTsM-W.mjs +1 -1
  42. package/.output/server/chunks/build/pages-DEv4ProW.mjs +1 -1
  43. package/.output/server/chunks/build/permissions-DgXQZSyx.mjs +1 -1
  44. package/.output/server/chunks/build/pipeline-CkavMBhk.mjs +1 -1
  45. package/.output/server/chunks/build/plugins-LRjaSEZy.mjs +1 -1
  46. package/.output/server/chunks/build/schedules-Hz2DJUE2.mjs +1 -1
  47. package/.output/server/chunks/build/settings-DXLJPq7d.mjs +1 -1
  48. package/.output/server/chunks/build/studio-hSqH9TYS.mjs +1 -1
  49. package/.output/server/chunks/build/teams-BbwYJyzv.mjs +1 -1
  50. package/.output/server/chunks/build/tokens-GYaGJ35Z.mjs +1 -1
  51. package/.output/server/chunks/build/tools-CnYi9cou.mjs +1 -1
  52. package/.output/server/chunks/build/town-BV-hbwb5.mjs +1 -1
  53. package/.output/server/chunks/build/town-CcoQKjMj.mjs +1 -1
  54. package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
  55. package/.output/server/chunks/build/types-DWqIeMRP.mjs +1 -1
  56. package/.output/server/chunks/build/useAgentHarnessOptions-Dq0l0Z6d.mjs +1 -1
  57. package/.output/server/chunks/build/useAgentTemplateActions-BFOHb5-L.mjs +1 -1
  58. package/.output/server/chunks/build/useAgentTemplateDialogState-CZzpu5og.mjs +1 -1
  59. package/.output/server/chunks/build/useAgentTemplateForm-CuJPOPcK.mjs +1 -1
  60. package/.output/server/chunks/build/useAgentTemplatesCatalog-B2bIhZLA.mjs +1 -1
  61. package/.output/server/chunks/build/useAmlDatasets-DeoE1idn.mjs +1 -1
  62. package/.output/server/chunks/build/useAmlEntities-BJhvdqHJ.mjs +1 -1
  63. package/.output/server/chunks/build/useAmlEnv-CDIZ6xTe.mjs +1 -1
  64. package/.output/server/chunks/build/useAmlExperiments-Dloi834i.mjs +1 -1
  65. package/.output/server/chunks/build/useAmlFormat-C2sl3MGB.mjs +1 -1
  66. package/.output/server/chunks/build/useAmlJobs-BF9LpwXz.mjs +1 -1
  67. package/.output/server/chunks/build/useAmlModels-_bweOr3R.mjs +1 -1
  68. package/.output/server/chunks/build/useAmlNotes-DQj980iX.mjs +1 -1
  69. package/.output/server/chunks/build/useAmlPredict-DHNgDSJn.mjs +1 -1
  70. package/.output/server/chunks/build/useAnalysisFeed-BrEGV_9x.mjs +1 -1
  71. package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs +1 -1
  72. package/.output/server/chunks/build/useDaqDetailBinding-DCWaLKXT.mjs +1 -1
  73. package/.output/server/chunks/build/useDaqDetailFrames-BOIcOJRB.mjs +1 -1
  74. package/.output/server/chunks/build/useDaqDetailHistory-D6WulPVs.mjs +1 -1
  75. package/.output/server/chunks/build/useDaqDetailNode-BGMRUIXN.mjs +1 -1
  76. package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs +1 -1
  77. package/.output/server/chunks/build/useDaqFilters-zAAVlzJQ.mjs +1 -1
  78. package/.output/server/chunks/build/useDaqNodeWindow-1DWj2H5G.mjs +1 -1
  79. package/.output/server/chunks/build/useDaqOptimizations-BE_9Z2s_.mjs +1 -1
  80. package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs +1 -1
  81. package/.output/server/chunks/build/useDashboardCharts-D45083s_.mjs +1 -1
  82. package/.output/server/chunks/build/useDashboardData-Cw8lrPo5.mjs +1 -1
  83. package/.output/server/chunks/build/useDcwAddNode-KnocMk1m.mjs +1 -1
  84. package/.output/server/chunks/build/useDcwCards-C53IiOcg.mjs +1 -1
  85. package/.output/server/chunks/build/useDcwDetailScope-D6qXrfZu.mjs +1 -1
  86. package/.output/server/chunks/build/useDcwDetailTemplates-DPOXu1o_.mjs +1 -1
  87. package/.output/server/chunks/build/useDcwFilter-DNQ6Ivwd.mjs +1 -1
  88. package/.output/server/chunks/build/useDcwLedger-BhbUBCbi.mjs +1 -1
  89. package/.output/server/chunks/build/useDcwLineMutations-KA7VnggO.mjs +1 -1
  90. package/.output/server/chunks/build/useDcwLineRun-3zx_UCxi.mjs +1 -1
  91. package/.output/server/chunks/build/useDcwParamApprovals-DzyY6Tmi.mjs +1 -1
  92. package/.output/server/chunks/build/useDcwProducts-B05ymt3k.mjs +1 -1
  93. package/.output/server/chunks/build/useDcwQuery-vWpgpSmJ.mjs +1 -1
  94. package/.output/server/chunks/build/useDcwQuickActions-DUkLy34S.mjs +1 -1
  95. package/.output/server/chunks/build/useDcwRecipes-BAb4Yg3d.mjs +1 -1
  96. package/.output/server/chunks/build/useDcwTemplates-D2aoejps.mjs +1 -1
  97. package/.output/server/chunks/build/useDcwWrites-7w8txksD.mjs +1 -1
  98. package/.output/server/chunks/build/useExpConfirmations-BM626N4p.mjs +1 -1
  99. package/.output/server/chunks/build/useLogsFormat-B-o4MHy7.mjs +1 -1
  100. package/.output/server/chunks/build/useLogsManual-D0dvgCsP.mjs +1 -1
  101. package/.output/server/chunks/build/useLogsQuery-D7hrJ3yn.mjs +1 -1
  102. package/.output/server/chunks/build/useLogsScope-DEUyq-My.mjs +1 -1
  103. package/.output/server/chunks/build/useMonitorColumns-vVnt3r5Q.mjs +1 -1
  104. package/.output/server/chunks/build/useMonitorData-DIkiLr_h.mjs +1 -1
  105. package/.output/server/chunks/build/useMonitorFormat-CyURtH8N.mjs +1 -1
  106. package/.output/server/chunks/build/useMonitorTerminal-Dj4QwRNW.mjs +1 -1
  107. package/.output/server/chunks/build/useOperationsFeed-Ci4mZAn_.mjs +1 -1
  108. package/.output/server/chunks/build/useRecipeDispatchApprovals-BYtiqfeb.mjs +1 -1
  109. package/.output/server/chunks/build/useTeamActions-BDjU06LP.mjs +1 -1
  110. package/.output/server/chunks/build/useTeamAddMemberDialog-J3oFn3FI.mjs +1 -1
  111. package/.output/server/chunks/build/useTeamCreateDialog-fbn4ixYu.mjs +1 -1
  112. package/.output/server/chunks/build/useTeamDeployDialog-BNezMD2k.mjs +1 -1
  113. package/.output/server/chunks/build/useTeamDialogState-CFRUQxAf.mjs +1 -1
  114. package/.output/server/chunks/build/useTeamPluginDialog-BZznrOVe.mjs +1 -1
  115. package/.output/server/chunks/build/useTeamsCatalog-DuZFP3oG.mjs +1 -1
  116. package/.output/server/chunks/build/useTokenClipboard-CwQpoFeP.mjs +1 -1
  117. package/.output/server/chunks/build/useTokenCreate-Ctl0YGhL.mjs +1 -1
  118. package/.output/server/chunks/build/useTokenRename-Cm3ypSfN.mjs +1 -1
  119. package/.output/server/chunks/build/useTokenReveal-CeoWL5Mt.mjs +1 -1
  120. package/.output/server/chunks/build/useTokensList-CNIzON3u.mjs +1 -1
  121. package/.output/server/chunks/build/useWorkbenchAuth-CY-bVFMu.mjs +1 -1
  122. package/.output/server/chunks/build/useWorkbenchWorkspaces-DhZQFss9.mjs +1 -1
  123. package/.output/server/chunks/build/useWorkspaceAuth-Dlvj9csD.mjs +1 -1
  124. package/.output/server/chunks/build/useWorkspaceChannels-CPFepxTN.mjs +1 -1
  125. package/.output/server/chunks/build/useWorkspacePanels-CTwREmq0.mjs +1 -1
  126. package/.output/server/chunks/build/useWorkspaceShell-BCypjjjE.mjs +1 -1
  127. package/.output/server/chunks/build/users-DjiVC_tS.mjs +1 -1
  128. package/.output/server/chunks/build/workshop-1gKeDASV.mjs +1 -1
  129. package/.output/server/chunks/routes/api/health.get.mjs +1 -1
  130. package/.output/server/chunks/routes/api/index.get.mjs +1 -1
  131. package/.output/server/chunks/routes/api/index.post.mjs +1 -1
  132. package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
  133. package/.output/server/chunks/routes/api/metrics.mjs +1 -1
  134. package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +1 -1
  135. package/.output/server/chunks/routes/api/plugins/client/_name_.get.mjs +1 -1
  136. package/.output/server/chunks/routes/api/plugins/i18n.get.mjs +1 -1
  137. package/.output/server/chunks/routes/api/plugins/manifest.get.mjs +1 -1
  138. package/.output/server/chunks/routes/api/system/backup/list.get.mjs +1 -1
  139. package/.output/server/chunks/routes/api/system/backup/run.post.mjs +3 -3
  140. package/.output/server/chunks/routes/api/system/config-groups/_id_.delete.mjs +1 -1
  141. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs +1 -1
  142. package/.output/server/chunks/routes/api/system/config.get.mjs +1 -1
  143. package/.output/server/chunks/routes/api/system/index.get.mjs +1 -1
  144. package/.output/server/chunks/routes/api/system/index.patch.mjs +1 -1
  145. package/.output/server/chunks/routes/api/system/index.post.mjs +1 -1
  146. package/.output/server/chunks/routes/api/system/index2.get.mjs +1 -1
  147. package/.output/server/chunks/routes/api/system/monitor/terminal/ws.mjs +1 -1
  148. package/.output/server/chunks/routes/api/system/monitor/terminate.post.mjs +1 -1
  149. package/.output/server/chunks/routes/api/system/monitor.get.mjs +1 -1
  150. package/.output/server/chunks/routes/api/system/settings/events.get.mjs +1 -1
  151. package/.output/server/chunks/routes/api/system/settings/reset.post.mjs +1 -1
  152. package/.output/server/chunks/routes/api/system/timezone.get.mjs +1 -1
  153. package/.output/server/chunks/routes/api/users/_id_.delete.mjs +1 -1
  154. package/.output/server/chunks/routes/api/users/_id_.get.mjs +1 -1
  155. package/.output/server/chunks/routes/api/users/_id_.put.mjs +1 -1
  156. package/.output/server/chunks/routes/api/users/change-password.post.mjs +1 -1
  157. package/.output/server/chunks/routes/api/users/index.get.mjs +1 -1
  158. package/.output/server/chunks/routes/api/users/index.post.mjs +1 -1
  159. package/.output/server/chunks/routes/api/users/login.post.mjs +1 -1
  160. package/.output/server/chunks/routes/api/users/logout.post.mjs +1 -1
  161. package/.output/server/chunks/routes/api/users/me.get.mjs +1 -1
  162. package/.output/server/chunks/routes/api/users/register.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/users/setup-status.get.mjs +1 -1
  164. package/.output/server/chunks/routes/api/users/tokens/_id/token.get.mjs +1 -1
  165. package/.output/server/chunks/routes/api/users/tokens/_id_.delete.mjs +1 -1
  166. package/.output/server/chunks/routes/api/users/tokens/_id_.patch.mjs +1 -1
  167. package/.output/server/chunks/routes/api/workshop/_...path_.mjs +1 -1
  168. package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.get.mjs +1 -1
  169. package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.post.mjs +1 -1
  170. package/.output/server/chunks/routes/api/workshop/a2a/send.post.mjs +1 -1
  171. package/.output/server/chunks/routes/api/workshop/agent-tools/approvals/_id/decide.post.mjs +1 -1
  172. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.delete.mjs +1 -1
  173. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.patch.mjs +1 -1
  174. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/grant.post.mjs +1 -1
  175. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/revoke.post.mjs +1 -1
  176. package/.output/server/chunks/routes/api/workshop/agent-tools/index.get.mjs +1 -1
  177. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +2 -2
  178. package/.output/server/chunks/routes/api/workshop/agent-tools/index2.get.mjs +1 -1
  179. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +1 -1
  180. package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs +1 -1
  181. package/.output/server/chunks/routes/api/workshop/agents/_id_.delete.mjs +1 -1
  182. package/.output/server/chunks/routes/api/workshop/agents/_id_.get.mjs +1 -1
  183. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +1 -1
  184. package/.output/server/chunks/routes/api/workshop/agents/subscribe.post.mjs +1 -1
  185. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id/preview.get.mjs +1 -1
  186. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.delete.mjs +1 -1
  187. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +1 -1
  188. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.patch.mjs +1 -1
  189. package/.output/server/chunks/routes/api/workshop/aml/entities/prune.post.mjs +1 -1
  190. package/.output/server/chunks/routes/api/workshop/aml/env/recheck.post.mjs +1 -1
  191. package/.output/server/chunks/routes/api/workshop/aml/env/uv.post.mjs +1 -1
  192. package/.output/server/chunks/routes/api/workshop/aml/env/venv.post.mjs +1 -1
  193. package/.output/server/chunks/routes/api/workshop/aml/index.get.mjs +1 -1
  194. package/.output/server/chunks/routes/api/workshop/aml/index.post.mjs +1 -1
  195. package/.output/server/chunks/routes/api/workshop/aml/index2.get.mjs +1 -1
  196. package/.output/server/chunks/routes/api/workshop/aml/index2.post.mjs +1 -1
  197. package/.output/server/chunks/routes/api/workshop/aml/index3.get.mjs +1 -1
  198. package/.output/server/chunks/routes/api/workshop/aml/index3.post.mjs +1 -1
  199. package/.output/server/chunks/routes/api/workshop/aml/index4.get.mjs +1 -1
  200. package/.output/server/chunks/routes/api/workshop/aml/index5.get.mjs +1 -1
  201. package/.output/server/chunks/routes/api/workshop/aml/index6.get.mjs +1 -1
  202. package/.output/server/chunks/routes/api/workshop/aml/index7.get.mjs +1 -1
  203. package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/cancel.post.mjs +1 -1
  204. package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/logs.get.mjs +1 -1
  205. package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/retry.post.mjs +1 -1
  206. package/.output/server/chunks/routes/api/workshop/aml/jobs/_id_.delete.mjs +1 -1
  207. package/.output/server/chunks/routes/api/workshop/aml/jobs/_id_.get.mjs +1 -1
  208. package/.output/server/chunks/routes/api/workshop/aml/models/_id/predict.post.mjs +1 -1
  209. package/.output/server/chunks/routes/api/workshop/aml/models/_id/promote.post.mjs +1 -1
  210. package/.output/server/chunks/routes/api/workshop/aml/models/_id_.delete.mjs +1 -1
  211. package/.output/server/chunks/routes/api/workshop/aml/models/_id_.patch.mjs +1 -1
  212. package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id/train.post.mjs +1 -1
  213. package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id_.delete.mjs +1 -1
  214. package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id_.patch.mjs +1 -1
  215. package/.output/server/chunks/routes/api/workshop/aml/twin/providers.get.mjs +1 -1
  216. package/.output/server/chunks/routes/api/workshop/analysis/_id_.delete.mjs +1 -1
  217. package/.output/server/chunks/routes/api/workshop/analysis/_id_.get.mjs +1 -1
  218. package/.output/server/chunks/routes/api/workshop/analysis/_id_.patch.mjs +1 -1
  219. package/.output/server/chunks/routes/api/workshop/approvals/_id/decide.post.mjs +1 -1
  220. package/.output/server/chunks/routes/api/workshop/assets/character/_id_.delete.mjs +1 -1
  221. package/.output/server/chunks/routes/api/workshop/assets/devices/_id_.delete.mjs +1 -1
  222. package/.output/server/chunks/routes/api/workshop/assets/index.get.mjs +1 -1
  223. package/.output/server/chunks/routes/api/workshop/assets/index.post.mjs +1 -1
  224. package/.output/server/chunks/routes/api/workshop/assets/index2.get.mjs +1 -1
  225. package/.output/server/chunks/routes/api/workshop/assets/index2.post.mjs +1 -1
  226. package/.output/server/chunks/routes/api/workshop/audit.get.mjs +1 -1
  227. package/.output/server/chunks/routes/api/workshop/caller.mjs +1 -1
  228. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +2 -2
  229. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.delete.mjs +1 -1
  230. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.get.mjs +1 -1
  231. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +1 -1
  232. package/.output/server/chunks/routes/api/workshop/channel-templates/from-channel.post.mjs +1 -1
  233. package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.mjs +1 -1
  234. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.get.mjs +1 -1
  235. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.post.mjs +1 -1
  236. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/_memoryId_.delete.mjs +1 -1
  237. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/search.post.mjs +1 -1
  238. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/model.patch.mjs +1 -1
  239. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/position.patch.mjs +1 -1
  240. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/range.patch.mjs +1 -1
  241. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/rotate-token.post.mjs +1 -1
  242. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/stop.post.mjs +1 -1
  243. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.delete.mjs +1 -1
  244. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +1 -1
  245. package/.output/server/chunks/routes/api/workshop/channels/_id/chat/index.get.mjs +1 -1
  246. package/.output/server/chunks/routes/api/workshop/channels/_id/chat/index.post.mjs +1 -1
  247. package/.output/server/chunks/routes/api/workshop/channels/_id/chat/messages/_messageId/read.post.mjs +1 -1
  248. package/.output/server/chunks/routes/api/workshop/channels/_id/chat/permissions.get.mjs +1 -1
  249. package/.output/server/chunks/routes/api/workshop/channels/_id/events.get.mjs +1 -1
  250. package/.output/server/chunks/routes/api/workshop/channels/_id/execution-mode.patch.mjs +1 -1
  251. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +1 -1
  252. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +1 -1
  253. package/.output/server/chunks/routes/api/workshop/channels/_id/index.put.mjs +1 -1
  254. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.get.mjs +1 -1
  255. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.post.mjs +1 -1
  256. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.put.mjs +1 -1
  257. package/.output/server/chunks/routes/api/workshop/channels/_id/index3.get.mjs +1 -1
  258. package/.output/server/chunks/routes/api/workshop/channels/_id/index3.post.mjs +1 -1
  259. package/.output/server/chunks/routes/api/workshop/channels/_id/index4.get.mjs +1 -1
  260. package/.output/server/chunks/routes/api/workshop/channels/_id/index4.post.mjs +1 -1
  261. package/.output/server/chunks/routes/api/workshop/channels/_id/index5.get.mjs +1 -1
  262. package/.output/server/chunks/routes/api/workshop/channels/_id/index6.get.mjs +1 -1
  263. package/.output/server/chunks/routes/api/workshop/channels/_id/index7.get.mjs +1 -1
  264. package/.output/server/chunks/routes/api/workshop/channels/_id/line.put.mjs +1 -1
  265. package/.output/server/chunks/routes/api/workshop/channels/_id/members/_userId/approve.post.mjs +1 -1
  266. package/.output/server/chunks/routes/api/workshop/channels/_id/members/_userId_.delete.mjs +1 -1
  267. package/.output/server/chunks/routes/api/workshop/channels/_id/members/join.post.mjs +1 -1
  268. package/.output/server/chunks/routes/api/workshop/channels/_id/members/leave.post.mjs +1 -1
  269. package/.output/server/chunks/routes/api/workshop/channels/_id/memories/_memoryId_.delete.mjs +1 -1
  270. package/.output/server/chunks/routes/api/workshop/channels/_id/queue.get.mjs +1 -1
  271. package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +1 -1
  272. package/.output/server/chunks/routes/api/workshop/channels/_id/tools/reset.post.mjs +1 -1
  273. package/.output/server/chunks/routes/api/workshop/channels/_id/twin-profile.get.mjs +1 -1
  274. package/.output/server/chunks/routes/api/workshop/channels/_id/twin-profile.patch.mjs +1 -1
  275. package/.output/server/chunks/routes/api/workshop/channels/_id_.delete.mjs +1 -1
  276. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +1 -1
  277. package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs +1 -1
  278. package/.output/server/chunks/routes/api/workshop/channels.get.mjs +1 -1
  279. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +7 -7
  280. package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +1 -1
  281. package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs +1 -1
  282. package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +1 -1
  283. package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +1 -1
  284. package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs +2 -2
  285. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +1 -1
  286. package/.output/server/chunks/routes/api/workshop/daq/_id/test.post.mjs +1 -1
  287. package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +1 -1
  288. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +1 -1
  289. package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +1 -1
  290. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +1 -1
  291. package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +1 -1
  292. package/.output/server/chunks/routes/api/workshop/daq/export/_id_.get.mjs +1 -1
  293. package/.output/server/chunks/routes/api/workshop/daq/export-file.get.mjs +1 -1
  294. package/.output/server/chunks/routes/api/workshop/daq/index.get.mjs +1 -1
  295. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +1 -1
  296. package/.output/server/chunks/routes/api/workshop/daq/index2.post.mjs +1 -1
  297. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +1 -1
  298. package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +1 -1
  299. package/.output/server/chunks/routes/api/workshop/daq/query.get.mjs +1 -1
  300. package/.output/server/chunks/routes/api/workshop/daq/storage/retention.put.mjs +1 -1
  301. package/.output/server/chunks/routes/api/workshop/daq/storage/rollup.post.mjs +1 -1
  302. package/.output/server/chunks/routes/api/workshop/daq/storage.get.mjs +1 -1
  303. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +1 -1
  304. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +1 -1
  305. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +1 -1
  306. package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
  307. package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +1 -1
  308. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +1 -1
  309. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +1 -1
  310. package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +1 -1
  311. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +1 -1
  312. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +1 -1
  313. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +1 -1
  314. package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +1 -1
  315. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +1 -1
  316. package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +1 -1
  317. package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +1 -1
  318. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +1 -1
  319. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +1 -1
  320. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +1 -1
  321. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +1 -1
  322. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +1 -1
  323. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +1 -1
  324. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
  325. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +1 -1
  326. package/.output/server/chunks/routes/api/workshop/dcw/index7.get.mjs +1 -1
  327. package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +1 -1
  328. package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +1 -1
  329. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  330. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  331. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +1 -1
  332. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs +1 -1
  333. package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs +1 -1
  334. package/.output/server/chunks/routes/api/workshop/dcw/mes-hook-test.post.mjs +1 -1
  335. package/.output/server/chunks/routes/api/workshop/dcw/mes-test-read.post.mjs +1 -1
  336. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
  337. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
  338. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +1 -1
  339. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +1 -1
  340. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +1 -1
  341. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +1 -1
  342. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
  343. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
  344. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +1 -1
  345. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +1 -1
  346. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +1 -1
  347. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/mark-good.post.mjs +1 -1
  348. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/revert.post.mjs +1 -1
  349. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +1 -1
  350. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs +1 -1
  351. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +2 -2
  352. package/.output/server/chunks/routes/api/workshop/dcw/recipes/index.patch.mjs +1 -1
  353. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +1 -1
  354. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +1 -1
  355. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +1 -1
  356. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  357. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  358. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +1 -1
  359. package/.output/server/chunks/routes/api/workshop/device-twins/_id/control.post.mjs +1 -1
  360. package/.output/server/chunks/routes/api/workshop/device-twins/_id/telemetry.post.mjs +1 -1
  361. package/.output/server/chunks/routes/api/workshop/device-twins/_id_.delete.mjs +1 -1
  362. package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +1 -1
  363. package/.output/server/chunks/routes/api/workshop/eda/charts/_id_.delete.mjs +1 -1
  364. package/.output/server/chunks/routes/api/workshop/eda/charts/_id_.get.mjs +1 -1
  365. package/.output/server/chunks/routes/api/workshop/eda/charts/_id_.patch.mjs +1 -1
  366. package/.output/server/chunks/routes/api/workshop/eda/commonality.get.mjs +1 -1
  367. package/.output/server/chunks/routes/api/workshop/eda/dashboards/_id_.delete.mjs +1 -1
  368. package/.output/server/chunks/routes/api/workshop/eda/dashboards/_id_.get.mjs +1 -1
  369. package/.output/server/chunks/routes/api/workshop/eda/dashboards/_id_.patch.mjs +1 -1
  370. package/.output/server/chunks/routes/api/workshop/eda/features/jobs.get.mjs +1 -1
  371. package/.output/server/chunks/routes/api/workshop/eda/features/run.post.mjs +1 -1
  372. package/.output/server/chunks/routes/api/workshop/eda/golden/_id_.delete.mjs +1 -1
  373. package/.output/server/chunks/routes/api/workshop/eda/golden/deviation.get.mjs +1 -1
  374. package/.output/server/chunks/routes/api/workshop/eda/golden/single.get.mjs +1 -1
  375. package/.output/server/chunks/routes/api/workshop/eda/index.get.mjs +1 -1
  376. package/.output/server/chunks/routes/api/workshop/eda/index.post.mjs +1 -1
  377. package/.output/server/chunks/routes/api/workshop/eda/index2.get.mjs +1 -1
  378. package/.output/server/chunks/routes/api/workshop/eda/index2.post.mjs +1 -1
  379. package/.output/server/chunks/routes/api/workshop/eda/index3.get.mjs +1 -1
  380. package/.output/server/chunks/routes/api/workshop/eda/index3.post.mjs +1 -1
  381. package/.output/server/chunks/routes/api/workshop/eda/index4.get.mjs +1 -1
  382. package/.output/server/chunks/routes/api/workshop/eda/index4.post.mjs +1 -1
  383. package/.output/server/chunks/routes/api/workshop/eda/index5.get.mjs +1 -1
  384. package/.output/server/chunks/routes/api/workshop/eda/index5.post.mjs +1 -1
  385. package/.output/server/chunks/routes/api/workshop/eda/index6.get.mjs +1 -1
  386. package/.output/server/chunks/routes/api/workshop/eda/index6.post.mjs +1 -1
  387. package/.output/server/chunks/routes/api/workshop/eda/index7.get.mjs +1 -1
  388. package/.output/server/chunks/routes/api/workshop/eda/index7.post.mjs +1 -1
  389. package/.output/server/chunks/routes/api/workshop/eda/index8.get.mjs +1 -1
  390. package/.output/server/chunks/routes/api/workshop/eda/index8.post.mjs +1 -1
  391. package/.output/server/chunks/routes/api/workshop/eda/ocap/_id_.get.mjs +1 -1
  392. package/.output/server/chunks/routes/api/workshop/eda/ocap/_id_.patch.mjs +1 -1
  393. package/.output/server/chunks/routes/api/workshop/eda/runs/_id/labels.patch.mjs +1 -1
  394. package/.output/server/chunks/routes/api/workshop/eda/runs.get.mjs +1 -1
  395. package/.output/server/chunks/routes/api/workshop/eda/spc/_id/recompute.post.mjs +1 -1
  396. package/.output/server/chunks/routes/api/workshop/eda/spc/_id_.delete.mjs +1 -1
  397. package/.output/server/chunks/routes/api/workshop/eda/spc/_id_.get.mjs +1 -1
  398. package/.output/server/chunks/routes/api/workshop/eda/templates/_id_.delete.mjs +1 -1
  399. package/.output/server/chunks/routes/api/workshop/eda/templates/_id_.get.mjs +1 -1
  400. package/.output/server/chunks/routes/api/workshop/eda/templates/_id_.patch.mjs +1 -1
  401. package/.output/server/chunks/routes/api/workshop/eda/vm/predict.post.mjs +1 -1
  402. package/.output/server/chunks/routes/api/workshop/exp/confirmations/_id/decide.post.mjs +1 -1
  403. package/.output/server/chunks/routes/api/workshop/exp/index.get.mjs +1 -1
  404. package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +1 -1
  405. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  406. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  407. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  408. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  409. package/.output/server/chunks/routes/api/workshop/index.get.mjs +1 -1
  410. package/.output/server/chunks/routes/api/workshop/index.mjs +1 -1
  411. package/.output/server/chunks/routes/api/workshop/index.post.mjs +1 -1
  412. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  413. package/.output/server/chunks/routes/api/workshop/index10.get.mjs +1 -1
  414. package/.output/server/chunks/routes/api/workshop/index10.post.mjs +1 -1
  415. package/.output/server/chunks/routes/api/workshop/index11.get.mjs +1 -1
  416. package/.output/server/chunks/routes/api/workshop/index12.get.mjs +1 -1
  417. package/.output/server/chunks/routes/api/workshop/index13.get.mjs +1 -1
  418. package/.output/server/chunks/routes/api/workshop/index14.get.mjs +1 -1
  419. package/.output/server/chunks/routes/api/workshop/index15.get.mjs +1 -1
  420. package/.output/server/chunks/routes/api/workshop/index16.get.mjs +1 -1
  421. package/.output/server/chunks/routes/api/workshop/index2.get.mjs +1 -1
  422. package/.output/server/chunks/routes/api/workshop/index2.post.mjs +1 -1
  423. package/.output/server/chunks/routes/api/workshop/index3.get.mjs +1 -1
  424. package/.output/server/chunks/routes/api/workshop/index3.post.mjs +1 -1
  425. package/.output/server/chunks/routes/api/workshop/index4.get.mjs +1 -1
  426. package/.output/server/chunks/routes/api/workshop/index4.post.mjs +1 -1
  427. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +1 -1
  428. package/.output/server/chunks/routes/api/workshop/index5.post.mjs +1 -1
  429. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
  430. package/.output/server/chunks/routes/api/workshop/index6.post.mjs +1 -1
  431. package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
  432. package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
  433. package/.output/server/chunks/routes/api/workshop/index8.get.mjs +1 -1
  434. package/.output/server/chunks/routes/api/workshop/index8.post.mjs +1 -1
  435. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +1 -1
  436. package/.output/server/chunks/routes/api/workshop/index9.post.mjs +1 -1
  437. package/.output/server/chunks/routes/api/workshop/mailbox/all.get.mjs +1 -1
  438. package/.output/server/chunks/routes/api/workshop/mailbox.get.mjs +1 -1
  439. package/.output/server/chunks/routes/api/workshop/memories/maintenance.post.mjs +1 -1
  440. package/.output/server/chunks/routes/api/workshop/mes/datasets.get.mjs +1 -1
  441. package/.output/server/chunks/routes/api/workshop/mes/index.get.mjs +1 -1
  442. package/.output/server/chunks/routes/api/workshop/mes/index.put.mjs +1 -1
  443. package/.output/server/chunks/routes/api/workshop/mes/points.get.mjs +1 -1
  444. package/.output/server/chunks/routes/api/workshop/mes/preview.post.mjs +1 -1
  445. package/.output/server/chunks/routes/api/workshop/mes/secrets/_ref_.delete.mjs +1 -1
  446. package/.output/server/chunks/routes/api/workshop/notifications/read.post.mjs +1 -1
  447. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/autofix.mjs +1 -1
  448. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/compile.mjs +1 -1
  449. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/evidence.mjs +1 -1
  450. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/publish.mjs +1 -1
  451. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval-decision.mjs +1 -1
  452. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval.mjs +1 -1
  453. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/transition.mjs +1 -1
  454. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/subgraph.mjs +1 -1
  455. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/validate.mjs +1 -1
  456. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId_.mjs +1 -1
  457. package/.output/server/chunks/routes/api/workshop/ontology/export/_ontologyId_.mjs +1 -1
  458. package/.output/server/chunks/routes/api/workshop/ontology/index.mjs +1 -1
  459. package/.output/server/chunks/routes/api/workshop/ontology/scene.mjs +1 -1
  460. package/.output/server/chunks/routes/api/workshop/ontology/traces/_ontologyId_.mjs +1 -1
  461. package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId/compare.mjs +1 -1
  462. package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId_.mjs +1 -1
  463. package/.output/server/chunks/routes/api/workshop/optimization/bindings/_id_.delete.mjs +1 -1
  464. package/.output/server/chunks/routes/api/workshop/optimization/capabilities.get.mjs +1 -1
  465. package/.output/server/chunks/routes/api/workshop/optimization/index.get.mjs +1 -1
  466. package/.output/server/chunks/routes/api/workshop/optimization/index.mjs +1 -1
  467. package/.output/server/chunks/routes/api/workshop/optimization/index2.mjs +1 -1
  468. package/.output/server/chunks/routes/api/workshop/optimization/profiles/_id/publish.post.mjs +1 -1
  469. package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/crosscheck.post.mjs +1 -1
  470. package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/dispatch.post.mjs +1 -1
  471. package/.output/server/chunks/routes/api/workshop/optimization/solve.post.mjs +1 -1
  472. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  473. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  474. package/.output/server/chunks/routes/api/workshop/plugins/index.get.mjs +1 -1
  475. package/.output/server/chunks/routes/api/workshop/process-model/datasets.post.mjs +1 -1
  476. package/.output/server/chunks/routes/api/workshop/process-model/eligibility.mjs +1 -1
  477. package/.output/server/chunks/routes/api/workshop/process-model/experiments.mjs +305 -20
  478. package/.output/server/chunks/routes/api/workshop/process-model/experiments.mjs.map +1 -1
  479. package/.output/server/chunks/routes/api/workshop/process-model/index.mjs +1 -1
  480. package/.output/server/chunks/routes/api/workshop/process-model/specs/_id/publish.post.mjs +1 -1
  481. package/.output/server/chunks/routes/api/workshop/process-model/train.post.mjs +1 -1
  482. package/.output/server/chunks/routes/api/workshop/runtime.get.mjs +1 -1
  483. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  484. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  485. package/.output/server/chunks/routes/api/workshop/scene/layouts.get.mjs +1 -1
  486. package/.output/server/chunks/routes/api/workshop/schedules/_id/run.post.mjs +1 -1
  487. package/.output/server/chunks/routes/api/workshop/schedules/_id/runs.get.mjs +1 -1
  488. package/.output/server/chunks/routes/api/workshop/schedules/_id_.delete.mjs +1 -1
  489. package/.output/server/chunks/routes/api/workshop/schedules/_id_.get.mjs +1 -1
  490. package/.output/server/chunks/routes/api/workshop/schedules/_id_.patch.mjs +1 -1
  491. package/.output/server/chunks/routes/api/workshop/tasks/_id/complete.post.mjs +1 -1
  492. package/.output/server/chunks/routes/api/workshop/tasks/_id/dispatch.post.mjs +1 -1
  493. package/.output/server/chunks/routes/api/workshop/tasks/_id/index.post.mjs +1 -1
  494. package/.output/server/chunks/routes/api/workshop/tasks/_id/reassign.post.mjs +1 -1
  495. package/.output/server/chunks/routes/api/workshop/tasks/_id/reopen.post.mjs +1 -1
  496. package/.output/server/chunks/routes/api/workshop/tasks/_id/report.post.mjs +1 -1
  497. package/.output/server/chunks/routes/api/workshop/tasks/_id/retry.post.mjs +1 -1
  498. package/.output/server/chunks/routes/api/workshop/tasks/_id/supersede.post.mjs +1 -1
  499. package/.output/server/chunks/routes/api/workshop/tasks/_id/update.post.mjs +1 -1
  500. package/.output/server/chunks/routes/api/workshop/tasks/_id_.get.mjs +1 -1
  501. package/.output/server/chunks/routes/api/workshop/tasks.get.mjs +1 -1
  502. package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.post.mjs +1 -1
  503. package/.output/server/chunks/routes/api/workshop/teams/_id/index.get.mjs +1 -1
  504. package/.output/server/chunks/routes/api/workshop/teams/_id/index.post.mjs +1 -1
  505. package/.output/server/chunks/routes/api/workshop/teams/_id/index.put.mjs +1 -1
  506. package/.output/server/chunks/routes/api/workshop/teams/_id/index2.get.mjs +1 -1
  507. package/.output/server/chunks/routes/api/workshop/teams/_id/members/_agentId_.delete.mjs +1 -1
  508. package/.output/server/chunks/routes/api/workshop/teams/_id_.delete.mjs +1 -1
  509. package/.output/server/chunks/routes/api/workshop/teams/_id_.get.mjs +1 -1
  510. package/.output/server/chunks/routes/api/workshop/teams/_id_.patch.mjs +1 -1
  511. package/.output/server/chunks/routes/api/workshop/tools/_name_.get.mjs +1 -1
  512. package/.output/server/chunks/routes/api/workshop/tools/sync.post.mjs +1 -1
  513. package/.output/server/chunks/routes/api/workshop/users/me.get.mjs +1 -1
  514. package/.output/server/chunks/routes/api/workshop/users/register.post.mjs +1 -1
  515. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +2 -2
  516. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.delete.mjs +1 -1
  517. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.post.mjs +1 -1
  518. package/.output/server/chunks/routes/api/workshop/workspaces/index.delete.mjs +1 -1
  519. package/.output/server/chunks/routes/renderer.mjs +2 -2
  520. package/.output/server/chunks/virtual/entry.mjs +5 -1
  521. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  522. package/.output/server/index.mjs +1 -1
  523. package/.output/server/package.json +1 -1
  524. package/README-zh.md +3 -3
  525. package/README.md +3 -3
  526. package/config.yml +1 -1
  527. package/docs/aml.md +112 -112
  528. package/docs/cli.en.md +4 -4
  529. package/docs/cli.md +3 -3
  530. package/docs/plugins.en.md +1 -1
  531. package/docs/plugins.md +1 -1
  532. package/docs/sdk.en.md +1 -1
  533. package/docs/sdk.md +1 -1
  534. package/i18n/locales/en.ts +2 -0
  535. package/i18n/locales/zh-CN.ts +2 -0
  536. package/package.json +1 -1
  537. package/scripts/_audit/demo-shot-studio.mjs +37 -0
  538. package/scripts/_audit/demo-shots.mjs +38 -0
  539. package/scripts/_audit/gen-demo-report.py +69 -0
  540. package/scripts/_audit/mqtt-pub.mjs +13 -0
  541. package/scripts/_audit/mqtt-topic-probe.mjs +15 -0
  542. package/scripts/_audit/proto-b3-device-4021.mjs +90 -0
  543. package/scripts/_audit/proto-doc-device-4120.mjs +53 -0
  544. package/scripts/acceptance-scientific-loop.ts +224 -0
  545. package/scripts/generate-identifiability-fixtures.ts +98 -0
  546. package/server/api/workshop/process-model/experiments.ts +28 -2
  547. package/server/plugins-builtin/diag-bridge/host/plugin.mjs +759 -753
  548. package/server/plugins-builtin/rag-bridge/index.mjs +490 -484
  549. package/server/repositories/user.repository.ts +24 -1
  550. package/server/services/user.service.ts +8 -6
  551. package/server/services/workshop/agents/host-tool-bridge/catalog.ts +2 -0
  552. package/server/services/workshop/agents/host-tool-bridge/tools/industrial.ts +4 -1
  553. package/server/services/workshop/agents/host-tool-bridge/tools/messaging.ts +5 -1
  554. package/server/services/workshop/agents/industrial/aml-dataset-tools.ts +3 -0
  555. package/server/services/workshop/agents/industrial/bench-approval-only.ts +335 -58
  556. package/server/services/workshop/agents/industrial/dcw-tools.ts +7 -1
  557. package/server/services/workshop/agents/industrial/identifiability-tools.ts +247 -0
  558. package/server/services/workshop/agents/industrial/index.ts +1 -0
  559. package/server/services/workshop/agents/industrial/manual-approval.ts +3 -1
  560. package/server/services/workshop/agents/industrial/ops-tools.ts +58 -15
  561. package/server/services/workshop/agents/industrial/optimization-tools.ts +33 -19
  562. package/server/services/workshop/agents/plugin-governance.ts +22 -2
  563. package/server/services/workshop/aml/action-coverage.ts +253 -0
  564. package/server/services/workshop/aml/causal-claim.ts +68 -0
  565. package/server/services/workshop/aml/dataset-builder.ts +19 -0
  566. package/server/services/workshop/aml/gates.ts +45 -1
  567. package/server/services/workshop/aml/identifiability.ts +335 -0
  568. package/server/services/workshop/aml/intervention-ledger.ts +281 -0
  569. package/server/services/workshop/aml/ontology-learning.ts +143 -0
  570. package/server/services/workshop/aml/twin/cross-check.ts +176 -0
  571. package/server/services/workshop/daq/daq-controller/contracts.ts +3 -0
  572. package/server/services/workshop/daq/daq-controller/crud.ts +12 -2
  573. package/server/services/workshop/daq/daq-controller/push.ts +22 -0
  574. package/server/services/workshop/daq/drivers/mqtt.ts +28 -0
  575. package/server/services/workshop/daq/drivers/shared.ts +6 -0
  576. package/server/services/workshop/db/database/open.ts +2 -0
  577. package/server/services/workshop/db/database/rows.ts +4 -1
  578. package/server/services/workshop/db/database/schema.ts +38 -2
  579. package/server/services/workshop/db/message.repo.ts +10 -3
  580. package/server/services/workshop/dcw/dcw-controller/write-audit.ts +21 -1
  581. package/server/services/workshop/process-model/experiment-runner.ts +393 -0
  582. package/server/services/workshop/process-model/experiment.ts +25 -10
  583. package/server/services/workshop/runtime/agent-runtime/lifecycle.ts +34 -11
  584. package/server/services/workshop/runtime/agent-runtime/state.ts +6 -1
  585. package/server/services/workshop/settings.ts +4 -1
  586. package/server/services/workshop/types/a2a.ts +16 -1
  587. package/shared/config/schema.json +2 -0
  588. package/shared/dcw-protocol/closed-loop.ts +2 -0
  589. package/.output/public/_nuxt/builds/meta/1231252d-2864-43c0-83b2-4e70d3b86dea.json +0 -1
@@ -1,753 +1,759 @@
1
- /**
2
- * 插件对象(settings / routes / tools / hooks)
3
- * (由 server/plugins-builtin/diag-bridge/index.mjs 按职责拆出;内容逐行原文搬运)
4
- */
5
- import { AUTO_COOLDOWN_MS, DEFAULT_BASE, MIN } from './constants.mjs'
6
- import { absoluteUrl, authHeadersOf, authHint, autoEnabledOf, autoRulesOf, baseOf, diagTokenOf, harnessOf, jget, jpatch, kvRuns, mapResultsError, parseAutoRules, probeResults, resultsAllowAnnotateOf, resultsAllowWorkerOf, resultsDefaultLimitOf, resultsEnabledOf, resultsMdMaxOf, runKey } from './helpers.mjs'
7
- import { snapshotCore } from './snapshot.mjs'
8
- import { startDiagnosis } from './diagnosis.mjs'
9
- import { uploadExportDataset } from './exports.mjs'
10
- import { sweepOnce } from './sweep.mjs'
11
- import { intentLabel } from './intents.mjs'
12
-
13
- export default {
14
- name: 'diag-bridge',
15
- version: '2.2.0',
16
- description: '深度诊断桥接 v2.2:异步任务式深度诊断(提交即返 task_id)+ daq-export 数据集多文件分析(diag_run export_id= 全量时序+manifest)+ 同步等待模式(mode=sync)+ 完成自动回执 Channel(修复发起者身份丢失)+ 报告在线查看。完成后经 rag-bridge kb_agent 入库知识库。',
17
- auth: 'user',
18
- client: './client.mjs', // 前端面板(插件页注入;i18n 见 i18n.json)
19
- // 插件配置分组(声明式):连接 / 执行 / 自动化 三个独立分区
20
- configGroups: [
21
- { id: 'default', labelKey: 'plugin.diag-bridge.group.conn', label: '诊断服务连接', description: '后端地址与鉴权 Token', order: 420 },
22
- { id: 'run', labelKey: 'plugin.diag-bridge.group.run', label: '诊断执行参数', description: 'diag_run 的引擎与预算', order: 430 },
23
- { id: 'auto', labelKey: 'plugin.diag-bridge.group.auto', label: '自动诊断', description: '越限自动发起诊断(DAQ 采样事件驱动)', order: 440, collapsed: true },
24
- { id: 'results', labelKey: 'plugin.diag-bridge.group.results', label: '分析结果库', description: '对 IDD 结果库的检索/详情/元数据编辑(默认关闭;结果可见面 = Token 属主可见面)', order: 450, collapsed: true },
25
- ],
26
- // 插件设置声明(key 编址 plugins.diag-bridge.<key>;labelKey 解析 i18n.json)
27
- settings: [
28
- { key: 'base_url', type: 'string', default: DEFAULT_BASE, group: 'default', labelKey: 'plugin.diag-bridge.settings.base_url', label: '诊断服务地址', description: 'industrial-deep-diagnostic 后端(http/https,host 限 127.0.0.1/localhost);保存即热生效' },
29
- { key: 'token', type: 'string', default: '', group: 'default', labelKey: 'plugin.diag-bridge.settings.token', label: '诊断 API Token', description: '诊断服务开启鉴权时(v4 起默认强制)的 API Token(Authorization: Bearer);空=匿名;保存即热生效' },
30
- { key: 'harness', type: 'string', default: '', group: 'run', labelKey: 'plugin.diag-bridge.settings.harness', label: '诊断引擎', description: 'diag_run 用的引擎 id(omp/claude/mock 等;空=取 kv/omp);无 Anthropic key 的机器请用 omp' },
31
- { key: 'max_turns', type: 'number', default: 0, min: 0, max: 2000, group: 'run', labelKey: 'plugin.diag-bridge.settings.max_turns', label: '诊断最大轮数', description: 'diagnosis/start 的 maxTurns(0=取 kv/内置默认 220)' },
32
- { key: 'max_minutes', type: 'number', default: 0, min: 0, max: 720, group: 'run', labelKey: 'plugin.diag-bridge.settings.max_minutes', label: '诊断超时(分钟)', description: 'diagnosis/start 的 timeoutMinutes(0=取 kv/内置默认 40)' },
33
- { key: 'auto_enabled', type: 'boolean', default: false, group: 'auto', labelKey: 'plugin.diag-bridge.settings.auto_enabled', label: '自动诊断', description: 'daq:sample 越限命中规则时自动发起深度诊断(每产线冷却 30 分钟)' },
34
- { key: 'results_enabled', type: 'boolean', default: false, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_enabled', label: '结果库总开关', description: '开启后提供 diag_results / diag_result_get / diag_result_annotate 三工具与结果面板;结果可见面 = 诊断 API Token 属主的可见面(平台级,不按产线隔离)' },
35
- { key: 'results_allow_annotate', type: 'boolean', default: false, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_allow_annotate', label: '允许元数据编辑', description: '允许 lead 角色编辑结果的 description/tags/title(PATCH;最终属主校验在 IDD 侧);只改元数据,不触产线' },
36
- { key: 'results_allow_worker', type: 'boolean', default: false, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_allow_worker', label: 'worker 可读', description: '允许 worker 角色使用读工具(默认仅 lead);编辑工具始终仅 lead' },
37
- { key: 'results_default_limit', type: 'number', default: 20, min: 1, max: 200, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_default_limit', label: '索引页大小', description: 'diag_results 缺省返回条数(1-200)' },
38
- { key: 'results_md_max_chars', type: 'number', default: 4000, min: 500, max: 50000, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_md_max_chars', label: '报告截断上限', description: 'diag_result_get include_md=true 时 report.md 最大字符数(超出截断并给完整 URL)' },
39
- ],
40
- setup(ctx) {
41
- // 运行时追加分区与字段(ctx.config.defineGroup / defineField):
42
- // 字段 key 与声明式完全等价(plugins.diag-bridge.auto_rules),已保存的值不丢;
43
- // 这里演示「插件在 setup 里按条件调用平台 API 扩展自己的配置面」。
44
- ctx.config.defineGroup({
45
- id: 'rules',
46
- labelKey: 'plugin.diag-bridge.group.rules',
47
- label: '自动诊断规则',
48
- description: 'JSON 规则;命中即触发自动诊断(仅 auto_enabled=true 时生效)',
49
- order: 450,
50
- })
51
- ctx.config.defineField({
52
- key: 'auto_rules',
53
- type: 'string',
54
- default: '',
55
- group: 'rules',
56
- labelKey: 'plugin.diag-bridge.settings.auto_rules',
57
- label: '自动诊断规则',
58
- description: 'JSON: {"节点id":{"op":"gt|lt","value":数值}};命中即触发自动诊断(空=无规则)',
59
- })
60
- // 轮询器(15s)+ setup 重水化:恢复对 kv 中 running run 的跟踪(热重载安全)
61
- const sweep = () => sweepOnce(ctx).catch(err => ctx.logger.warn(`诊断轮询异常(继续): ${err?.message ?? err}`))
62
- ctx.timer.setInterval(sweep, 15000)
63
- sweep()
64
-
65
- // 自动诊断(默认关):daq:sample 命中规则 → 冷却 30min → 同管线发起(source:'auto')
66
- ctx.hooks.on('daq:sample', (s) => {
67
- try {
68
- if (!autoEnabledOf(ctx)) return
69
- if (!s || typeof s.value !== 'number' || !s.nodeId || !s.lineId) return
70
- const rule = parseAutoRules(autoRulesOf(ctx))[s.nodeId]
71
- if (!rule || typeof rule?.value !== 'number' || !['gt', 'lt'].includes(rule.op)) return
72
- const hit = rule.op === 'lt' ? s.value < rule.value : s.value > rule.value
73
- if (!hit) return
74
- const line = s.lineId
75
- const last = Number(ctx.kv.get(`cooldown:${line}`)) || 0
76
- if (Date.now() - last < AUTO_COOLDOWN_MS) return
77
- const now = Date.now()
78
- ctx.kv.set(`cooldown:${line}`, now) // 先占冷却,防同窗连发
79
- // 首拍竞态:开线后首个越限样本即刻触发,快照查询早于 tsdb 首次刷盘(TSDB_FLUSH_MS=500ms)
80
- // → 时窗查空失败。空窗失败定向重试(≤3 次、间隔 5s,总窗 ≤20s);冷却不回滚,仍只此一次派发。
81
- const question = `${line} 产线该时窗数据深度根因诊断(自动触发:节点 ${s.nodeId} 采样值 ${s.value} ${rule.op === 'lt' ? '<' : '>'} ${rule.value})`
82
- const attempt = (n) => {
83
- startDiagnosis(ctx, {
84
- line,
85
- fromMs: now - 60 * MIN,
86
- toMs: now,
87
- question,
88
- scene: `${line}_auto`,
89
- source: 'auto',
90
- }).then((r) => {
91
- if (r.ok) ctx.logger.warn(`自动诊断已发起:产线 ${line} runId=${r.runId}(节点 ${s.nodeId}=${s.value} 越限)`)
92
- else if (n < 3 && String(r.error ?? '').includes('无数采样本')) setTimeout(() => attempt(n + 1), 5000)
93
- else ctx.logger.warn(`自动诊断发起失败(产线 ${line}): ${r.error}`)
94
- }).catch(() => {})
95
- }
96
- attempt(0)
97
- }
98
- catch { /* 钩子内绝不抛 */ }
99
- })
100
-
101
- // 工具 ×2
102
- ctx.omp.registerTool({
103
- name: 'diag_run',
104
- label: '发起分析',
105
- description: '发起 IDD 后台分析任务(提交即返 task_id,无需等待;完成后平台自动回执本频道,空闲时也可 diag_status 查询)。analysis_intent 选择分析模式,每种意图由 IDD 侧对应 skill 协议执行(harness 在 IDD 内,分析业务与平台解耦):deep-diagnosis=深度根因诊断(9 阶段管线,最重);stability-report=产线稳定报告(评分+等级+建议,零根因);quick-screen=快速筛查(越限/漂移/冻结异常清单+HTML 看板,最快);doe-analysis=DOE 试验分析(因子效应/显著性/最优工况);experience-mining=调参经验挖掘(结构化经验条目,供知识库沉淀);optimizer-campaign=优化战役(寻优迭代+推荐工况,最重)。数据来源三选一:① line+时间窗(导出产线 DAQ 快照);② data_path:诊断服务内已有数据文件;③ export_id(推荐):daq_export 导出的数据集。',
106
- parameters: {
107
- type: 'object',
108
- properties: {
109
- line: { type: 'string', description: '产线 id(与 data_path/export_id 三选一;export/data_path 模式可填 any)' },
110
- export_id: { type: 'string', description: 'daq_export 返回的数据集 id(daqexp-…):上传 manifest+全部节点全量时序 CSV 做多文件分析' },
111
- data_path: { type: 'string', description: '可选:诊断服务内数据文件相对路径(如 data/aw-snapshots/x.csv;跳过快照导出,适合离线数据分析)' },
112
- from_ms: { type: 'number', description: '窗口起点(epoch 毫秒;仅 line 模式;缺省 now-60min)' },
113
- to_ms: { type: 'number', description: '窗口终点(epoch 毫秒;仅 line 模式;缺省 now)' },
114
- question: { type: 'string', description: '分析问题(缺省按意图自动生成情景化提示词;业务要求特殊时才覆写)' },
115
- analysis_intent: {
116
- type: 'string',
117
- enum: ['deep-diagnosis', 'stability-report', 'quick-screen', 'doe-analysis', 'experience-mining', 'optimizer-campaign'],
118
- description: '分析模式(缺省 deep-diagnosis)。按业务情景选择:要根因→deep-diagnosis;要运行健康度评分/等级→stability-report;要快速异常清单→quick-screen;要多因子试验数据→doe-analysis;要沉淀调参经验→experience-mining;要寻优推荐工况→optimizer-campaign',
119
- },
120
- scene: { type: 'string', description: '场景名(缺省 <line>_diag);场景为自由标签,分析管线按数据特征自适应' },
121
- mode: { type: 'string', enum: ['async', 'sync'], description: 'async(缺省)=提交即返后台跑;sync=同步等待完成(最长 sync_timeout_s,超时自动转后台继续)' },
122
- sync_timeout_s: { type: 'number', description: 'sync 模式等待上限秒(缺省 240,上限 540)' },
123
- harness: { type: 'string', description: '分析引擎按次覆盖(omp/claude/mock;缺省用插件设置)' },
124
- model: { type: 'string', description: '可选:引擎模型按次覆盖(合法性由 IDD 校验,不合法返回 400 说明)' },
125
- permission_mode: { type: 'string', description: '可选:引擎权限模式按次覆盖(合法性由 IDD 校验)' },
126
- },
127
- required: [],
128
- },
129
- roles: ['lead', 'worker'],
130
- handler: async (args, agent) => {
131
- try {
132
- const line = String(args.line ?? '').trim()
133
- const dataPathDirect = String(args.data_path ?? '').trim()
134
- const exportId = String(args.export_id ?? '').trim()
135
- if (!line && !dataPathDirect && !exportId) return { text: 'line / data_path / export_id 至少给一个。', isError: true }
136
- // 产线模式按调用者可读面授权(插件工具不走工业工具 grant/verify 两道闸;
137
- // 此前任意 Agent 可对任意产线导出快照做诊断,构成跨线数据读取)。
138
- if (line && agent?.agentId) {
139
- try {
140
- const scope = await ctx.services.get('agentScope')
141
- const readable = scope.readableLines(agent.agentId)
142
- if (!readable.includes(line)) {
143
- return {
144
- text: `产线「${line}」不在你的可读范围。你的可读产线:${readable.length ? readable.join(', ') : '(无)'}。如需诊断该产线,请让用户绑定该产线节点/把频道绑定到该产线,或改用上述产线重试。`,
145
- isError: true,
146
- }
147
- }
148
- }
149
- catch (err) {
150
- // fail-closed:无法确认授权时不读取产线数据
151
- return { text: `产线授权校验不可用,已拒绝本次分析:${err?.message ?? err}。请联系管理员检查插件运行时服务。`, isError: true }
152
- }
153
- }
154
- const toMs = Number(args.to_ms) > 0 ? Number(args.to_ms) : Date.now()
155
- const fromMs = Number(args.from_ms) > 0 ? Number(args.from_ms) : toMs - 60 * MIN
156
- if (fromMs >= toMs) return { text: 'from_ms 必须小于 to_ms。', isError: true }
157
-
158
- // 数据集模式:上传 manifest + 全部节点 CSV(manifest 首位,IDD multi 模式)
159
- let dataPaths
160
- let via = ''
161
- let manifest = null
162
- if (exportId) {
163
- const up = await uploadExportDataset(ctx, exportId)
164
- if (!up.ok) return { text: up.error, isError: true }
165
- dataPaths = up.dataPaths
166
- manifest = up.manifest
167
- const nodeCount = Array.isArray(manifest?.nodes) ? manifest.nodes.length : dataPaths.length - 1
168
- const win = manifest?.window ? `窗口 ${String(manifest.window.from ?? '').slice(0, 16)} ~ ${String(manifest.window.to ?? '').slice(0, 16)}` : ''
169
- via = `数据集 ${up.folder}(${nodeCount} 节点全量时序 + manifest,${win})`
170
- }
171
-
172
- // 问题缺省:按数据来源自动组题(export 模式带产线与场景备注)
173
- let question = args.question ? String(args.question) : ''
174
- if (!question) {
175
- if (manifest) {
176
- const lineNames = (Array.isArray(manifest.lines) ? manifest.lines : []).map(l => l?.lineName || l?.lineId).filter(Boolean).join('、') || line || 'offline'
177
- const note = manifest.note ? `;场景备注:${String(manifest.note).slice(0, 200)}` : ''
178
- question = `${lineNames} 产线数据集深度根因诊断(${via})${note}`
179
- }
180
- else {
181
- question = `${line || 'offline-data'} 产线该时窗数据深度根因诊断`
182
- }
183
- }
184
- const r = await startDiagnosis(ctx, {
185
- line: line || 'offline-data',
186
- fromMs, toMs,
187
- question,
188
- scene: args.scene ? String(args.scene) : '',
189
- source: 'agent',
190
- dataPath: dataPathDirect || undefined,
191
- dataPaths,
192
- harness: args.harness ? String(args.harness) : undefined,
193
- analysisIntent: args.analysis_intent ? String(args.analysis_intent) : undefined,
194
- model: args.model ? String(args.model) : undefined,
195
- permissionMode: args.permission_mode ? String(args.permission_mode) : undefined,
196
- // 归因(分发层注入的调用方上下文):诊断完成后平台据此把结果回执给发起 Agent
197
- agentId: agent?.agentId ?? '',
198
- channelId: agent?.channelId ?? '',
199
- })
200
- if (!r.ok) return { text: r.error, isError: true }
201
- if (!via) via = dataPathDirect ? `离线数据 ${r.csvPath}` : `产线 ${line} 窗口 ${new Date(fromMs).toISOString()} ~ ${new Date(toMs).toISOString()} 快照 ${r.rows} 行 × ${r.nodes} 节点`
202
-
203
- // 同步模式:轮询到终态或超时(超时自动转后台,sweep 完成仍会回执)
204
- if (String(args.mode ?? '').toLowerCase() === 'sync') {
205
- const base = baseOf(ctx)
206
- const timeoutS = Math.min(Math.max(Number(args.sync_timeout_s) || 240, 10), 540)
207
- if (base) {
208
- const deadline = Date.now() + timeoutS * 1000
209
- let lastPollError = ''
210
- while (Date.now() < deadline) {
211
- await new Promise(resolve => setTimeout(resolve, 5000))
212
- let st = null
213
- try {
214
- st = (await jget(ctx, `${base}/api/diagnosis/tasks/${encodeURIComponent(r.runId)}`, 8000)).data ?? null
215
- lastPollError = ''
216
- }
217
- catch (err) {
218
- lastPollError = String(err?.message ?? err)
219
- // 鉴权失败/任务不存在:继续等只会空耗到超时,直接如实返回
220
- if (/HTTP (401|403|404)/.test(lastPollError)) {
221
- return { text: `同步等待中止 task_id=${r.runId}:${lastPollError}。任务可能仍在后台运行;修复后可用 diag_status(run_id=${r.runId}) 查询,完成后平台仍会回执。`, isError: true }
222
- }
223
- }
224
- const status = String(st?.status ?? r.status ?? 'running')
225
- if (status === 'completed') {
226
- // 已同步拿到结果,认领回执防 sweep 补送重复
227
- try {
228
- const notify = await ctx.services.get('notify')
229
- await notify?.claim?.('diag_run', r.runId)
230
- }
231
- catch { /* 未接线时忽略 */ }
232
- const rep = st?.result?.report_md_path ?? '-'
233
- return { text: `✅ 同步诊断完成 task_id=${r.runId}(${via})\n评分=${st?.result?.score ?? '-'} 结论=${st?.result?.verdict ?? '-'}\nreport_md_path=${rep}\n总结:${String(st?.result?.summary ?? '').slice(0, 800)}\n\n入库知识库:调用 rag-bridge 的 kb_agent(mode=async,prompt=「请把这份深度诊断报告入库到 aw-industrial 知识库:读取文件 ${rep} 的全文内容并入库,完成后报告 doc_path 与分块数。标签:诊断、深度诊断。」),完成后 kb_agent_status(task_id) 查询。` }
234
- }
235
- if (status === 'failed' || status === 'stopped') {
236
- return { text: `分析任务 ${r.runId} 以 ${status} 结束:${st?.result?.error ?? '详见 diag_status'}`, isError: true }
237
- }
238
- }
239
- return { text: `已发起${intentLabel(r.intent)}任务 task_id=${r.runId}(${via})。同步等待 ${timeoutS}s 超时,分析仍在后台运行${lastPollError ? `(最近一次轮询失败:${lastPollError})` : ''}——完成后平台会自动把结果回执到你的信箱并唤醒你;也可 diag_status(run_id=${r.runId}) 主动查询。注入的分析问题:${r.question}` }
240
- }
241
- }
242
- return {
243
- text: `已发起「${intentLabel(r.intent)}」异步分析任务 task_id=${r.runId}(${via})。IDD 在后台以对应 skill 协议执行,Channel 无需等待——可继续其他任务;完成后平台会自动把结果回执到你的信箱并唤醒你(空闲时也可用 diag_status(run_id=${r.runId}) 主动查询;完成后按指引调 kb_agent 把报告入库知识库)。注入的分析问题:${r.question}`,
244
- }
245
- }
246
- catch (err) {
247
- return { text: `diag_run 异常: ${err?.message ?? err}`, isError: true }
248
- }
249
- },
250
- })
251
-
252
- ctx.omp.registerTool({
253
- name: 'diag_status',
254
- label: '分析任务状态',
255
- description: '查询后台分析任务进度:传 run_id 看单条实时状态(完成时含评分/结论/总结/报告路径),不传看最近 5 条概要。适用 diag_run 发起的全部意图(深度诊断/产线报告/快速筛查/DOE/经验挖掘/优化战役)。',
256
- parameters: {
257
- type: 'object',
258
- properties: {
259
- run_id: { type: 'string', description: '诊断 runId(缺省列最近 5 条概要)' },
260
- },
261
- },
262
- roles: ['lead', 'worker'],
263
- handler: async (args) => {
264
- try {
265
- const id = String(args.run_id ?? '').trim()
266
- if (!id) {
267
- const runs = kvRuns(ctx)
268
- .sort((a, b) => (b.meta?.createdAt ?? 0) - (a.meta?.createdAt ?? 0))
269
- .slice(0, 5)
270
- if (!runs.length) return { text: '暂无诊断记录。用 diag_run 发起一次深度诊断。' }
271
- const lines = runs.map(({ id: rid, meta }) =>
272
- `- ${rid} | ${meta.line} | [${intentLabel(meta.intent ?? 'deep-diagnosis')}] ${meta.status}${meta.stored ? '(已入库)' : ''} | ${meta.source ?? 'agent'} | ${meta.createdAt ? new Date(meta.createdAt).toISOString() : '?'} | ${meta.question ?? ''}`)
273
- return { text: `最近 ${runs.length} 条诊断:\n${lines.join('\n')}` }
274
- }
275
- const meta = ctx.kv.get(runKey(id))
276
- const base = baseOf(ctx)
277
- let st = null
278
- let remoteError = ''
279
- if (base) {
280
- try {
281
- // v2.1:走任务管理面 /tasks/:id——result.report_md_path 是绝对路径,
282
- // 可直接交给 kb_agent 读取入库(旧 /status 的相对路径会让 kb_agent 找不到文件)
283
- st = (await jget(ctx, `${base}/api/diagnosis/tasks/${encodeURIComponent(id)}`, 8000)).data ?? null
284
- if (st && st.result) {
285
- st = {
286
- ...st,
287
- engineStatus: st.status,
288
- score: st.result.score ?? null,
289
- judge_verdict: st.result.verdict ?? null,
290
- summary: st.result.summary ?? '',
291
- report_path: st.result.report_md_path ?? st.report_path ?? null,
292
- error_message: st.result.error ?? st.error_message ?? null,
293
- }
294
- }
295
- }
296
- catch (err) {
297
- // 保留原始错误(404=任务不存在 / 401=token / 网络不可达),不再一律吞成"不可达"
298
- remoteError = String(err?.message ?? err)
299
- }
300
- }
301
- if (!st && !meta) {
302
- return { text: `诊断 ${id} 不存在或诊断服务不可达${remoteError ? `(${remoteError})` : ''}。`, isError: true }
303
- }
304
- const status = st?.engineStatus || st?.status || meta?.status || 'unknown'
305
- // 终态经轮询面亲自看到 → 认领结果所有权,取消平台的事后补送(先到先得)
306
- if (['completed', 'failed', 'stopped'].includes(status)) {
307
- const notify = await ctx.services.get('notify').catch(() => null)
308
- notify?.claim?.('diag_run', id)
309
- }
310
- const parts = [
311
- `run_id=${id}`,
312
- `name=${st?.name ?? meta?.name ?? '?'}`,
313
- `产线=${meta?.line ?? st?.scene_name ?? '?'}`,
314
- `意图=${meta?.intent ?? 'deep-diagnosis'}(${intentLabel(meta?.intent ?? 'deep-diagnosis')})`,
315
- `状态=${status}`,
316
- `来源=${meta?.source ?? '?'}`,
317
- ]
318
- if (meta?.fromMs && meta?.toMs) parts.push(`窗口=${new Date(meta.fromMs).toISOString()} ~ ${new Date(meta.toMs).toISOString()}`)
319
- if (meta?.question) parts.push(`问题=${meta.question}`)
320
- if (status === 'completed') {
321
- const reportPath = st?.report_path ?? meta?.reportPath ?? '-'
322
- parts.push(`评分=${st?.score ?? meta?.score ?? '-'}`)
323
- parts.push(`结论=${st?.judge_verdict ?? meta?.verdict ?? '-'}`)
324
- if (st?.summary) parts.push(`总结=${String(st.summary).slice(0, 600)}`)
325
- parts.push(`report_md_path=${reportPath}`)
326
- parts.push('')
327
- parts.push('✅ 分析完成,报告已就绪。入库知识库请调用 rag-bridge 的 kb_agent 工具:')
328
- parts.push(` mode=async, prompt=「请把这份分析报告入库到 aw-industrial 知识库:读取文件 ${reportPath} 的全文内容并入库(写盘+索引),完成后报告 doc_path 与分块数。标签:分析、${intentLabel(meta?.intent ?? 'deep-diagnosis')}。」`)
329
- parts.push('提交后频道无需等待,空闲时用 kb_agent_status(task_id) 查询入库结果。')
330
- }
331
- if (status === 'failed' || status === 'stopped') parts.push(`错误=${st?.error_message ?? meta?.error ?? '-'}`)
332
- if (!st) {
333
- parts.push(`(远端查询失败:${remoteError || '诊断服务不可达'};以上为本地记录)`)
334
- if (remoteError.includes('HTTP 404') && ['queued', 'running'].includes(String(meta?.status))) {
335
- parts.push('远端任务已不存在(可能被 IDD 清理或服务重启);sweep 将在连续 3 轮确认后把本地状态收敛为 failed 并回执,不会永久占用产线单飞锁。')
336
- }
337
- }
338
- return { text: parts.join('\n') }
339
- }
340
- catch (err) {
341
- return { text: `diag_status 异常: ${err?.message ?? err}`, isError: true }
342
- }
343
- },
344
- })
345
-
346
- // ── 工具 ×3:分析结果库(§16;默认关闭,results_enabled 开启) ────────────
347
- // 可见性注意:结果库按 IDD Token 属主聚合,平台内不按产线隔离 —— 所以
348
- // 读面默认只给 lead(worker 可在 handler 内按设置热放开)、写面双闸门(设置 + lead + IDD 属主校验)。
349
- // roles 必须在注册期就包含 worker:注册后 roles 固化,设置热改不会重注册工具;
350
- // 因此 worker 的开关判断放在 handler 内(每次调用实时读设置)。
351
- const denyWorkerIfDisabled = agent => agent?.role === 'worker' && !resultsAllowWorkerOf(ctx)
352
-
353
- ctx.omp.registerTool({
354
- name: 'diag_results',
355
- label: '检索分析结果',
356
- description: '检索 IDD 分析结果库的摘要索引(全量历史分析,不限本平台发起):按关键词/意图/标签/场景/分数过滤,返回 result_id、标题、意图、评分与结论、时间与标签。先用本工具拿到 result_id,再用 diag_result_get 看完整详情。brief 模式(缺省)单卡很省 token。',
357
- parameters: {
358
- type: 'object',
359
- properties: {
360
- q: { type: 'string', description: '关键词(匹配 title/description/summary/scene/tags,大小写不敏感)' },
361
- intent: { type: 'string', enum: ['deep-diagnosis', 'stability-report', 'quick-screen', 'doe-analysis', 'experience-mining', 'optimizer-campaign'], description: '按分析模式过滤' },
362
- category_id: { type: 'string', description: '按结果分类 id 过滤(见 /results/categories)' },
363
- tag: { type: 'string', description: '按标签精确匹配' },
364
- scene: { type: 'string', description: '按场景名精确匹配' },
365
- harness: { type: 'string', description: '按执行引擎过滤(如 omp/claude/mock)' },
366
- min_score: { type: 'number', description: 'score >= min_score(稳定性报告 0-100;深度诊断为 Judge 分)' },
367
- sort: { type: 'string', enum: ['created_desc', 'created_asc', 'score_desc'], description: '排序(缺省 created_desc)' },
368
- limit: { type: 'number', description: '每页条数(1-200,缺省取插件设置)' },
369
- offset: { type: 'number', description: '分页偏移(缺省 0)' },
370
- brief: { type: 'boolean', description: '省 token 模式(缺省 true:无 key_numbers,display_text 截 160 字符)' },
371
- },
372
- },
373
- roles: ['lead', 'worker'],
374
- handler: async (args, agent) => {
375
- try {
376
- if (denyWorkerIfDisabled(agent)) {
377
- return { text: 'diag_results 当前仅对 lead 角色开放(管理员可在插件设置打开「worker 可读」)。', isError: true }
378
- }
379
- if (!resultsEnabledOf(ctx)) {
380
- return { text: '分析结果库未启用:请在 系统设置→运行配置→插件→diag-bridge 打开「结果库总开关」。', isError: true }
381
- }
382
- const probe = await probeResults(ctx)
383
- if (!probe.ok) return { text: probe.reason, isError: true }
384
- const base = baseOf(ctx)
385
- const params = new URLSearchParams()
386
- for (const [key, value] of Object.entries({
387
- q: args.q, intent: args.intent, category_id: args.category_id, tag: args.tag,
388
- scene: args.scene, harness: args.harness,
389
- min_score: args.min_score, sort: args.sort,
390
- limit: args.limit ?? resultsDefaultLimitOf(ctx), offset: args.offset,
391
- brief: args.brief === undefined ? true : args.brief,
392
- })) {
393
- if (value !== undefined && value !== null && String(value) !== '') params.set(key, String(value))
394
- }
395
- const body = await jget(ctx, `${base}/api/external/v1/results?${params.toString()}`)
396
- const d = body.data ?? {}
397
- const cards = d.results ?? []
398
- if (!cards.length) return { text: `无匹配结果(total=${d.total ?? 0})。放宽过滤或用 q 换个关键词。` }
399
- const showKn = briefOf(args) === false
400
- const lines = cards.map(c =>
401
- `- ${c.result_id} | ${c.intent} | ${c.score ?? '-'}分 ${c.verdict ?? ''} | ${c.created_at ?? '?'} | ${c.title}${(c.tags ?? []).length ? ` [${c.tags.join(',')}]` : ''}`
402
- + `\n ${String(c.display_text ?? '').slice(0, showKn ? 400 : 160)}`
403
- + (showKn && c.key_numbers ? `\n key_numbers=${JSON.stringify(c.key_numbers)}` : ''))
404
- return { text: [`分析结果 total=${d.total} limit=${d.limit} offset=${d.offset} 本页 ${cards.length} 条(brief=${briefOf(args)}):`, ...lines, '', '按 id 取完整详情 → diag_result_get(result_id)。'].join('\n') }
405
- }
406
- catch (err) {
407
- return { text: `diag_results 失败: ${mapResultsError(err, baseOf(ctx))}`, isError: true }
408
- }
409
- },
410
- })
411
-
412
- ctx.omp.registerTool({
413
- name: 'diag_result_get',
414
- label: '分析结果详情',
415
- description: '按 result_id 取一条分析结果的完整详情:结论摘要、关键数字(可溯源到工件字段)、产物清单(报告/HTML 的 API URL)、运行状态。缺省不带 report.md 全文(只给 URL);include_md=true 时携带全文但按插件设置截断保护上下文。',
416
- parameters: {
417
- type: 'object',
418
- properties: {
419
- result_id: { type: 'string', description: '结果 id(diag_results 返回的 result_id = run_id)' },
420
- include_md: { type: 'boolean', description: '携带 report.md 全文(缺省 false 只给 URL)' },
421
- md_max_chars: { type: 'number', description: '本次调用的 md 截断上限(覆盖插件设置;500-50000)' },
422
- },
423
- },
424
- required: ['result_id'],
425
- roles: ['lead', 'worker'],
426
- handler: async (args, agent) => {
427
- try {
428
- if (denyWorkerIfDisabled(agent)) {
429
- return { text: 'diag_result_get 当前仅对 lead 角色开放(管理员可在插件设置打开「worker 可读」)。', isError: true }
430
- }
431
- if (!resultsEnabledOf(ctx)) {
432
- return { text: '分析结果库未启用:请在 系统设置→运行配置→插件→diag-bridge 打开「结果库总开关」。', isError: true }
433
- }
434
- const probe = await probeResults(ctx)
435
- if (!probe.ok) return { text: probe.reason, isError: true }
436
- const id = String(args.result_id ?? '').trim()
437
- if (!id) return { text: 'result_id 必填(先用 diag_results 检索)。', isError: true }
438
- const base = baseOf(ctx)
439
- const wantMd = args.include_md === true
440
- const cap = Math.min(50000, Math.max(500, Math.round(Number(args.md_max_chars) || resultsMdMaxOf(ctx))))
441
- const body = await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=${wantMd ? 'true' : 'false'}`)
442
- const d = body.data ?? {}
443
- const sj = d.summary_json ?? {}
444
- const meta = d.metadata ?? {}
445
- const parts = [
446
- `result_id=${d.result_id ?? id}`,
447
- `标题=${d.title ?? '-'} 意图=${d.intent ?? '-'} 评分=${d.score ?? '-'} 结论=${d.verdict ?? '-'}`,
448
- `摘要=${String(d.summary ?? '').slice(0, 800)}`,
449
- ]
450
- const kn = sj.key_numbers
451
- if (kn && Object.keys(kn).length) parts.push(`关键数字=${JSON.stringify(kn)}`)
452
- if (sj.sources) parts.push(`数字溯源=summary_json.sources(每个数字钉到 工件路径#字段)`)
453
- const artifacts = meta.artifacts ?? []
454
- if (artifacts.length) parts.push(`工件 ${artifacts.length} 项:${artifacts.slice(0, 12).map(a => a.path ?? a).join(', ')}${artifacts.length > 12 ? ' …' : ''}`)
455
- const htmls = d.files?.html ?? []
456
- if (htmls.length) parts.push(`HTML 报告 ${htmls.length} 份(在线查看需带 ?token=<诊断API Token>):${htmls.slice(0, 4).map(h => absoluteUrl(base, h.url)).join(', ')}`)
457
- parts.push(`报告=${absoluteUrl(base, d.report_md_url)}`)
458
- if (d.run) parts.push(`run=${d.run.run_id} ${d.run.status}${d.run.session_resumable ? '(可续聊)' : ''}`)
459
- if (d.derived) parts.push('(本条为按需派生记录:行缺失时详情接口即时生成并标注 derived=true)')
460
- if (wantMd && typeof d.report_md === 'string' && d.report_md.length) {
461
- const md = d.report_md
462
- if (md.length > cap) parts.push(`\nreport.md(截断 ${cap}/${md.length} 字符,全文用上面的报告 URL):\n${md.slice(0, cap)}\n…[已截断]`)
463
- else parts.push(`\nreport.md:\n${md}`)
464
- }
465
- const edited = d.description
466
- if (edited) parts.push(`\n用户描述=${String(edited).slice(0, 400)}`)
467
- if ((d.tags ?? []).length) parts.push(`标签=[${d.tags.join(',')}]`)
468
- parts.push('\n补充描述/标签 → diag_result_annotate(result_id, description, tags)。')
469
- return { text: parts.filter(Boolean).join('\n') }
470
- }
471
- catch (err) {
472
- return { text: `diag_result_get 失败: ${mapResultsError(err, baseOf(ctx))}`, isError: true }
473
- }
474
- },
475
- })
476
-
477
- ctx.omp.registerTool({
478
- name: 'diag_result_annotate',
479
- label: '编辑结果元数据',
480
- description: '编辑一条分析结果的元数据(description ≤2000 / tags ≤16×≤40 / title ≤200),用于沉淀"这条结果代表什么、何时复用"。只改元数据、不触产线;需插件设置「允许元数据编辑」且调用者是 lead;属主校验最终在 IDD 侧(非属主 403 如实回执)。',
481
- parameters: {
482
- type: 'object',
483
- properties: {
484
- result_id: { type: 'string', description: '结果 id(diag_results 返回)' },
485
- description: { type: ['string', 'null'], description: '结果描述(≤2000 字符;null=清除回落自动摘要)' },
486
- tags: { type: 'array', items: { type: 'string' }, description: '标签数组(≤16 项,单项 ≤40 字符)' },
487
- title: { type: 'string', description: '标题(≤200 字符)' },
488
- },
489
- },
490
- required: ['result_id'],
491
- roles: ['lead'],
492
- handler: async (args) => {
493
- try {
494
- if (!resultsEnabledOf(ctx)) {
495
- return { text: '分析结果库未启用:请在 系统设置→运行配置→插件→diag-bridge 打开「结果库总开关」。', isError: true }
496
- }
497
- if (!resultsAllowAnnotateOf(ctx)) {
498
- return { text: '元数据编辑未开放:请管理员在插件设置打开「允许元数据编辑」(results_allow_annotate)。', isError: true }
499
- }
500
- const probe = await probeResults(ctx)
501
- if (!probe.ok) return { text: probe.reason, isError: true }
502
- const id = String(args.result_id ?? '').trim()
503
- if (!id) return { text: 'result_id 必填。', isError: true }
504
- const patch = {}
505
- let touched = 0
506
- if (args.description !== undefined) {
507
- patch.description = args.description
508
- touched += 1
509
- }
510
- if (args.tags !== undefined) {
511
- patch.tags = args.tags
512
- touched += 1
513
- }
514
- if (args.title !== undefined) {
515
- patch.title = args.title
516
- touched += 1
517
- }
518
- if (!touched) return { text: '至少给 description / tags / title 之一。', isError: true }
519
- const base = baseOf(ctx)
520
- await jpatch(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}`, patch)
521
- // 回读校验:PATCH 响应不含 display_text,重新 GET 详情确认落库;
522
- // IDD 详情接口不返回 display_text,按索引口径本地拼(description || summary)
523
- const detail = (await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=false`)).data ?? {}
524
- ctx.kv.set('diag.results.lastWrite', JSON.stringify({ at: Date.now(), resultId: id, fields: Object.keys(patch) }))
525
- const displayText = String(detail.description || detail.summary || '').slice(0, 160)
526
- return { text: `已更新 result_id=${id}:title=${detail.title ?? '-'}\ndescription=${String(detail.description ?? '(未改/为空)').slice(0, 200)}\ntags=[${(detail.tags ?? []).join(',')}]\n(display_text 现为:${displayText || '(空)'}…)。` }
527
- }
528
- catch (err) {
529
- return { text: `diag_result_annotate 失败: ${mapResultsError(err, baseOf(ctx))}`, isError: true }
530
- }
531
- },
532
- })
533
-
534
- function briefOf(args) {
535
- return args.brief === undefined ? true : Boolean(args.brief)
536
- }
537
-
538
- // 路由 ×3(挂 /api/plugins/diag-bridge/*)
539
- ctx.route('GET', '/health', async () => {
540
- const runs = kvRuns(ctx)
541
- const byStatus = {}
542
- for (const { meta } of runs) byStatus[meta?.status ?? 'unknown'] = (byStatus[meta?.status ?? 'unknown'] ?? 0) + 1
543
- const base = baseOf(ctx)
544
- let remote
545
- let tokenState = { configured: Boolean(diagTokenOf(ctx)), accepted: null }
546
- if (base) {
547
- try {
548
- // 连通性:/api/health 是公开端点(无鉴权),只回答「服务在不在」。
549
- const res = await ctx.http.get(`${base}/api/health`, { timeoutMs: 5000 })
550
- const body = await res.json().catch(() => null)
551
- remote = res.ok && body
552
- ? { status: body.status ?? 'ok', activeRuns: body.checks?.activeRuns ?? null }
553
- : { status: `HTTP ${res.status}`, activeRuns: null }
554
- // Token 有效性:/api/health 不校验鉴权,必须另打一个受保护端点,
555
- // 否则 token 配错时健康面依旧 ok —— 配置错误完全不可见(实测踩过)。
556
- if (tokenState.configured) {
557
- try {
558
- const auth = await ctx.http.get(`${base}/api/files/workspace`, {
559
- timeoutMs: 5000,
560
- headers: authHeadersOf(ctx),
561
- })
562
- if (auth.status === 401 || auth.status === 403) {
563
- tokenState = { configured: true, accepted: false, hint: authHint(auth.status, 'Token 被上游拒绝') }
564
- remote = { ...remote, auth: `HTTP ${auth.status}(鉴权被拒)`, hint: tokenState.hint }
565
- }
566
- else if (auth.ok) {
567
- tokenState = { configured: true, accepted: true }
568
- remote = { ...remote, auth: 'ok' }
569
- }
570
- else {
571
- remote = { ...remote, auth: `HTTP ${auth.status}` }
572
- }
573
- }
574
- catch (err) {
575
- remote = { ...remote, auth: 'unreachable', authError: String(err?.message ?? err) }
576
- }
577
- }
578
- else {
579
- tokenState = { configured: false, accepted: null }
580
- }
581
- }
582
- catch (err) {
583
- remote = { status: 'unreachable', error: String(err?.message ?? err) }
584
- }
585
- }
586
- else {
587
- remote = { status: 'bad_base_url' }
588
- }
589
- return {
590
- plugin: ctx.name,
591
- base: base ?? String(ctx.kv.get('diag.base_url') || DEFAULT_BASE),
592
- harness: harnessOf(ctx),
593
- auth: diagTokenOf(ctx) ? 'bearer' : 'anonymous',
594
- /** Token 配置态 + 实测是否被上游接受(401/403 → false;网络不通 → null) */
595
- token: tokenState,
596
- remote,
597
- runs: { total: runs.length, byStatus },
598
- }
599
- })
600
-
601
- ctx.route('GET', '/runs', () => ({
602
- success: true,
603
- runs: kvRuns(ctx)
604
- .sort((a, b) => (b.meta?.createdAt ?? 0) - (a.meta?.createdAt ?? 0))
605
- .map(({ id, meta }) => ({ runId: id, ...meta })),
606
- }))
607
-
608
- ctx.route('POST', '/snapshot', async (event) => {
609
- try {
610
- const b = event?.awBody ?? {}
611
- const line = String(b.line ?? '').trim()
612
- if (!line) return { success: false, error: 'line 必填(产线 id)' }
613
- const toMs = Number(b.to_ms) > 0 ? Number(b.to_ms) : Date.now()
614
- const fromMs = Number(b.from_ms) > 0 ? Number(b.from_ms) : toMs - 60 * MIN
615
- if (fromMs >= toMs) return { success: false, error: 'from_ms 必须小于 to_ms' }
616
- const snap = await snapshotCore(ctx, line, fromMs, toMs)
617
- if (!snap.ok) return { success: false, error: snap.error }
618
- return { success: true, csvPath: snap.csvPath, rows: snap.rows, nodes: snap.nodes, fromMs: snap.fromMs, toMs: snap.toMs }
619
- }
620
- catch (err) {
621
- return { success: false, error: String(err?.message ?? err) }
622
- }
623
- })
624
-
625
- // 报告查看(面板/前端):run_id → 诊断任务 → workspace run 目录 → report md 内容
626
- ctx.route('GET', '/report', async (event) => {
627
- try {
628
- const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
629
- const runId = String(u.searchParams.get('run_id') ?? '').trim()
630
- if (!runId) return { success: false, error: 'run_id 必填' }
631
- const base = baseOf(ctx)
632
- if (!base) return { success: false, error: 'diag.base_url 非法' }
633
- const st = (await jget(ctx, `${base}/api/diagnosis/tasks/${encodeURIComponent(runId)}`, 8000)).data ?? {}
634
- if (String(st.status ?? '') !== 'completed') {
635
- return { success: false, error: `诊断 ${runId} 状态 ${st.status ?? 'unknown'},报告未就绪` }
636
- }
637
- const ws = (await jget(ctx, `${base}/api/files/workspace`, 8000)).data ?? []
638
- const names = (Array.isArray(ws) ? ws : [])
639
- const meta = ctx.kv.get(runKey(runId)) ?? {}
640
- // 目录命名不统一(<ts>_<scene> 或 <ts>_<scene>_<runId>):runId → scene → 最新 逐级兜底
641
- const hit = names.find(e => String(e?.name ?? '').includes(runId))
642
- ?? names.find(e => meta?.scene && String(e?.name ?? '').toLowerCase().includes(String(meta.scene).toLowerCase()))
643
- ?? (names.length ? names.slice().sort((a, b) => Number(b?.created ?? 0) - Number(a?.created ?? 0))[0] : null)
644
- if (!hit) return { success: false, error: `诊断服务 workspace 未找到 ${runId} 的运行目录` }
645
- const rep = (await jget(ctx, `${base}/api/files/workspace/report/${encodeURIComponent(hit.name)}`, 15000)).data
646
- return { success: true, run_id: runId, run_name: hit.name, report: rep ?? null }
647
- }
648
- catch (err) {
649
- return { success: false, error: String(err?.message ?? err) }
650
- }
651
- })
652
-
653
- // ── 路由 ×4:分析结果库(§16;UI 面板同源,读写开关与工具一致) ────────────
654
- ctx.route('GET', '/results', async (event) => {
655
- try {
656
- if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
657
- const probe = await probeResults(ctx)
658
- if (!probe.ok) return { success: false, error: probe.reason }
659
- const base = baseOf(ctx)
660
- const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
661
- const params = new URLSearchParams()
662
- for (const key of ['q', 'intent', 'category_id', 'tag', 'scene', 'harness', 'min_score', 'sort', 'limit', 'offset', 'brief']) {
663
- const v = u.searchParams.get(key)
664
- if (v !== null && v !== '') params.set(key, v)
665
- }
666
- if (!params.has('limit')) params.set('limit', String(resultsDefaultLimitOf(ctx)))
667
- if (!params.has('brief')) params.set('brief', 'true')
668
- const body = await jget(ctx, `${base}/api/external/v1/results?${params.toString()}`)
669
- return { success: true, data: body.data }
670
- }
671
- catch (err) {
672
- return { success: false, error: mapResultsError(err, baseOf(ctx)) }
673
- }
674
- })
675
-
676
- ctx.route('GET', '/results/detail', async (event) => {
677
- try {
678
- if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
679
- const probe = await probeResults(ctx)
680
- if (!probe.ok) return { success: false, error: probe.reason }
681
- const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
682
- const id = String(u.searchParams.get('id') ?? '').trim()
683
- if (!id) return { success: false, error: 'id 必填(result_id = run_id)' }
684
- const includeMd = u.searchParams.get('include_md') === 'true' ? 'true' : 'false'
685
- const base = baseOf(ctx)
686
- const body = await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=${includeMd}`)
687
- const d = body.data ?? {}
688
- // D5 跨机语义:不向浏览器透出 IDD 主机绝对路径;报告/HTML 一律给可打开的 URL
689
- const safe = {
690
- ...d,
691
- metadata: d.metadata ? { ...d.metadata, workspace_path: undefined } : d.metadata,
692
- files: {
693
- ...(d.files ?? {}),
694
- html: (d.files?.html ?? []).map(h => ({ path: h.path, url: absoluteUrl(base, h.url) })),
695
- report_md_path: undefined,
696
- },
697
- report_md_url: absoluteUrl(base, d.report_md_url),
698
- }
699
- return { success: true, data: { ...safe, baseUrl: base, canAnnotate: resultsAllowAnnotateOf(ctx) && String(event?.awUser?.role ?? '') === 'admin' } }
700
- }
701
- catch (err) {
702
- return { success: false, error: mapResultsError(err, baseOf(ctx)) }
703
- }
704
- })
705
-
706
- ctx.route('PATCH', '/results/edit', async (event) => {
707
- try {
708
- if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
709
- if (!resultsAllowAnnotateOf(ctx)) return { success: false, error: '元数据编辑未开放(results_allow_annotate)' }
710
- // 写面角色闸门(文档 D3):UI 路由是用户面,要求 AW 管理员;
711
- // Agent 面走 diag_result_annotate 工具(lead-only + IDD 属主校验)。
712
- if (String(event?.awUser?.role ?? '') !== 'admin') {
713
- return { success: false, error: '元数据编辑需要管理员身份(当前账号角色不足)' }
714
- }
715
- const probe = await probeResults(ctx)
716
- if (!probe.ok) return { success: false, error: probe.reason }
717
- const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
718
- const id = String(u.searchParams.get('id') ?? '').trim()
719
- if (!id) return { success: false, error: 'id 必填(result_id = run_id)' }
720
- const b = event?.awBody ?? {}
721
- const patch = {}
722
- for (const key of ['description', 'tags', 'title']) {
723
- if (b[key] !== undefined) patch[key] = b[key]
724
- }
725
- if (!Object.keys(patch).length) return { success: false, error: '至少给 description/tags/title 之一' }
726
- const base = baseOf(ctx)
727
- await jpatch(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}`, patch)
728
- const detail = (await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=false`)).data ?? {}
729
- ctx.kv.set('diag.results.lastWrite', JSON.stringify({ at: Date.now(), resultId: id, fields: Object.keys(patch) }))
730
- return { success: true, data: { ...detail, baseUrl: base, canAnnotate: true } }
731
- }
732
- catch (err) {
733
- return { success: false, error: mapResultsError(err, baseOf(ctx)) }
734
- }
735
- })
736
-
737
- ctx.route('GET', '/results/categories', async () => {
738
- try {
739
- if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
740
- const probe = await probeResults(ctx)
741
- if (!probe.ok) return { success: false, error: probe.reason }
742
- const base = baseOf(ctx)
743
- const body = await jget(ctx, `${base}/api/external/v1/results/categories`)
744
- return { success: true, data: body.data }
745
- }
746
- catch (err) {
747
- return { success: false, error: mapResultsError(err, baseOf(ctx)) }
748
- }
749
- })
750
-
751
- ctx.logger.info('diag-bridge 就绪:工具 diag_run/diag_status/diag_results/diag_result_get/diag_result_annotate + 路由 health/runs/snapshot/report/results,轮询 15s 已启动')
752
- },
753
- }
1
+ /**
2
+ * 插件对象(settings / routes / tools / hooks)
3
+ * (由 server/plugins-builtin/diag-bridge/index.mjs 按职责拆出;内容逐行原文搬运)
4
+ */
5
+ import { AUTO_COOLDOWN_MS, DEFAULT_BASE, MIN } from './constants.mjs'
6
+ import { absoluteUrl, authHeadersOf, authHint, autoEnabledOf, autoRulesOf, baseOf, diagTokenOf, harnessOf, jget, jpatch, kvRuns, mapResultsError, parseAutoRules, probeResults, resultsAllowAnnotateOf, resultsAllowWorkerOf, resultsDefaultLimitOf, resultsEnabledOf, resultsMdMaxOf, runKey } from './helpers.mjs'
7
+ import { snapshotCore } from './snapshot.mjs'
8
+ import { startDiagnosis } from './diagnosis.mjs'
9
+ import { uploadExportDataset } from './exports.mjs'
10
+ import { sweepOnce } from './sweep.mjs'
11
+ import { intentLabel } from './intents.mjs'
12
+
13
+ export default {
14
+ name: 'diag-bridge',
15
+ version: '2.3.0',
16
+ description: '深度诊断桥接 v2.2:异步任务式深度诊断(提交即返 task_id)+ daq-export 数据集多文件分析(diag_run export_id= 全量时序+manifest)+ 同步等待模式(mode=sync)+ 完成自动回执 Channel(修复发起者身份丢失)+ 报告在线查看。完成后经 rag-bridge kb_agent 入库知识库。',
17
+ auth: 'user',
18
+ client: './client.mjs', // 前端面板(插件页注入;i18n 见 i18n.json)
19
+ // 插件配置分组(声明式):连接 / 执行 / 自动化 三个独立分区
20
+ configGroups: [
21
+ { id: 'default', labelKey: 'plugin.diag-bridge.group.conn', label: '诊断服务连接', description: '后端地址与鉴权 Token', order: 420 },
22
+ { id: 'run', labelKey: 'plugin.diag-bridge.group.run', label: '诊断执行参数', description: 'diag_run 的引擎与预算', order: 430 },
23
+ { id: 'auto', labelKey: 'plugin.diag-bridge.group.auto', label: '自动诊断', description: '越限自动发起诊断(DAQ 采样事件驱动)', order: 440, collapsed: true },
24
+ { id: 'results', labelKey: 'plugin.diag-bridge.group.results', label: '分析结果库', description: '对 IDD 结果库的检索/详情/元数据编辑(默认关闭;结果可见面 = Token 属主可见面)', order: 450, collapsed: true },
25
+ ],
26
+ // 插件设置声明(key 编址 plugins.diag-bridge.<key>;labelKey 解析 i18n.json)
27
+ settings: [
28
+ { key: 'base_url', type: 'string', default: DEFAULT_BASE, group: 'default', labelKey: 'plugin.diag-bridge.settings.base_url', label: '诊断服务地址', description: 'industrial-deep-diagnostic 后端(http/https,host 限 127.0.0.1/localhost);保存即热生效' },
29
+ { key: 'token', type: 'string', default: '', group: 'default', labelKey: 'plugin.diag-bridge.settings.token', label: '诊断 API Token', description: '诊断服务开启鉴权时(v4 起默认强制)的 API Token(Authorization: Bearer);空=匿名;保存即热生效' },
30
+ { key: 'harness', type: 'string', default: '', group: 'run', labelKey: 'plugin.diag-bridge.settings.harness', label: '诊断引擎', description: 'diag_run 用的引擎 id(omp/claude/mock 等;空=取 kv/omp);无 Anthropic key 的机器请用 omp' },
31
+ { key: 'max_turns', type: 'number', default: 0, min: 0, max: 2000, group: 'run', labelKey: 'plugin.diag-bridge.settings.max_turns', label: '诊断最大轮数', description: 'diagnosis/start 的 maxTurns(0=取 kv/内置默认 220)' },
32
+ { key: 'max_minutes', type: 'number', default: 0, min: 0, max: 720, group: 'run', labelKey: 'plugin.diag-bridge.settings.max_minutes', label: '诊断超时(分钟)', description: 'diagnosis/start 的 timeoutMinutes(0=取 kv/内置默认 40)' },
33
+ { key: 'auto_enabled', type: 'boolean', default: false, group: 'auto', labelKey: 'plugin.diag-bridge.settings.auto_enabled', label: '自动诊断', description: 'daq:sample 越限命中规则时自动发起深度诊断(每产线冷却 30 分钟)' },
34
+ { key: 'results_enabled', type: 'boolean', default: false, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_enabled', label: '结果库总开关', description: '开启后提供 diag_results / diag_result_get / diag_result_annotate 三工具与结果面板;结果可见面 = 诊断 API Token 属主的可见面(平台级,不按产线隔离)' },
35
+ { key: 'results_allow_annotate', type: 'boolean', default: false, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_allow_annotate', label: '允许元数据编辑', description: '允许 lead 角色编辑结果的 description/tags/title(PATCH;最终属主校验在 IDD 侧);只改元数据,不触产线' },
36
+ { key: 'results_allow_worker', type: 'boolean', default: false, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_allow_worker', label: 'worker 可读', description: '允许 worker 角色使用读工具(默认仅 lead);编辑工具始终仅 lead' },
37
+ { key: 'results_default_limit', type: 'number', default: 20, min: 1, max: 200, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_default_limit', label: '索引页大小', description: 'diag_results 缺省返回条数(1-200)' },
38
+ { key: 'results_md_max_chars', type: 'number', default: 4000, min: 500, max: 50000, group: 'results', labelKey: 'plugin.diag-bridge.settings.results_md_max_chars', label: '报告截断上限', description: 'diag_result_get include_md=true 时 report.md 最大字符数(超出截断并给完整 URL)' },
39
+ ],
40
+ setup(ctx) {
41
+ // 运行时追加分区与字段(ctx.config.defineGroup / defineField):
42
+ // 字段 key 与声明式完全等价(plugins.diag-bridge.auto_rules),已保存的值不丢;
43
+ // 这里演示「插件在 setup 里按条件调用平台 API 扩展自己的配置面」。
44
+ ctx.config.defineGroup({
45
+ id: 'rules',
46
+ labelKey: 'plugin.diag-bridge.group.rules',
47
+ label: '自动诊断规则',
48
+ description: 'JSON 规则;命中即触发自动诊断(仅 auto_enabled=true 时生效)',
49
+ order: 450,
50
+ })
51
+ ctx.config.defineField({
52
+ key: 'auto_rules',
53
+ type: 'string',
54
+ default: '',
55
+ group: 'rules',
56
+ labelKey: 'plugin.diag-bridge.settings.auto_rules',
57
+ label: '自动诊断规则',
58
+ description: 'JSON: {"节点id":{"op":"gt|lt","value":数值}};命中即触发自动诊断(空=无规则)',
59
+ })
60
+ // 轮询器(15s)+ setup 重水化:恢复对 kv 中 running run 的跟踪(热重载安全)
61
+ const sweep = () => sweepOnce(ctx).catch(err => ctx.logger.warn(`诊断轮询异常(继续): ${err?.message ?? err}`))
62
+ ctx.timer.setInterval(sweep, 15000)
63
+ sweep()
64
+
65
+ // 自动诊断(默认关):daq:sample 命中规则 → 冷却 30min → 同管线发起(source:'auto')
66
+ ctx.hooks.on('daq:sample', (s) => {
67
+ try {
68
+ if (!autoEnabledOf(ctx)) return
69
+ if (!s || typeof s.value !== 'number' || !s.nodeId || !s.lineId) return
70
+ const rule = parseAutoRules(autoRulesOf(ctx))[s.nodeId]
71
+ if (!rule || typeof rule?.value !== 'number' || !['gt', 'lt'].includes(rule.op)) return
72
+ const hit = rule.op === 'lt' ? s.value < rule.value : s.value > rule.value
73
+ if (!hit) return
74
+ const line = s.lineId
75
+ const last = Number(ctx.kv.get(`cooldown:${line}`)) || 0
76
+ if (Date.now() - last < AUTO_COOLDOWN_MS) return
77
+ const now = Date.now()
78
+ ctx.kv.set(`cooldown:${line}`, now) // 先占冷却,防同窗连发
79
+ // 首拍竞态:开线后首个越限样本即刻触发,快照查询早于 tsdb 首次刷盘(TSDB_FLUSH_MS=500ms)
80
+ // → 时窗查空失败。空窗失败定向重试(≤3 次、间隔 5s,总窗 ≤20s);冷却不回滚,仍只此一次派发。
81
+ const question = `${line} 产线该时窗数据深度根因诊断(自动触发:节点 ${s.nodeId} 采样值 ${s.value} ${rule.op === 'lt' ? '<' : '>'} ${rule.value})`
82
+ const attempt = (n) => {
83
+ startDiagnosis(ctx, {
84
+ line,
85
+ fromMs: now - 60 * MIN,
86
+ toMs: now,
87
+ question,
88
+ scene: `${line}_auto`,
89
+ source: 'auto',
90
+ }).then((r) => {
91
+ if (r.ok) ctx.logger.warn(`自动诊断已发起:产线 ${line} runId=${r.runId}(节点 ${s.nodeId}=${s.value} 越限)`)
92
+ else if (n < 3 && String(r.error ?? '').includes('无数采样本')) setTimeout(() => attempt(n + 1), 5000)
93
+ else ctx.logger.warn(`自动诊断发起失败(产线 ${line}): ${r.error}`)
94
+ }).catch(() => {})
95
+ }
96
+ attempt(0)
97
+ }
98
+ catch { /* 钩子内绝不抛 */ }
99
+ })
100
+
101
+ // 工具 ×2
102
+ ctx.omp.registerTool({
103
+ name: 'diag_run',
104
+ label: '发起分析',
105
+ description: '发起 IDD 后台分析任务(提交即返 task_id,无需等待;完成后平台自动回执本频道,空闲时也可 diag_status 查询)。analysis_intent 选择分析模式,每种意图由 IDD 侧对应 skill 协议执行(harness 在 IDD 内,分析业务与平台解耦):deep-diagnosis=深度根因诊断(9 阶段管线,最重);stability-report=产线稳定报告(评分+等级+建议,零根因);quick-screen=快速筛查(越限/漂移/冻结异常清单+HTML 看板,最快);doe-analysis=DOE 试验分析(因子效应/显著性/最优工况);experience-mining=调参经验挖掘(结构化经验条目,供知识库沉淀);optimizer-campaign=优化战役(寻优迭代+推荐工况,最重)。数据来源三选一:① line+时间窗(导出产线 DAQ 快照);② data_path:诊断服务内已有数据文件;③ export_id(推荐):daq_export 导出的数据集。',
106
+ parameters: {
107
+ type: 'object',
108
+ properties: {
109
+ line: { type: 'string', description: '产线 id(与 data_path/export_id 三选一;export/data_path 模式可填 any)' },
110
+ export_id: { type: 'string', description: 'daq_export 返回的数据集 id(daqexp-…):上传 manifest+全部节点全量时序 CSV 做多文件分析' },
111
+ data_path: { type: 'string', description: '可选:诊断服务内数据文件相对路径(如 data/aw-snapshots/x.csv;跳过快照导出,适合离线数据分析)' },
112
+ from_ms: { type: 'number', description: '窗口起点(epoch 毫秒;仅 line 模式;缺省 now-60min)' },
113
+ to_ms: { type: 'number', description: '窗口终点(epoch 毫秒;仅 line 模式;缺省 now)' },
114
+ question: { type: 'string', description: '分析问题(缺省按意图自动生成情景化提示词;业务要求特殊时才覆写)' },
115
+ analysis_intent: {
116
+ type: 'string',
117
+ enum: ['deep-diagnosis', 'stability-report', 'quick-screen', 'doe-analysis', 'experience-mining', 'optimizer-campaign'],
118
+ description: '分析模式(缺省 deep-diagnosis)。按业务情景选择:要根因→deep-diagnosis;要运行健康度评分/等级→stability-report;要快速异常清单→quick-screen;要多因子试验数据→doe-analysis;要沉淀调参经验→experience-mining;要寻优推荐工况→optimizer-campaign',
119
+ },
120
+ scene: { type: 'string', description: '场景名(缺省 <line>_diag);场景为自由标签,分析管线按数据特征自适应' },
121
+ mode: { type: 'string', enum: ['async', 'sync'], description: 'async(缺省)=提交即返后台跑;sync=同步等待完成(最长 sync_timeout_s,超时自动转后台继续)。建议改用 wait/wait_seconds(语义更直白)' },
122
+ sync_timeout_s: { type: 'number', description: 'sync 模式等待上限秒(缺省 240,上限 540)' },
123
+ wait: { type: 'boolean', description: '是否等待结果(推荐):true=本次调用挂起直到任务终态——成功/失败都返回完整结果,任务未完不返回;false=提交即返 task_id 后台跑(等价 mode=async)' },
124
+ wait_seconds: { type: 'number', description: 'wait=true 的等待上限秒(缺省 600,上限 1800);窗口内未完则如实返回 running 态与 task_id,任务转后台继续并仍会自动回执' },
125
+ harness: { type: 'string', description: '分析引擎按次覆盖(omp/claude/mock;缺省用插件设置)' },
126
+ model: { type: 'string', description: '可选:引擎模型按次覆盖(合法性由 IDD 校验,不合法返回 400 说明)' },
127
+ permission_mode: { type: 'string', description: '可选:引擎权限模式按次覆盖(合法性由 IDD 校验)' },
128
+ },
129
+ required: [],
130
+ },
131
+ roles: ['lead', 'worker'],
132
+ handler: async (args, agent) => {
133
+ try {
134
+ const line = String(args.line ?? '').trim()
135
+ const dataPathDirect = String(args.data_path ?? '').trim()
136
+ const exportId = String(args.export_id ?? '').trim()
137
+ if (!line && !dataPathDirect && !exportId) return { text: 'line / data_path / export_id 至少给一个。', isError: true }
138
+ // 产线模式按调用者可读面授权(插件工具不走工业工具 grant/verify 两道闸;
139
+ // 此前任意 Agent 可对任意产线导出快照做诊断,构成跨线数据读取)。
140
+ if (line && agent?.agentId) {
141
+ try {
142
+ const scope = await ctx.services.get('agentScope')
143
+ const readable = scope.readableLines(agent.agentId)
144
+ if (!readable.includes(line)) {
145
+ return {
146
+ text: `产线「${line}」不在你的可读范围。你的可读产线:${readable.length ? readable.join(', ') : '(无)'}。如需诊断该产线,请让用户绑定该产线节点/把频道绑定到该产线,或改用上述产线重试。`,
147
+ isError: true,
148
+ }
149
+ }
150
+ }
151
+ catch (err) {
152
+ // fail-closed:无法确认授权时不读取产线数据
153
+ return { text: `产线授权校验不可用,已拒绝本次分析:${err?.message ?? err}。请联系管理员检查插件运行时服务。`, isError: true }
154
+ }
155
+ }
156
+ const toMs = Number(args.to_ms) > 0 ? Number(args.to_ms) : Date.now()
157
+ const fromMs = Number(args.from_ms) > 0 ? Number(args.from_ms) : toMs - 60 * MIN
158
+ if (fromMs >= toMs) return { text: 'from_ms 必须小于 to_ms。', isError: true }
159
+
160
+ // 数据集模式:上传 manifest + 全部节点 CSV(manifest 首位,IDD multi 模式)
161
+ let dataPaths
162
+ let via = ''
163
+ let manifest = null
164
+ if (exportId) {
165
+ const up = await uploadExportDataset(ctx, exportId)
166
+ if (!up.ok) return { text: up.error, isError: true }
167
+ dataPaths = up.dataPaths
168
+ manifest = up.manifest
169
+ const nodeCount = Array.isArray(manifest?.nodes) ? manifest.nodes.length : dataPaths.length - 1
170
+ const win = manifest?.window ? `窗口 ${String(manifest.window.from ?? '').slice(0, 16)} ~ ${String(manifest.window.to ?? '').slice(0, 16)}` : ''
171
+ via = `数据集 ${up.folder}(${nodeCount} 节点全量时序 + manifest,${win})`
172
+ }
173
+
174
+ // 问题缺省:按数据来源自动组题(export 模式带产线与场景备注)
175
+ let question = args.question ? String(args.question) : ''
176
+ if (!question) {
177
+ if (manifest) {
178
+ const lineNames = (Array.isArray(manifest.lines) ? manifest.lines : []).map(l => l?.lineName || l?.lineId).filter(Boolean).join('、') || line || 'offline'
179
+ const note = manifest.note ? `;场景备注:${String(manifest.note).slice(0, 200)}` : ''
180
+ question = `${lineNames} 产线数据集深度根因诊断(${via})${note}`
181
+ }
182
+ else {
183
+ question = `${line || 'offline-data'} 产线该时窗数据深度根因诊断`
184
+ }
185
+ }
186
+ const r = await startDiagnosis(ctx, {
187
+ line: line || 'offline-data',
188
+ fromMs, toMs,
189
+ question,
190
+ scene: args.scene ? String(args.scene) : '',
191
+ source: 'agent',
192
+ dataPath: dataPathDirect || undefined,
193
+ dataPaths,
194
+ harness: args.harness ? String(args.harness) : undefined,
195
+ analysisIntent: args.analysis_intent ? String(args.analysis_intent) : undefined,
196
+ model: args.model ? String(args.model) : undefined,
197
+ permissionMode: args.permission_mode ? String(args.permission_mode) : undefined,
198
+ // 归因(分发层注入的调用方上下文):诊断完成后平台据此把结果回执给发起 Agent
199
+ agentId: agent?.agentId ?? '',
200
+ channelId: agent?.channelId ?? '',
201
+ })
202
+ if (!r.ok) return { text: r.error, isError: true }
203
+ if (!via) via = dataPathDirect ? `离线数据 ${r.csvPath}` : `产线 ${line} 窗口 ${new Date(fromMs).toISOString()} ~ ${new Date(toMs).toISOString()} 快照 ${r.rows} 行 × ${r.nodes} 节点`
204
+
205
+ // 等待语义:wait=true 显式挂起到终态(成功/失败都给完整结果);兼容旧 mode=sync。
206
+ // 挂起期间 Agent 的回合被本工具调用占住——结构上保证"任务未完不能标记完成"。
207
+ const wantWait = args.wait === true || (args.wait === undefined && args.wait_seconds === undefined && String(args.mode ?? '').toLowerCase() === 'sync')
208
+ if (wantWait) {
209
+ const base = baseOf(ctx)
210
+ const timeoutS = args.wait === true
211
+ ? Math.min(Math.max(Number(args.wait_seconds) || 600, 10), 1800)
212
+ : Math.min(Math.max(Number(args.sync_timeout_s) || 240, 10), 540)
213
+ if (base) {
214
+ const deadline = Date.now() + timeoutS * 1000
215
+ let lastPollError = ''
216
+ while (Date.now() < deadline) {
217
+ await new Promise(resolve => setTimeout(resolve, 5000))
218
+ let st = null
219
+ try {
220
+ st = (await jget(ctx, `${base}/api/diagnosis/tasks/${encodeURIComponent(r.runId)}`, 8000)).data ?? null
221
+ lastPollError = ''
222
+ }
223
+ catch (err) {
224
+ lastPollError = String(err?.message ?? err)
225
+ // 鉴权失败/任务不存在:继续等只会空耗到超时,直接如实返回
226
+ if (/HTTP (401|403|404)/.test(lastPollError)) {
227
+ return { text: `同步等待中止 task_id=${r.runId}:${lastPollError}。任务可能仍在后台运行;修复后可用 diag_status(run_id=${r.runId}) 查询,完成后平台仍会回执。`, isError: true }
228
+ }
229
+ }
230
+ const status = String(st?.status ?? r.status ?? 'running')
231
+ if (status === 'completed') {
232
+ // 已同步拿到结果,认领回执防 sweep 补送重复
233
+ try {
234
+ const notify = await ctx.services.get('notify')
235
+ await notify?.claim?.('diag_run', r.runId)
236
+ }
237
+ catch { /* 未接线时忽略 */ }
238
+ const rep = st?.result?.report_md_path ?? '-'
239
+ return { text: `✅ 同步诊断完成 task_id=${r.runId}(${via})\n评分=${st?.result?.score ?? '-'} 结论=${st?.result?.verdict ?? '-'}\nreport_md_path=${rep}\n总结:${String(st?.result?.summary ?? '').slice(0, 800)}\n结果已存分析结果台账(diag_results / diag_result_get 可随时复查)。\n\n入库知识库:调用 rag-bridge 的 kb_agent(wait=true,wait_seconds=600,prompt=「请把这份深度诊断报告入库到 aw-industrial 知识库:读取文件 ${rep} 的全文内容并入库,完成后报告 doc_path 与分块数。标签:诊断、深度诊断。」),完成后 kb_agent_status(task_id) 查询。` }
240
+ }
241
+ if (status === 'failed' || status === 'stopped') {
242
+ return { text: `分析任务 ${r.runId} 以 ${status} 结束:${st?.result?.error ?? '详见 diag_status'}`, isError: true }
243
+ }
244
+ }
245
+ return { text: `已发起${intentLabel(r.intent)}任务 task_id=${r.runId}(${via})。等待 ${timeoutS}s 窗口内未完(仍在后台运行${lastPollError ? `(最近一次轮询失败:${lastPollError})` : ''})——任务结果会存入分析结果台账;完成后平台会自动把结果回执到你的信箱并唤醒你;也可 diag_status(run_id=${r.runId}) 主动查询。注入的分析问题:${r.question}` }
246
+ }
247
+ }
248
+ return {
249
+ text: `已发起「${intentLabel(r.intent)}」异步分析任务 task_id=${r.runId}(${via})。IDD 在后台以对应 skill 协议执行,Channel 无需等待——可继续其他任务;完成后平台会自动把结果回执到你的信箱并唤醒你(空闲时也可用 diag_status(run_id=${r.runId}) 主动查询;完成后按指引调 kb_agent 把报告入库知识库)。注入的分析问题:${r.question}`,
250
+ }
251
+ }
252
+ catch (err) {
253
+ return { text: `diag_run 异常: ${err?.message ?? err}`, isError: true }
254
+ }
255
+ },
256
+ })
257
+
258
+ ctx.omp.registerTool({
259
+ name: 'diag_status',
260
+ label: '分析任务状态',
261
+ description: '查询后台分析任务进度:传 run_id 看单条实时状态(完成时含评分/结论/总结/报告路径),不传看最近 5 条概要。适用 diag_run 发起的全部意图(深度诊断/产线报告/快速筛查/DOE/经验挖掘/优化战役)。',
262
+ parameters: {
263
+ type: 'object',
264
+ properties: {
265
+ run_id: { type: 'string', description: '诊断 runId(缺省列最近 5 条概要)' },
266
+ },
267
+ },
268
+ roles: ['lead', 'worker'],
269
+ handler: async (args) => {
270
+ try {
271
+ const id = String(args.run_id ?? '').trim()
272
+ if (!id) {
273
+ const runs = kvRuns(ctx)
274
+ .sort((a, b) => (b.meta?.createdAt ?? 0) - (a.meta?.createdAt ?? 0))
275
+ .slice(0, 5)
276
+ if (!runs.length) return { text: '暂无诊断记录。用 diag_run 发起一次深度诊断。' }
277
+ const lines = runs.map(({ id: rid, meta }) =>
278
+ `- ${rid} | ${meta.line} | [${intentLabel(meta.intent ?? 'deep-diagnosis')}] ${meta.status}${meta.stored ? '(已入库)' : ''} | ${meta.source ?? 'agent'} | ${meta.createdAt ? new Date(meta.createdAt).toISOString() : '?'} | ${meta.question ?? ''}`)
279
+ return { text: `最近 ${runs.length} 条诊断:\n${lines.join('\n')}` }
280
+ }
281
+ const meta = ctx.kv.get(runKey(id))
282
+ const base = baseOf(ctx)
283
+ let st = null
284
+ let remoteError = ''
285
+ if (base) {
286
+ try {
287
+ // v2.1:走任务管理面 /tasks/:id——result.report_md_path 是绝对路径,
288
+ // 可直接交给 kb_agent 读取入库(旧 /status 的相对路径会让 kb_agent 找不到文件)
289
+ st = (await jget(ctx, `${base}/api/diagnosis/tasks/${encodeURIComponent(id)}`, 8000)).data ?? null
290
+ if (st && st.result) {
291
+ st = {
292
+ ...st,
293
+ engineStatus: st.status,
294
+ score: st.result.score ?? null,
295
+ judge_verdict: st.result.verdict ?? null,
296
+ summary: st.result.summary ?? '',
297
+ report_path: st.result.report_md_path ?? st.report_path ?? null,
298
+ error_message: st.result.error ?? st.error_message ?? null,
299
+ }
300
+ }
301
+ }
302
+ catch (err) {
303
+ // 保留原始错误(404=任务不存在 / 401=token / 网络不可达),不再一律吞成"不可达"
304
+ remoteError = String(err?.message ?? err)
305
+ }
306
+ }
307
+ if (!st && !meta) {
308
+ return { text: `诊断 ${id} 不存在或诊断服务不可达${remoteError ? `(${remoteError})` : ''}。`, isError: true }
309
+ }
310
+ const status = st?.engineStatus || st?.status || meta?.status || 'unknown'
311
+ // 终态经轮询面亲自看到 → 认领结果所有权,取消平台的事后补送(先到先得)
312
+ if (['completed', 'failed', 'stopped'].includes(status)) {
313
+ const notify = await ctx.services.get('notify').catch(() => null)
314
+ notify?.claim?.('diag_run', id)
315
+ }
316
+ const parts = [
317
+ `run_id=${id}`,
318
+ `name=${st?.name ?? meta?.name ?? '?'}`,
319
+ `产线=${meta?.line ?? st?.scene_name ?? '?'}`,
320
+ `意图=${meta?.intent ?? 'deep-diagnosis'}(${intentLabel(meta?.intent ?? 'deep-diagnosis')})`,
321
+ `状态=${status}`,
322
+ `来源=${meta?.source ?? '?'}`,
323
+ ]
324
+ if (meta?.fromMs && meta?.toMs) parts.push(`窗口=${new Date(meta.fromMs).toISOString()} ~ ${new Date(meta.toMs).toISOString()}`)
325
+ if (meta?.question) parts.push(`问题=${meta.question}`)
326
+ if (status === 'completed') {
327
+ const reportPath = st?.report_path ?? meta?.reportPath ?? '-'
328
+ parts.push(`评分=${st?.score ?? meta?.score ?? '-'}`)
329
+ parts.push(`结论=${st?.judge_verdict ?? meta?.verdict ?? '-'}`)
330
+ if (st?.summary) parts.push(`总结=${String(st.summary).slice(0, 600)}`)
331
+ parts.push(`report_md_path=${reportPath}`)
332
+ parts.push('')
333
+ parts.push('✅ 分析完成,报告已就绪。入库知识库请调用 rag-bridge 的 kb_agent 工具:')
334
+ parts.push(` mode=async, prompt=「请把这份分析报告入库到 aw-industrial 知识库:读取文件 ${reportPath} 的全文内容并入库(写盘+索引),完成后报告 doc_path 与分块数。标签:分析、${intentLabel(meta?.intent ?? 'deep-diagnosis')}。」`)
335
+ parts.push('提交后频道无需等待,空闲时用 kb_agent_status(task_id) 查询入库结果。')
336
+ }
337
+ if (status === 'failed' || status === 'stopped') parts.push(`错误=${st?.error_message ?? meta?.error ?? '-'}`)
338
+ if (!st) {
339
+ parts.push(`(远端查询失败:${remoteError || '诊断服务不可达'};以上为本地记录)`)
340
+ if (remoteError.includes('HTTP 404') && ['queued', 'running'].includes(String(meta?.status))) {
341
+ parts.push('远端任务已不存在(可能被 IDD 清理或服务重启);sweep 将在连续 3 轮确认后把本地状态收敛为 failed 并回执,不会永久占用产线单飞锁。')
342
+ }
343
+ }
344
+ return { text: parts.join('\n') }
345
+ }
346
+ catch (err) {
347
+ return { text: `diag_status 异常: ${err?.message ?? err}`, isError: true }
348
+ }
349
+ },
350
+ })
351
+
352
+ // ── 工具 ×3:分析结果库(§16;默认关闭,results_enabled 开启) ────────────
353
+ // 可见性注意:结果库按 IDD Token 属主聚合,平台内不按产线隔离 —— 所以
354
+ // 读面默认只给 lead(worker 可在 handler 内按设置热放开)、写面双闸门(设置 + lead + IDD 属主校验)。
355
+ // roles 必须在注册期就包含 worker:注册后 roles 固化,设置热改不会重注册工具;
356
+ // 因此 worker 的开关判断放在 handler 内(每次调用实时读设置)。
357
+ const denyWorkerIfDisabled = agent => agent?.role === 'worker' && !resultsAllowWorkerOf(ctx)
358
+
359
+ ctx.omp.registerTool({
360
+ name: 'diag_results',
361
+ label: '检索分析结果',
362
+ description: '检索 IDD 分析结果库的摘要索引(全量历史分析,不限本平台发起):按关键词/意图/标签/场景/分数过滤,返回 result_id、标题、意图、评分与结论、时间与标签。先用本工具拿到 result_id,再用 diag_result_get 看完整详情。brief 模式(缺省)单卡很省 token。',
363
+ parameters: {
364
+ type: 'object',
365
+ properties: {
366
+ q: { type: 'string', description: '关键词(匹配 title/description/summary/scene/tags,大小写不敏感)' },
367
+ intent: { type: 'string', enum: ['deep-diagnosis', 'stability-report', 'quick-screen', 'doe-analysis', 'experience-mining', 'optimizer-campaign'], description: '按分析模式过滤' },
368
+ category_id: { type: 'string', description: '按结果分类 id 过滤(见 /results/categories)' },
369
+ tag: { type: 'string', description: '按标签精确匹配' },
370
+ scene: { type: 'string', description: '按场景名精确匹配' },
371
+ harness: { type: 'string', description: '按执行引擎过滤(如 omp/claude/mock)' },
372
+ min_score: { type: 'number', description: 'score >= min_score(稳定性报告 0-100;深度诊断为 Judge 分)' },
373
+ sort: { type: 'string', enum: ['created_desc', 'created_asc', 'score_desc'], description: '排序(缺省 created_desc)' },
374
+ limit: { type: 'number', description: '每页条数(1-200,缺省取插件设置)' },
375
+ offset: { type: 'number', description: '分页偏移(缺省 0)' },
376
+ brief: { type: 'boolean', description: '省 token 模式(缺省 true:无 key_numbers,display_text 截 160 字符)' },
377
+ },
378
+ },
379
+ roles: ['lead', 'worker'],
380
+ handler: async (args, agent) => {
381
+ try {
382
+ if (denyWorkerIfDisabled(agent)) {
383
+ return { text: 'diag_results 当前仅对 lead 角色开放(管理员可在插件设置打开「worker 可读」)。', isError: true }
384
+ }
385
+ if (!resultsEnabledOf(ctx)) {
386
+ return { text: '分析结果库未启用:请在 系统设置→运行配置→插件→diag-bridge 打开「结果库总开关」。', isError: true }
387
+ }
388
+ const probe = await probeResults(ctx)
389
+ if (!probe.ok) return { text: probe.reason, isError: true }
390
+ const base = baseOf(ctx)
391
+ const params = new URLSearchParams()
392
+ for (const [key, value] of Object.entries({
393
+ q: args.q, intent: args.intent, category_id: args.category_id, tag: args.tag,
394
+ scene: args.scene, harness: args.harness,
395
+ min_score: args.min_score, sort: args.sort,
396
+ limit: args.limit ?? resultsDefaultLimitOf(ctx), offset: args.offset,
397
+ brief: args.brief === undefined ? true : args.brief,
398
+ })) {
399
+ if (value !== undefined && value !== null && String(value) !== '') params.set(key, String(value))
400
+ }
401
+ const body = await jget(ctx, `${base}/api/external/v1/results?${params.toString()}`)
402
+ const d = body.data ?? {}
403
+ const cards = d.results ?? []
404
+ if (!cards.length) return { text: `无匹配结果(total=${d.total ?? 0})。放宽过滤或用 q 换个关键词。` }
405
+ const showKn = briefOf(args) === false
406
+ const lines = cards.map(c =>
407
+ `- ${c.result_id} | ${c.intent} | ${c.score ?? '-'}分 ${c.verdict ?? ''} | ${c.created_at ?? '?'} | ${c.title}${(c.tags ?? []).length ? ` [${c.tags.join(',')}]` : ''}`
408
+ + `\n ${String(c.display_text ?? '').slice(0, showKn ? 400 : 160)}`
409
+ + (showKn && c.key_numbers ? `\n key_numbers=${JSON.stringify(c.key_numbers)}` : ''))
410
+ return { text: [`分析结果 total=${d.total} limit=${d.limit} offset=${d.offset} 本页 ${cards.length} 条(brief=${briefOf(args)}):`, ...lines, '', '按 id 取完整详情 → diag_result_get(result_id)。'].join('\n') }
411
+ }
412
+ catch (err) {
413
+ return { text: `diag_results 失败: ${mapResultsError(err, baseOf(ctx))}`, isError: true }
414
+ }
415
+ },
416
+ })
417
+
418
+ ctx.omp.registerTool({
419
+ name: 'diag_result_get',
420
+ label: '分析结果详情',
421
+ description: '按 result_id 取一条分析结果的完整详情:结论摘要、关键数字(可溯源到工件字段)、产物清单(报告/HTML 的 API URL)、运行状态。缺省不带 report.md 全文(只给 URL);include_md=true 时携带全文但按插件设置截断保护上下文。',
422
+ parameters: {
423
+ type: 'object',
424
+ properties: {
425
+ result_id: { type: 'string', description: '结果 id(diag_results 返回的 result_id = run_id)' },
426
+ include_md: { type: 'boolean', description: '携带 report.md 全文(缺省 false 只给 URL)' },
427
+ md_max_chars: { type: 'number', description: '本次调用的 md 截断上限(覆盖插件设置;500-50000)' },
428
+ },
429
+ },
430
+ required: ['result_id'],
431
+ roles: ['lead', 'worker'],
432
+ handler: async (args, agent) => {
433
+ try {
434
+ if (denyWorkerIfDisabled(agent)) {
435
+ return { text: 'diag_result_get 当前仅对 lead 角色开放(管理员可在插件设置打开「worker 可读」)。', isError: true }
436
+ }
437
+ if (!resultsEnabledOf(ctx)) {
438
+ return { text: '分析结果库未启用:请在 系统设置→运行配置→插件→diag-bridge 打开「结果库总开关」。', isError: true }
439
+ }
440
+ const probe = await probeResults(ctx)
441
+ if (!probe.ok) return { text: probe.reason, isError: true }
442
+ const id = String(args.result_id ?? '').trim()
443
+ if (!id) return { text: 'result_id 必填(先用 diag_results 检索)。', isError: true }
444
+ const base = baseOf(ctx)
445
+ const wantMd = args.include_md === true
446
+ const cap = Math.min(50000, Math.max(500, Math.round(Number(args.md_max_chars) || resultsMdMaxOf(ctx))))
447
+ const body = await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=${wantMd ? 'true' : 'false'}`)
448
+ const d = body.data ?? {}
449
+ const sj = d.summary_json ?? {}
450
+ const meta = d.metadata ?? {}
451
+ const parts = [
452
+ `result_id=${d.result_id ?? id}`,
453
+ `标题=${d.title ?? '-'} 意图=${d.intent ?? '-'} 评分=${d.score ?? '-'} 结论=${d.verdict ?? '-'}`,
454
+ `摘要=${String(d.summary ?? '').slice(0, 800)}`,
455
+ ]
456
+ const kn = sj.key_numbers
457
+ if (kn && Object.keys(kn).length) parts.push(`关键数字=${JSON.stringify(kn)}`)
458
+ if (sj.sources) parts.push(`数字溯源=summary_json.sources(每个数字钉到 工件路径#字段)`)
459
+ const artifacts = meta.artifacts ?? []
460
+ if (artifacts.length) parts.push(`工件 ${artifacts.length} 项:${artifacts.slice(0, 12).map(a => a.path ?? a).join(', ')}${artifacts.length > 12 ? ' …' : ''}`)
461
+ const htmls = d.files?.html ?? []
462
+ if (htmls.length) parts.push(`HTML 报告 ${htmls.length} 份(在线查看需带 ?token=<诊断API Token>):${htmls.slice(0, 4).map(h => absoluteUrl(base, h.url)).join(', ')}`)
463
+ parts.push(`报告=${absoluteUrl(base, d.report_md_url)}`)
464
+ if (d.run) parts.push(`run=${d.run.run_id} ${d.run.status}${d.run.session_resumable ? '(可续聊)' : ''}`)
465
+ if (d.derived) parts.push('(本条为按需派生记录:行缺失时详情接口即时生成并标注 derived=true)')
466
+ if (wantMd && typeof d.report_md === 'string' && d.report_md.length) {
467
+ const md = d.report_md
468
+ if (md.length > cap) parts.push(`\nreport.md(截断 ${cap}/${md.length} 字符,全文用上面的报告 URL):\n${md.slice(0, cap)}\n…[已截断]`)
469
+ else parts.push(`\nreport.md:\n${md}`)
470
+ }
471
+ const edited = d.description
472
+ if (edited) parts.push(`\n用户描述=${String(edited).slice(0, 400)}`)
473
+ if ((d.tags ?? []).length) parts.push(`标签=[${d.tags.join(',')}]`)
474
+ parts.push('\n补充描述/标签 → diag_result_annotate(result_id, description, tags)。')
475
+ return { text: parts.filter(Boolean).join('\n') }
476
+ }
477
+ catch (err) {
478
+ return { text: `diag_result_get 失败: ${mapResultsError(err, baseOf(ctx))}`, isError: true }
479
+ }
480
+ },
481
+ })
482
+
483
+ ctx.omp.registerTool({
484
+ name: 'diag_result_annotate',
485
+ label: '编辑结果元数据',
486
+ description: '编辑一条分析结果的元数据(description ≤2000 / tags ≤16×≤40 / title ≤200),用于沉淀"这条结果代表什么、何时复用"。只改元数据、不触产线;需插件设置「允许元数据编辑」且调用者是 lead;属主校验最终在 IDD 侧(非属主 403 如实回执)。',
487
+ parameters: {
488
+ type: 'object',
489
+ properties: {
490
+ result_id: { type: 'string', description: '结果 id(diag_results 返回)' },
491
+ description: { type: ['string', 'null'], description: '结果描述(≤2000 字符;null=清除回落自动摘要)' },
492
+ tags: { type: 'array', items: { type: 'string' }, description: '标签数组(≤16 项,单项 ≤40 字符)' },
493
+ title: { type: 'string', description: '标题(≤200 字符)' },
494
+ },
495
+ },
496
+ required: ['result_id'],
497
+ roles: ['lead'],
498
+ handler: async (args) => {
499
+ try {
500
+ if (!resultsEnabledOf(ctx)) {
501
+ return { text: '分析结果库未启用:请在 系统设置→运行配置→插件→diag-bridge 打开「结果库总开关」。', isError: true }
502
+ }
503
+ if (!resultsAllowAnnotateOf(ctx)) {
504
+ return { text: '元数据编辑未开放:请管理员在插件设置打开「允许元数据编辑」(results_allow_annotate)。', isError: true }
505
+ }
506
+ const probe = await probeResults(ctx)
507
+ if (!probe.ok) return { text: probe.reason, isError: true }
508
+ const id = String(args.result_id ?? '').trim()
509
+ if (!id) return { text: 'result_id 必填。', isError: true }
510
+ const patch = {}
511
+ let touched = 0
512
+ if (args.description !== undefined) {
513
+ patch.description = args.description
514
+ touched += 1
515
+ }
516
+ if (args.tags !== undefined) {
517
+ patch.tags = args.tags
518
+ touched += 1
519
+ }
520
+ if (args.title !== undefined) {
521
+ patch.title = args.title
522
+ touched += 1
523
+ }
524
+ if (!touched) return { text: '至少给 description / tags / title 之一。', isError: true }
525
+ const base = baseOf(ctx)
526
+ await jpatch(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}`, patch)
527
+ // 回读校验:PATCH 响应不含 display_text,重新 GET 详情确认落库;
528
+ // IDD 详情接口不返回 display_text,按索引口径本地拼(description || summary)
529
+ const detail = (await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=false`)).data ?? {}
530
+ ctx.kv.set('diag.results.lastWrite', JSON.stringify({ at: Date.now(), resultId: id, fields: Object.keys(patch) }))
531
+ const displayText = String(detail.description || detail.summary || '').slice(0, 160)
532
+ return { text: `已更新 result_id=${id}:title=${detail.title ?? '-'}\ndescription=${String(detail.description ?? '(未改/为空)').slice(0, 200)}\ntags=[${(detail.tags ?? []).join(',')}]\n(display_text 现为:${displayText || '(空)'}…)。` }
533
+ }
534
+ catch (err) {
535
+ return { text: `diag_result_annotate 失败: ${mapResultsError(err, baseOf(ctx))}`, isError: true }
536
+ }
537
+ },
538
+ })
539
+
540
+ function briefOf(args) {
541
+ return args.brief === undefined ? true : Boolean(args.brief)
542
+ }
543
+
544
+ // 路由 ×3(挂 /api/plugins/diag-bridge/*)
545
+ ctx.route('GET', '/health', async () => {
546
+ const runs = kvRuns(ctx)
547
+ const byStatus = {}
548
+ for (const { meta } of runs) byStatus[meta?.status ?? 'unknown'] = (byStatus[meta?.status ?? 'unknown'] ?? 0) + 1
549
+ const base = baseOf(ctx)
550
+ let remote
551
+ let tokenState = { configured: Boolean(diagTokenOf(ctx)), accepted: null }
552
+ if (base) {
553
+ try {
554
+ // 连通性:/api/health 是公开端点(无鉴权),只回答「服务在不在」。
555
+ const res = await ctx.http.get(`${base}/api/health`, { timeoutMs: 5000 })
556
+ const body = await res.json().catch(() => null)
557
+ remote = res.ok && body
558
+ ? { status: body.status ?? 'ok', activeRuns: body.checks?.activeRuns ?? null }
559
+ : { status: `HTTP ${res.status}`, activeRuns: null }
560
+ // Token 有效性:/api/health 不校验鉴权,必须另打一个受保护端点,
561
+ // 否则 token 配错时健康面依旧 ok —— 配置错误完全不可见(实测踩过)。
562
+ if (tokenState.configured) {
563
+ try {
564
+ const auth = await ctx.http.get(`${base}/api/files/workspace`, {
565
+ timeoutMs: 5000,
566
+ headers: authHeadersOf(ctx),
567
+ })
568
+ if (auth.status === 401 || auth.status === 403) {
569
+ tokenState = { configured: true, accepted: false, hint: authHint(auth.status, 'Token 被上游拒绝') }
570
+ remote = { ...remote, auth: `HTTP ${auth.status}(鉴权被拒)`, hint: tokenState.hint }
571
+ }
572
+ else if (auth.ok) {
573
+ tokenState = { configured: true, accepted: true }
574
+ remote = { ...remote, auth: 'ok' }
575
+ }
576
+ else {
577
+ remote = { ...remote, auth: `HTTP ${auth.status}` }
578
+ }
579
+ }
580
+ catch (err) {
581
+ remote = { ...remote, auth: 'unreachable', authError: String(err?.message ?? err) }
582
+ }
583
+ }
584
+ else {
585
+ tokenState = { configured: false, accepted: null }
586
+ }
587
+ }
588
+ catch (err) {
589
+ remote = { status: 'unreachable', error: String(err?.message ?? err) }
590
+ }
591
+ }
592
+ else {
593
+ remote = { status: 'bad_base_url' }
594
+ }
595
+ return {
596
+ plugin: ctx.name,
597
+ base: base ?? String(ctx.kv.get('diag.base_url') || DEFAULT_BASE),
598
+ harness: harnessOf(ctx),
599
+ auth: diagTokenOf(ctx) ? 'bearer' : 'anonymous',
600
+ /** Token 配置态 + 实测是否被上游接受(401/403 → false;网络不通 → null) */
601
+ token: tokenState,
602
+ remote,
603
+ runs: { total: runs.length, byStatus },
604
+ }
605
+ })
606
+
607
+ ctx.route('GET', '/runs', () => ({
608
+ success: true,
609
+ runs: kvRuns(ctx)
610
+ .sort((a, b) => (b.meta?.createdAt ?? 0) - (a.meta?.createdAt ?? 0))
611
+ .map(({ id, meta }) => ({ runId: id, ...meta })),
612
+ }))
613
+
614
+ ctx.route('POST', '/snapshot', async (event) => {
615
+ try {
616
+ const b = event?.awBody ?? {}
617
+ const line = String(b.line ?? '').trim()
618
+ if (!line) return { success: false, error: 'line 必填(产线 id)' }
619
+ const toMs = Number(b.to_ms) > 0 ? Number(b.to_ms) : Date.now()
620
+ const fromMs = Number(b.from_ms) > 0 ? Number(b.from_ms) : toMs - 60 * MIN
621
+ if (fromMs >= toMs) return { success: false, error: 'from_ms 必须小于 to_ms' }
622
+ const snap = await snapshotCore(ctx, line, fromMs, toMs)
623
+ if (!snap.ok) return { success: false, error: snap.error }
624
+ return { success: true, csvPath: snap.csvPath, rows: snap.rows, nodes: snap.nodes, fromMs: snap.fromMs, toMs: snap.toMs }
625
+ }
626
+ catch (err) {
627
+ return { success: false, error: String(err?.message ?? err) }
628
+ }
629
+ })
630
+
631
+ // 报告查看(面板/前端):run_id → 诊断任务 → workspace run 目录 → report md 内容
632
+ ctx.route('GET', '/report', async (event) => {
633
+ try {
634
+ const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
635
+ const runId = String(u.searchParams.get('run_id') ?? '').trim()
636
+ if (!runId) return { success: false, error: 'run_id 必填' }
637
+ const base = baseOf(ctx)
638
+ if (!base) return { success: false, error: 'diag.base_url 非法' }
639
+ const st = (await jget(ctx, `${base}/api/diagnosis/tasks/${encodeURIComponent(runId)}`, 8000)).data ?? {}
640
+ if (String(st.status ?? '') !== 'completed') {
641
+ return { success: false, error: `诊断 ${runId} 状态 ${st.status ?? 'unknown'},报告未就绪` }
642
+ }
643
+ const ws = (await jget(ctx, `${base}/api/files/workspace`, 8000)).data ?? []
644
+ const names = (Array.isArray(ws) ? ws : [])
645
+ const meta = ctx.kv.get(runKey(runId)) ?? {}
646
+ // 目录命名不统一(<ts>_<scene> 或 <ts>_<scene>_<runId>):runId → scene → 最新 逐级兜底
647
+ const hit = names.find(e => String(e?.name ?? '').includes(runId))
648
+ ?? names.find(e => meta?.scene && String(e?.name ?? '').toLowerCase().includes(String(meta.scene).toLowerCase()))
649
+ ?? (names.length ? names.slice().sort((a, b) => Number(b?.created ?? 0) - Number(a?.created ?? 0))[0] : null)
650
+ if (!hit) return { success: false, error: `诊断服务 workspace 未找到 ${runId} 的运行目录` }
651
+ const rep = (await jget(ctx, `${base}/api/files/workspace/report/${encodeURIComponent(hit.name)}`, 15000)).data
652
+ return { success: true, run_id: runId, run_name: hit.name, report: rep ?? null }
653
+ }
654
+ catch (err) {
655
+ return { success: false, error: String(err?.message ?? err) }
656
+ }
657
+ })
658
+
659
+ // ── 路由 ×4:分析结果库(§16;UI 面板同源,读写开关与工具一致) ────────────
660
+ ctx.route('GET', '/results', async (event) => {
661
+ try {
662
+ if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
663
+ const probe = await probeResults(ctx)
664
+ if (!probe.ok) return { success: false, error: probe.reason }
665
+ const base = baseOf(ctx)
666
+ const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
667
+ const params = new URLSearchParams()
668
+ for (const key of ['q', 'intent', 'category_id', 'tag', 'scene', 'harness', 'min_score', 'sort', 'limit', 'offset', 'brief']) {
669
+ const v = u.searchParams.get(key)
670
+ if (v !== null && v !== '') params.set(key, v)
671
+ }
672
+ if (!params.has('limit')) params.set('limit', String(resultsDefaultLimitOf(ctx)))
673
+ if (!params.has('brief')) params.set('brief', 'true')
674
+ const body = await jget(ctx, `${base}/api/external/v1/results?${params.toString()}`)
675
+ return { success: true, data: body.data }
676
+ }
677
+ catch (err) {
678
+ return { success: false, error: mapResultsError(err, baseOf(ctx)) }
679
+ }
680
+ })
681
+
682
+ ctx.route('GET', '/results/detail', async (event) => {
683
+ try {
684
+ if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
685
+ const probe = await probeResults(ctx)
686
+ if (!probe.ok) return { success: false, error: probe.reason }
687
+ const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
688
+ const id = String(u.searchParams.get('id') ?? '').trim()
689
+ if (!id) return { success: false, error: 'id 必填(result_id = run_id)' }
690
+ const includeMd = u.searchParams.get('include_md') === 'true' ? 'true' : 'false'
691
+ const base = baseOf(ctx)
692
+ const body = await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=${includeMd}`)
693
+ const d = body.data ?? {}
694
+ // D5 跨机语义:不向浏览器透出 IDD 主机绝对路径;报告/HTML 一律给可打开的 URL
695
+ const safe = {
696
+ ...d,
697
+ metadata: d.metadata ? { ...d.metadata, workspace_path: undefined } : d.metadata,
698
+ files: {
699
+ ...(d.files ?? {}),
700
+ html: (d.files?.html ?? []).map(h => ({ path: h.path, url: absoluteUrl(base, h.url) })),
701
+ report_md_path: undefined,
702
+ },
703
+ report_md_url: absoluteUrl(base, d.report_md_url),
704
+ }
705
+ return { success: true, data: { ...safe, baseUrl: base, canAnnotate: resultsAllowAnnotateOf(ctx) && String(event?.awUser?.role ?? '') === 'admin' } }
706
+ }
707
+ catch (err) {
708
+ return { success: false, error: mapResultsError(err, baseOf(ctx)) }
709
+ }
710
+ })
711
+
712
+ ctx.route('PATCH', '/results/edit', async (event) => {
713
+ try {
714
+ if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
715
+ if (!resultsAllowAnnotateOf(ctx)) return { success: false, error: '元数据编辑未开放(results_allow_annotate)' }
716
+ // 写面角色闸门(文档 D3):UI 路由是用户面,要求 AW 管理员;
717
+ // Agent 面走 diag_result_annotate 工具(lead-only + IDD 属主校验)。
718
+ if (String(event?.awUser?.role ?? '') !== 'admin') {
719
+ return { success: false, error: '元数据编辑需要管理员身份(当前账号角色不足)' }
720
+ }
721
+ const probe = await probeResults(ctx)
722
+ if (!probe.ok) return { success: false, error: probe.reason }
723
+ const u = new URL(String(event?.path ?? event?.node?.req?.url ?? ''), 'http://local')
724
+ const id = String(u.searchParams.get('id') ?? '').trim()
725
+ if (!id) return { success: false, error: 'id 必填(result_id = run_id)' }
726
+ const b = event?.awBody ?? {}
727
+ const patch = {}
728
+ for (const key of ['description', 'tags', 'title']) {
729
+ if (b[key] !== undefined) patch[key] = b[key]
730
+ }
731
+ if (!Object.keys(patch).length) return { success: false, error: '至少给 description/tags/title 之一' }
732
+ const base = baseOf(ctx)
733
+ await jpatch(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}`, patch)
734
+ const detail = (await jget(ctx, `${base}/api/external/v1/results/${encodeURIComponent(id)}?include_md=false`)).data ?? {}
735
+ ctx.kv.set('diag.results.lastWrite', JSON.stringify({ at: Date.now(), resultId: id, fields: Object.keys(patch) }))
736
+ return { success: true, data: { ...detail, baseUrl: base, canAnnotate: true } }
737
+ }
738
+ catch (err) {
739
+ return { success: false, error: mapResultsError(err, baseOf(ctx)) }
740
+ }
741
+ })
742
+
743
+ ctx.route('GET', '/results/categories', async () => {
744
+ try {
745
+ if (!resultsEnabledOf(ctx)) return { success: false, error: '结果库未启用(results_enabled)' }
746
+ const probe = await probeResults(ctx)
747
+ if (!probe.ok) return { success: false, error: probe.reason }
748
+ const base = baseOf(ctx)
749
+ const body = await jget(ctx, `${base}/api/external/v1/results/categories`)
750
+ return { success: true, data: body.data }
751
+ }
752
+ catch (err) {
753
+ return { success: false, error: mapResultsError(err, baseOf(ctx)) }
754
+ }
755
+ })
756
+
757
+ ctx.logger.info('diag-bridge 就绪:工具 diag_run/diag_status/diag_results/diag_result_get/diag_result_annotate + 路由 health/runs/snapshot/report/results,轮询 15s 已启动')
758
+ },
759
+ }