agentworkshop 0.8.5 → 0.8.6

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 (349) hide show
  1. package/.AgentWorkShop/prompts/host-tools.en.json +3 -3
  2. package/.AgentWorkShop/prompts/host-tools.json +14 -4
  3. package/.output/.AgentWorkShop/prompts/host-tools.en.json +3 -3
  4. package/.output/.AgentWorkShop/prompts/host-tools.json +14 -4
  5. package/.output/nitro.json +1 -1
  6. package/.output/public/_nuxt/{U_n8pryv2.js → 7op-BMk82.js} +1 -1
  7. package/.output/public/_nuxt/{zWZ26UWi.js → B1SIgyrl.js} +1 -1
  8. package/.output/public/_nuxt/{C4tXwpSt.js → BA2lmfW4.js} +1 -1
  9. package/.output/public/_nuxt/{EJkmXP432.js → BDXQqSkl2.js} +1 -1
  10. package/.output/public/_nuxt/{CY_7u1ST2.js → BI9jj13f2.js} +1 -1
  11. package/.output/public/_nuxt/{CGf9neEK2.js → BiXqyYNw2.js} +1 -1
  12. package/.output/public/_nuxt/{D9GoOkyP2.js → BlOm8JIs2.js} +1 -1
  13. package/.output/public/_nuxt/{CQ31GiJU2.js → BoXKUOiN2.js} +1 -1
  14. package/.output/public/_nuxt/{DZzbCFY2.js → BuLoPmcf.js} +1 -1
  15. package/.output/public/_nuxt/{D9ppMCbw.js → BwOK42JQ.js} +1 -1
  16. package/.output/public/_nuxt/{CZJ5cw6r2.js → C1RMfPoB2.js} +1 -1
  17. package/.output/public/_nuxt/{CGviZq4E.js → COfo_Bg3.js} +1 -1
  18. package/.output/public/_nuxt/CTGJHZ8X2.js +1 -0
  19. package/.output/public/_nuxt/{SgsmmrsZ.js → CY2NkFvI.js} +1 -1
  20. package/.output/public/_nuxt/{B4GOTVfN.js → C_4mzQ5X.js} +1 -1
  21. package/.output/public/_nuxt/{DGmFHb7m.js → CdvMP3lv.js} +1 -1
  22. package/.output/public/_nuxt/{iqxZGXsY.js → CooauJ7D.js} +1 -1
  23. package/.output/public/_nuxt/{C3vxePED.js → D1eVi1zr.js} +1 -1
  24. package/.output/public/_nuxt/{DnuJ66Jy.js → D6PUmBb0.js} +1 -1
  25. package/.output/public/_nuxt/{BBhroEeN.js → DC8sMA36.js} +1 -1
  26. package/.output/public/_nuxt/{DOUuGOQR2.js → DDHh85c42.js} +1 -1
  27. package/.output/public/_nuxt/{Cy_HDgSl.js → DDXOfmo2.js} +2 -2
  28. package/.output/public/_nuxt/{Cr2xJ_CY.js → DMFwLxfu.js} +1 -1
  29. package/.output/public/_nuxt/{BZg_MIK42.js → DoprKJ5a2.js} +1 -1
  30. package/.output/public/_nuxt/{BzBBEE-o2.js → Dr1FVeQG2.js} +1 -1
  31. package/.output/public/_nuxt/{W0sNnJQh.js → DtnsTW7e.js} +1 -1
  32. package/.output/public/_nuxt/{gUYi1hLJ2.js → DxDqd6T42.js} +1 -1
  33. package/.output/public/_nuxt/{B0UUODeh.js → DzU3zEKX.js} +1 -1
  34. package/.output/public/_nuxt/{BkO5WdX5.js → EgO70wrE.js} +1 -1
  35. package/.output/public/_nuxt/{7OZQiw002.js → HJKu4eEV2.js} +1 -1
  36. package/.output/public/_nuxt/{ChF6qLpY2.js → K5Xen0wZ2.js} +1 -1
  37. package/.output/public/_nuxt/{CNrRZmSx2.js → Rb4o4o-82.js} +1 -1
  38. package/.output/public/_nuxt/{BqBTlfiz.js → Tf6SJSDj.js} +1 -1
  39. package/.output/public/_nuxt/{DJaGVY4v2.js → ZzOyoXex2.js} +1 -1
  40. package/.output/public/_nuxt/{PbzVdCTA2.js → azOGy_tU2.js} +1 -1
  41. package/.output/public/_nuxt/{D1thFC3z.js → be-4wyTo.js} +1 -1
  42. package/.output/public/_nuxt/builds/latest.json +1 -1
  43. package/.output/public/_nuxt/builds/meta/593b65bd-cf1d-4b7f-a993-57e939c38f36.json +1 -0
  44. package/.output/public/_nuxt/{8b9CKi6F.js → dcjPjLPI.js} +1 -1
  45. package/.output/public/_nuxt/{ae2-5ZPt.js → h-Fn7pRD.js} +1 -1
  46. package/.output/public/_nuxt/{-gy9k_TE2.js → hyLdZsOd2.js} +1 -1
  47. package/.output/public/_nuxt/{BHxG41IQ2.js → o8sYG6xx2.js} +1 -1
  48. package/.output/server/chunks/_/access.mjs +1 -1
  49. package/.output/server/chunks/_/agent-dto.mjs +26 -0
  50. package/.output/server/chunks/_/agent-dto.mjs.map +1 -0
  51. package/.output/server/chunks/_/data-export.mjs +6 -6
  52. package/.output/server/chunks/_/data-query.mjs +3 -3
  53. package/.output/server/chunks/_/eda-run.repo.mjs +2 -2
  54. package/.output/server/chunks/_/features.mjs +5 -5
  55. package/.output/server/chunks/_/golden.mjs +3 -3
  56. package/.output/server/chunks/_/harness-spawn-policy.mjs +77 -0
  57. package/.output/server/chunks/_/harness-spawn-policy.mjs.map +1 -0
  58. package/.output/server/chunks/_/index.mjs +1 -1
  59. package/.output/server/chunks/_/line-scope.mjs +32 -0
  60. package/.output/server/chunks/_/line-scope.mjs.map +1 -0
  61. package/.output/server/chunks/_/mes-console.mjs +1 -1
  62. package/.output/server/chunks/_/nitro.mjs +2429 -1726
  63. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  64. package/.output/server/chunks/_/ocap.mjs +1 -1
  65. package/.output/server/chunks/_/rest-auth.mjs +1 -1
  66. package/.output/server/chunks/_/spc.mjs +1 -1
  67. package/.output/server/chunks/build/TownView-BWMDbosY.mjs +1 -1
  68. package/.output/server/chunks/build/daq-CdxcfY_G.mjs +1 -1
  69. package/.output/server/chunks/build/dcw-BlDQtS4q.mjs +1 -1
  70. package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
  71. package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs +1 -1
  72. package/.output/server/chunks/build/useDaqDetailNode-DeOsDxK5.mjs +1 -1
  73. package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs +1 -1
  74. package/.output/server/chunks/build/useDaqFilters-CctmO03p.mjs +1 -1
  75. package/.output/server/chunks/build/useDaqStream-B9ZBVQrI.mjs +1 -1
  76. package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs +1 -1
  77. package/.output/server/chunks/build/useDcwAddNode-DACVcEHs.mjs +1 -1
  78. package/.output/server/chunks/build/useDcwStream-Dir5ufAU.mjs +1 -1
  79. package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
  80. package/.output/server/chunks/routes/api/system/backup/run.post.mjs +2 -2
  81. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +17 -9
  82. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs.map +1 -1
  83. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +3 -0
  84. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs.map +1 -1
  85. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  86. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +6 -0
  87. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs.map +1 -1
  88. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +5 -0
  89. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs.map +1 -1
  90. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +5 -2
  91. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs.map +1 -1
  92. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +7 -1
  93. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs.map +1 -1
  94. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +3 -1
  95. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs.map +1 -1
  96. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +8 -6
  97. package/.output/server/chunks/routes/api/workshop/channels.post.mjs.map +1 -1
  98. package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs +29 -49
  99. package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs.map +1 -1
  100. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +1 -1
  101. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +6 -1
  102. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs.map +1 -1
  103. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +4 -2
  104. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs.map +1 -1
  105. package/.output/server/chunks/routes/api/workshop/daq/index2.post.mjs +1 -1
  106. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +14 -5
  107. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs.map +1 -1
  108. package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +1 -1
  109. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +1 -1
  110. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +1 -1
  111. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +7 -4
  112. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs.map +1 -1
  113. package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
  114. package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +1 -1
  115. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +6 -2
  116. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +6 -2
  118. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +1 -1
  120. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +1 -1
  121. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +1 -1
  122. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +6 -1
  123. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +1 -1
  125. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +7 -4
  126. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs.map +1 -1
  127. package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +1 -1
  129. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +6 -2
  131. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +1 -1
  133. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +7 -4
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +1 -1
  137. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +6 -2
  139. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -1
  140. package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +1 -1
  141. package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +1 -1
  142. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  143. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  144. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +1 -1
  145. package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs +6 -2
  146. package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs.map +1 -1
  147. package/.output/server/chunks/routes/api/workshop/dcw/mes-hook-test.post.mjs +1 -1
  148. package/.output/server/chunks/routes/api/workshop/dcw/mes-test-read.post.mjs +1 -1
  149. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
  150. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
  151. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +6 -2
  152. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs.map +1 -1
  153. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +4 -2
  154. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs.map +1 -1
  155. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +5 -2
  156. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -1
  157. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
  159. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
  160. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +1 -1
  161. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +1 -1
  162. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +1 -1
  164. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs +7 -3
  165. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs.map +1 -1
  166. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +8 -1
  167. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs.map +1 -1
  168. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +6 -2
  169. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs.map +1 -1
  170. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +1 -1
  171. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +1 -1
  172. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  173. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  174. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +7 -4
  175. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs.map +1 -1
  176. package/.output/server/chunks/routes/api/workshop/eda/commonality.get.mjs +1 -1
  177. package/.output/server/chunks/routes/api/workshop/eda/vm/predict.post.mjs +1 -1
  178. package/.output/server/chunks/routes/api/workshop/exp/confirmations/_id/decide.post.mjs +1 -1
  179. package/.output/server/chunks/routes/api/workshop/exp/index.get.mjs +1 -1
  180. package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +1 -1
  181. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  182. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  183. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  184. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  185. package/.output/server/chunks/routes/api/workshop/index.mjs +1 -1
  186. package/.output/server/chunks/routes/api/workshop/index.post.mjs +2 -0
  187. package/.output/server/chunks/routes/api/workshop/index.post.mjs.map +1 -1
  188. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  189. package/.output/server/chunks/routes/api/workshop/index3.post.mjs +8 -3
  190. package/.output/server/chunks/routes/api/workshop/index3.post.mjs.map +1 -1
  191. package/.output/server/chunks/routes/api/workshop/index5.post.mjs +1 -1
  192. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
  193. package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
  194. package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
  195. package/.output/server/chunks/routes/api/workshop/mes/datasets.get.mjs +1 -1
  196. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/autofix.mjs +1 -1
  197. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/compile.mjs +1 -1
  198. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/evidence.mjs +1 -1
  199. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/publish.mjs +1 -1
  200. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval-decision.mjs +1 -1
  201. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval.mjs +1 -1
  202. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/transition.mjs +1 -1
  203. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/subgraph.mjs +1 -1
  204. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/validate.mjs +1 -1
  205. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId_.mjs +1 -1
  206. package/.output/server/chunks/routes/api/workshop/ontology/export/_ontologyId_.mjs +1 -1
  207. package/.output/server/chunks/routes/api/workshop/ontology/index.mjs +1 -1
  208. package/.output/server/chunks/routes/api/workshop/ontology/scene.mjs +1 -1
  209. package/.output/server/chunks/routes/api/workshop/ontology/traces/_ontologyId_.mjs +1 -1
  210. package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId/compare.mjs +1 -1
  211. package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId_.mjs +1 -1
  212. package/.output/server/chunks/routes/api/workshop/optimization/capabilities.get.mjs +1 -1
  213. package/.output/server/chunks/routes/api/workshop/optimization/index.get.mjs +1 -1
  214. package/.output/server/chunks/routes/api/workshop/optimization/index.mjs +1 -1
  215. package/.output/server/chunks/routes/api/workshop/optimization/index2.mjs +1 -1
  216. package/.output/server/chunks/routes/api/workshop/optimization/profiles/_id/publish.post.mjs +1 -1
  217. package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/crosscheck.post.mjs +1 -1
  218. package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/dispatch.post.mjs +1 -1
  219. package/.output/server/chunks/routes/api/workshop/optimization/solve.post.mjs +1 -1
  220. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  221. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  222. package/.output/server/chunks/routes/api/workshop/process-model/datasets.post.mjs +1 -1
  223. package/.output/server/chunks/routes/api/workshop/process-model/eligibility.mjs +1 -1
  224. package/.output/server/chunks/routes/api/workshop/process-model/index.mjs +1 -1
  225. package/.output/server/chunks/routes/api/workshop/process-model/specs/_id/publish.post.mjs +1 -1
  226. package/.output/server/chunks/routes/api/workshop/process-model/train.post.mjs +1 -1
  227. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  228. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  229. package/.output/server/chunks/routes/api/workshop/tools/sync.post.mjs +1 -1
  230. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  231. package/.output/server/chunks/routes/renderer.mjs +2 -2
  232. package/.output/server/chunks/virtual/entry.mjs +1 -1
  233. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  234. package/.output/server/package.json +1 -1
  235. package/README-zh.md +4 -2
  236. package/README.md +9 -2
  237. package/config.yml +1 -1
  238. package/docker-compose.yml +4 -2
  239. package/docs/cli.en.md +2 -2
  240. package/docs/cli.md +2 -2
  241. package/docs/plugins.en.md +1 -1
  242. package/docs/plugins.md +1 -1
  243. package/docs/sdk.en.md +1 -1
  244. package/docs/sdk.md +1 -1
  245. package/package.json +1 -1
  246. package/scripts/_audit/_live-mqtt-e2e-20261028.mjs +219 -0
  247. package/scripts/_audit/prompt-injection-probe.mts +76 -0
  248. package/scripts/api-live-e2e.mjs +9 -3
  249. package/scripts/e2e-multiexpert-remediation-real.mjs +453 -0
  250. package/scripts/e2e-node-binding-3mode-real.mjs +697 -0
  251. package/scripts/e2e-node-binding-authz.mjs +332 -0
  252. package/scripts/security-acceptance.mjs +218 -0
  253. package/server/api/workshop/agent-tools/bindings/index.post.ts +33 -17
  254. package/server/api/workshop/agents/[id].patch.ts +3 -0
  255. package/server/api/workshop/agents/index.post.ts +3 -0
  256. package/server/api/workshop/channel-templates/[id].patch.ts +5 -0
  257. package/server/api/workshop/channel-templates/index.post.ts +6 -0
  258. package/server/api/workshop/channels/[id]/agents/[agentId].patch.ts +5 -0
  259. package/server/api/workshop/channels/[id]/agents/index.get.ts +8 -2
  260. package/server/api/workshop/channels/[id]/agents/index.post.ts +7 -0
  261. package/server/api/workshop/channels/[id]/index.get.ts +10 -4
  262. package/server/api/workshop/channels.post.ts +3 -0
  263. package/server/api/workshop/daq/[id]/ingest.post.ts +26 -22
  264. package/server/api/workshop/daq/[id].patch.ts +8 -0
  265. package/server/api/workshop/daq/alarms.get.ts +18 -16
  266. package/server/api/workshop/daq/infra/reconnect.post.ts +21 -5
  267. package/server/api/workshop/daq/test-driver.post.ts +10 -3
  268. package/server/api/workshop/dcw/[id]/param-ledger.get.ts +7 -1
  269. package/server/api/workshop/dcw/[id]/read.post.ts +6 -1
  270. package/server/api/workshop/dcw/[id].patch.ts +49 -40
  271. package/server/api/workshop/dcw/journal/index.get.ts +7 -3
  272. package/server/api/workshop/dcw/mes-artifacts/file.get.ts +7 -1
  273. package/server/api/workshop/dcw/mes-artifacts/index.get.ts +7 -1
  274. package/server/api/workshop/dcw/optimizations/[id]/series.get.ts +6 -1
  275. package/server/api/workshop/dcw/optimizations/[id].get.ts +3 -1
  276. package/server/api/workshop/dcw/optimizations/index.get.ts +6 -3
  277. package/server/api/workshop/dcw/params/[id]/read.post.ts +22 -17
  278. package/server/api/workshop/dcw/recipes/[id]/versions.get.ts +20 -14
  279. package/server/api/workshop/dcw/recipes/[id].delete.ts +33 -25
  280. package/server/api/workshop/dcw/recipes/index.get.ts +17 -12
  281. package/server/api/workshop/dcw/runs/[id]/close.post.ts +21 -15
  282. package/server/api/workshop/dcw/test-driver.post.ts +21 -16
  283. package/server/api/workshop/ws/hub.ts +2 -1
  284. package/server/plugins/01-instance-guard.ts +82 -0
  285. package/server/plugins/daq.ts +40 -8
  286. package/server/plugins/workshop.ts +12 -0
  287. package/server/services/workshop/agents/adapters/line-spawn.ts +24 -1
  288. package/server/services/workshop/agents/agent-dto.ts +54 -0
  289. package/server/services/workshop/agents/harness-spawn-policy.ts +114 -0
  290. package/server/services/workshop/agents/host-tool-bridge/dispatch.ts +22 -0
  291. package/server/services/workshop/agents/host-tool-bridge/index.ts +1 -1
  292. package/server/services/workshop/agents/host-tool-bridge/per-agent.ts +17 -3
  293. package/server/services/workshop/agents/host-tool-bridge/tools/tasks.ts +1 -1
  294. package/server/services/workshop/agents/host-tool-bridge/tools/team-delegation.ts +5 -1
  295. package/server/services/workshop/agents/industrial/dcw-tools.ts +6 -2
  296. package/server/services/workshop/agents/industrial/ops-tools.ts +54 -12
  297. package/server/services/workshop/agents/industrial/recipe-gate.ts +68 -15
  298. package/server/services/workshop/agents/industrial/tool-verify-legacy-specs.ts +2 -2
  299. package/server/services/workshop/agents/industrial/tool-verify.ts +31 -2
  300. package/server/services/workshop/agents/node-bindings.repo.ts +4 -3
  301. package/server/services/workshop/agents/node-delegation.ts +49 -4
  302. package/server/services/workshop/agents/tool-approvals.ts +23 -0
  303. package/server/services/workshop/agents/tool-reference-spec.ts +3 -0
  304. package/server/services/workshop/daq/bus/mqtt.adapter.ts +12 -0
  305. package/server/services/workshop/daq/daq-controller/crud.ts +5 -3
  306. package/server/services/workshop/daq/daq-controller/ingest.ts +23 -0
  307. package/server/services/workshop/daq/daq-controller/push.ts +24 -3
  308. package/server/services/workshop/daq/daq-controller/runtime-control.ts +6 -0
  309. package/server/services/workshop/daq/daq-controller/sweep-alarms.ts +3 -1
  310. package/server/services/workshop/daq/daq-controller/tsdb-twin.ts +21 -1
  311. package/server/services/workshop/daq/daq-node.repo.ts +10 -7
  312. package/server/services/workshop/daq/daq-node.ts +2 -0
  313. package/server/services/workshop/daq/daq-runtime.ts +10 -2
  314. package/server/services/workshop/daq/drivers/modbus-tcp.ts +8 -5
  315. package/server/services/workshop/daq/drivers/registry.ts +5 -4
  316. package/server/services/workshop/daq/infra.ts +13 -0
  317. package/server/services/workshop/daq/ingest-policy.ts +24 -1
  318. package/server/services/workshop/daq/storage/timescale.adapter.ts +42 -3
  319. package/server/services/workshop/db/database/migrations.ts +83 -68
  320. package/server/services/workshop/dcw/dcw-controller/crud.ts +27 -2
  321. package/server/services/workshop/dcw/dcw-controller/lines.ts +14 -3
  322. package/server/services/workshop/dcw/dcw-controller/recipes.ts +63 -5
  323. package/server/services/workshop/dcw/dcw-recipe.repo.ts +10 -1
  324. package/server/services/workshop/dcw/dcw-runtime.ts +37 -14
  325. package/server/services/workshop/dcw/drivers/shared.ts +62 -6
  326. package/server/services/workshop/dcw/recipe-op-anchor.ts +28 -0
  327. package/server/services/workshop/dcw/recipe-rollback-manager/rollback.ts +204 -190
  328. package/server/services/workshop/dcw/recipe-rollback-manager/sweep.ts +97 -77
  329. package/server/services/workshop/line-scope.ts +80 -0
  330. package/server/services/workshop/optimization/dispatch.service.ts +18 -2
  331. package/server/services/workshop/permissions.ts +2 -0
  332. package/server/services/workshop/runtime/agent-runtime/message.ts +17 -0
  333. package/server/services/workshop/runtime/agent-runtime/state.ts +9 -3
  334. package/server/services/workshop/runtime/agent-runtime/types.ts +2 -1
  335. package/server/services/workshop/runtime/channel-runtime.ts +3 -1
  336. package/server/services/workshop/runtime/mailbox.ts +6 -3
  337. package/server/services/workshop/runtime/manager/a2a.ts +4 -2
  338. package/server/services/workshop/runtime/manager/admin-channel.ts +1 -1
  339. package/server/services/workshop/runtime/manager/chat.ts +3 -1
  340. package/server/services/workshop/runtime/manager/host-tools.ts +1 -1
  341. package/server/services/workshop/runtime/manager/runtime-wiring.ts +31 -19
  342. package/server/services/workshop/runtime/manager/state.ts +43 -1
  343. package/server/services/workshop/runtime/permission-scope.ts +21 -0
  344. package/server/services/workshop/runtime/scheduler-loop/tick.ts +16 -4
  345. package/server/services/workshop/scene-events.ts +19 -0
  346. package/shared/daq-protocol.ts +24 -5
  347. package/shared/dcw-protocol/catalog.ts +4 -4
  348. package/.output/public/_nuxt/CCI-fwcS2.js +0 -1
  349. package/.output/public/_nuxt/builds/meta/5cd11284-05bf-4fcd-9a69-56a01c9f56dd.json +0 -1
@@ -0,0 +1,80 @@
1
+ /**
2
+ * REST 数据面 → 产线授权解析(2026-10-28 审计修复 S4)。
3
+ *
4
+ * 背景:工具面有 grant-guard/tool-verify 双层产线校验,但大量 REST 读数/删除端点
5
+ * 只有 `resolveUser` —— 任意登录用户可读全量产线配方参数、写账本、PLC 实时值,
6
+ * 甚至删除配方/关闭批次。本模块把"资源 → 产线"的解析收敛到一处,配合
7
+ * permissions.requireLineMode 让 REST 与工具面同口径。
8
+ *
9
+ * 约定:
10
+ * - 资源不存在时返回 null,由路由负责 404(避免把"不存在"误报成 403)。
11
+ * - 列表用 filterByLine(admin/特权返回全集)。
12
+ */
13
+ import { AppError } from '../../utils/errors'
14
+ import { filterByLine, requireLineMode } from './permissions'
15
+ import { getDcwRecipeRepo } from './dcw/dcw-recipe.repo'
16
+ import { getDcwController } from './dcw/dcw-controller'
17
+ import { getDcwParamRepo } from './dcw/param-map.repo'
18
+ import { getDaqNodeRepo } from './daq/daq-node.repo'
19
+ import { getRecipeRollBackManager } from './dcw/recipe-rollback-manager'
20
+
21
+ export interface ScopedUser {
22
+ id: string
23
+ role: string
24
+ }
25
+
26
+ /** 配方 → 产线 */
27
+ export function recipeLineId(recipeId: string): string | null {
28
+ return getDcwRecipeRepo().byId(recipeId)?.lineId ?? null
29
+ }
30
+
31
+ /** 数控节点 → 产线 */
32
+ export function dcwNodeLineId(nodeId: string): string | null {
33
+ return getDcwController().byId(nodeId)?.lineId ?? null
34
+ }
35
+
36
+ /** 数采节点 → 产线 */
37
+ export function daqNodeLineId(nodeId: string): string | null {
38
+ return getDaqNodeRepo().byId(nodeId)?.lineId ?? null
39
+ }
40
+
41
+ /** 工艺参数 → 执行节点 → 产线 */
42
+ export function paramLineId(paramId: string): string | null {
43
+ const row = getDcwParamRepo().byId(paramId)
44
+ if (!row) return null
45
+ return dcwNodeLineId(row.nodeId)
46
+ }
47
+
48
+ /** 批次 → 产线 */
49
+ export function runLineId(runId: string): string | null {
50
+ return getDcwRecipeRepo().runById(runId)?.lineId ?? null
51
+ }
52
+
53
+ /** 优化记录 → 产线 */
54
+ export function optimizationLineId(recordId: string): string | null {
55
+ return getRecipeRollBackManager().recordById(recordId)?.lineId ?? null
56
+ }
57
+
58
+ /**
59
+ * 解析并校验:资源不存在 → 404;无该线权限 → 403。
60
+ * need='readonly' 允许只读/操控;need='operate' 仅操控。
61
+ */
62
+ export function requireLineForResource(
63
+ user: ScopedUser,
64
+ lineId: string | null | undefined,
65
+ resourceLabel: string,
66
+ need: 'readonly' | 'operate' = 'readonly',
67
+ ): void {
68
+ if (!lineId) {
69
+ // 未分配产线的资源(独立节点/遗留数据):与 permissions 的"无记录=不可见"同口径,
70
+ // 仅 admin 可见;否则拒绝,避免成为绕过产线隔离的旁路。
71
+ if (user.role === 'admin') return
72
+ throw new AppError(403, 'LINE_FORBIDDEN', `${resourceLabel}未归属产线,仅管理员可访问`)
73
+ }
74
+ requireLineMode(user, lineId, need)
75
+ }
76
+
77
+ /** 列表按产线过滤(admin 见全集;无授权线的条目被剔掉) */
78
+ export function filterRowsByLine<T>(user: ScopedUser, rows: T[], lineIdOf: (row: T) => string | null | undefined): T[] {
79
+ return filterByLine(user, rows, lineIdOf)
80
+ }
@@ -34,6 +34,7 @@ import { toModel } from '@/shared/units'
34
34
  import { getDcwController } from '../dcw/dcw-controller'
35
35
  import { evaluateRecipeParamLimits } from '../dcw/dcw-controller/write'
36
36
  import { getDcwLineRepo } from '../dcw/dcw-line.repo'
37
+ import { getActiveLineRun } from '../dcw/line-run'
37
38
  import { assertRecipeOpInterval } from '../dcw/recipe-op-anchor'
38
39
  import { getToolApprovals } from '../agents/tool-approvals'
39
40
  import { amlActivityForLine } from '../agents/industrial/aml-activity'
@@ -116,7 +117,7 @@ export type DispatchGateResult = { ok: true } | { ok: false, code: string, comme
116
117
 
117
118
  /** D13 批准时刻八项重验(fail-closed)。任一失败 → {ok:false,code,comment}。
118
119
  * overrides:建卡时刻预检用 —— 卡尚未挂出,payload 不可读,由调用方传入本地构造值。 */
119
- export async function assertDispatchApprovable(recId: string, overrides?: { modelFirstMove?: Record<string, number>, packages?: Array<{ params: Array<{ nodeId: string, to: number }> }> }): Promise<DispatchGateResult> {
120
+ export async function assertDispatchApprovable(recId: string, overrides?: { modelFirstMove?: Record<string, number>, packages?: Array<{ params: Array<{ nodeId: string, to: number }> }> }, opts: { requireRunning?: boolean } = {}): Promise<DispatchGateResult> {
120
121
  try {
121
122
  const { rec, run, binding, profile, spec, candidate, recipe } = loadRec(recId)
122
123
  // ① 建议:未过期(显式持久失效)+ 最新 crosscheck 行 pass 且读时 stale=false
@@ -154,6 +155,21 @@ export async function assertDispatchApprovable(recId: string, overrides?: { mode
154
155
  // ⑥ 当前操作点:MV 步限(模型单位)+ CV 新鲜度
155
156
  const line = getDcwLineRepo().byId(spec.lineId)
156
157
  if (!line) return { ok: false, code: 'DISPATCH_GATE_LINE_GONE', comment: `产线 ${spec.lineId} 不存在` }
158
+ // 运行门(2026-10-28):D13 八项重验此前不含"产线在跑" —— 停线检修中的设备仍会收到
159
+ // 首步下发(applyRecipe 自身也无运行门)。批准时刻再查一次(decide 会重跑本函数)。
160
+ // 建卡时刻不做运行门(允许先备好方案,开线后再批准),由调用方传 requireRunning:false。
161
+ if (opts.requireRunning !== false) {
162
+ const activeLine = getActiveLineRun(spec.lineId)
163
+ if (!activeLine) {
164
+ return { ok: false, code: 'DISPATCH_GATE_LINE_NOT_RUNNING', comment: '产线当前未运行(无活动批次):真实下发已拒绝。请先开线并确认参数写入完成后再批准。' }
165
+ }
166
+ if (activeLine.recipeId !== recipe.id) {
167
+ return { ok: false, code: 'DISPATCH_GATE_RECIPE_NOT_ACTIVE', comment: `活动批次正在执行配方 ${activeLine.recipeId},与本建议配方 ${recipe.id} 不一致;请重新 solve 或切换产线配方。` }
168
+ }
169
+ if (recipe.productId && activeLine.productId !== recipe.productId) {
170
+ return { ok: false, code: 'DISPATCH_GATE_PRODUCT_MISMATCH', comment: `活动批次产品 ${activeLine.productId} 与配方产品 ${recipe.productId} 不一致;按拒收敛。` }
171
+ }
172
+ }
157
173
  const modelFirstMove = overrides?.modelFirstMove
158
174
  ?? (getToolApprovals().listPending().map(a => a.payload).find(p => (p as { recommendationId?: string } | null)?.recommendationId === recId) as (RecipeProposePayload & { modelFirstMove?: Record<string, number> }) | undefined)?.modelFirstMove
159
175
  ?? {}
@@ -359,7 +375,7 @@ export async function createDispatchCard(initiator: ResolvedUser, recommendation
359
375
  if (dispatchProfileRow) assertOntologyUnchanged(parseProfileRow(dispatchProfileRow).ontologyRefs)
360
376
  }
361
377
  // 闸序预检(建卡时刻;批准时刻 D13 全量重验)
362
- const gate = await assertDispatchApprovable(recommendationId, { modelFirstMove, packages: [{ params: paramRows }] })
378
+ const gate = await assertDispatchApprovable(recommendationId, { modelFirstMove, packages: [{ params: paramRows }] }, { requireRunning: false })
363
379
  if (!gate.ok) throw new AppError(gate.code.startsWith('DISPATCH_GATE') || gate.code === 'DISPATCH_STEP_EXCEEDED' ? 403 : 409, gate.code, gate.comment)
364
380
  // 在飞去重(三前缀;建卡时无自身卡可排除)
365
381
  const inflight = recipeWriteInFlight(recipe.id)
@@ -67,6 +67,8 @@ export function notifyGrantsChanged(userId: string): void {
67
67
  // 延迟动态导入避免与 scene-events 循环依赖
68
68
  void import('@/server/services/workshop/scene-events').then((m) => {
69
69
  m.broadcastSceneEvent('permissions.changed', { userId, at: new Date().toISOString() })
70
+ // S14:已连接的 peer 就地重算可见集(撤权后不必等重连)
71
+ void m.refreshPeerVisibility(userId)
70
72
  }).catch(() => {})
71
73
  }
72
74
  catch { /* 广播失败不影响授权写入 */ }
@@ -44,6 +44,8 @@ export abstract class AgentRuntimeMessage extends AgentRuntimeSupervise {
44
44
  this.deps.bus.notifyAgent({ agentId: this.agentId, state: 'busy', ...this.queueContext() })
45
45
  // 回合失败标记(须在 try 外声明,finally 依据它决定重投还是消费)
46
46
  let sawRunError = false
47
+ /** C-P1-2:本回合是否产生过平台回执(决定人类调用作用域是否还需由 settle 兜底收口) */
48
+ let repliedToRequester = false
47
49
  /** 失败原因(事件 error / 生成器抛错):写入任务 history,供事后诊断 */
48
50
  let runErrorMessage = ''
49
51
  try {
@@ -205,6 +207,7 @@ export abstract class AgentRuntimeMessage extends AgentRuntimeSupervise {
205
207
  replyToId: typeof msg.metadata['x-aw-reply-to'] === 'string' ? msg.metadata['x-aw-reply-to'] : undefined,
206
208
  deliveryId: typeof msg.metadata['x-aw-delivery-id'] === 'string' ? msg.metadata['x-aw-delivery-id'] : undefined,
207
209
  })
210
+ repliedToRequester = true
208
211
  }
209
212
  // 交付兜底(harness 回合结束 ≠ 任务完成):
210
213
  // - 回合产出过实质 artifact(LLM 完成了工作但跳过 complete_task 工具)→ 隐式完成,
@@ -318,6 +321,20 @@ export abstract class AgentRuntimeMessage extends AgentRuntimeSupervise {
318
321
  finally {
319
322
  this.abortController = null
320
323
  this.currentTaskId = null
324
+ // C-P1-2:回合落定即通知 manager。回执成功时 manager 已在 platformReply 里按
325
+ // invocationId 精确清除;失败/无回执路径必须由这里兜底清除,否则 scope 永久驻留。
326
+ if (this.deps.onInvocationSettled) {
327
+ try {
328
+ this.deps.onInvocationSettled({
329
+ agentId: this.agentId,
330
+ deliveryId: typeof msg.metadata?.['x-aw-delivery-id'] === 'string' ? msg.metadata['x-aw-delivery-id'] : null,
331
+ replied: repliedToRequester,
332
+ })
333
+ }
334
+ catch (err) {
335
+ log.error(`[AgentRuntime:${this.agentId}] 调用作用域收口回调失败:`, err)
336
+ }
337
+ }
321
338
  if (this.state === 'busy') {
322
339
  this.state = 'idle'
323
340
  this.deps.bus.notifyAgent({ agentId: this.agentId, state: 'idle', ...this.queueContext() })
@@ -94,6 +94,12 @@ export abstract class AgentRuntimeState extends AgentRuntimeContracts {
94
94
  /** 投递台账 id(闭环留痕) */
95
95
  deliveryId?: string
96
96
  }) => void
97
+ /**
98
+ * C-P1-2(2026-10-28 审查):回合落定回调 —— **无论是否产生回执**都触发。
99
+ * manager 据此收口"人类发起者作用域":失败回合(只调工具/报错/被停)不会再走
100
+ * platformReply,旧实现让 scope 永久驻留并封锁该 Agent 的管理面/写面。
101
+ */
102
+ onInvocationSettled?: (e: { agentId: string, deliveryId: string | null, replied: boolean }) => void
97
103
  /**
98
104
  * 监督信号回调(§7.1 supervise.watchdog):watchdog 触发时把结构化信号交给
99
105
  * manager 落 Channel 共享记忆。平台侧回调,不改变 watchdog 的非破坏语义。
@@ -134,9 +140,9 @@ export abstract class AgentRuntimeState extends AgentRuntimeContracts {
134
140
  if (typeof attach === 'function') attach.call(this.impl, deps.workspace)
135
141
  }
136
142
 
137
- /** 投递消息;idle 时立即唤醒消费 */
138
- enqueue(message: A2AMessage): void {
139
- this.deps.mailbox.enqueue(message)
143
+ /** 投递消息;idle 时立即唤醒消费。返回 false = 信箱已关闭(停机窗口),消息未落地。 */
144
+ enqueue(message: A2AMessage): boolean {
145
+ return this.deps.mailbox.enqueue(message)
140
146
  }
141
147
 
142
148
  getState(): 'idle' | 'busy' | 'stopped' {
@@ -164,7 +164,8 @@ export interface AgentRuntimeLike {
164
164
  readonly role: 'lead' | 'worker'
165
165
  readonly channelId: string
166
166
  readonly name: string
167
- enqueue(message: A2AMessage): void
167
+ /** 投递消息;返回 false = 信箱已关闭(停机窗口),调用方必须如实上报未送达 */
168
+ enqueue(message: A2AMessage): boolean
168
169
  getState(): 'idle' | 'busy' | 'stopped'
169
170
  /** 实时状态视图(idle/busy/stopped + 当前任务 + 队列长度) */
170
171
  getStatus(): AgentStatusView
@@ -79,7 +79,9 @@ export class ChannelRuntime {
79
79
  for (const agentId of this.resolveRecipients(message)) {
80
80
  const agent = this.ensureAgent(agentId)
81
81
  if (!agent) continue
82
- agent.enqueue(message)
82
+ // 闭箱(停机窗口)不算送达:如实返回未投递,发送方据此可重试/报错,
83
+ // 绝不出现"success + 消息永久丢失"(2026-10-28 审查 C-P1-1)。
84
+ if (!agent.enqueue(message)) continue
83
85
  delivered.push(agentId)
84
86
  // 空闲收件人即时唤醒消费循环
85
87
  if (agent.getState() === 'idle') agent.wakeMailbox()
@@ -56,9 +56,11 @@ export class Mailbox {
56
56
  this.onWake = wake
57
57
  }
58
58
 
59
- /** 投递:落库 pending + 唤醒 dequeue 门闩 + 到信回调 + 通知外部 */
60
- enqueue(message: A2AMessage): void {
61
- if (this.closed) return
59
+ /** 投递:落库 pending + 唤醒 dequeue 门闩 + 到信回调 + 通知外部。
60
+ * 返回 false = 信箱已关闭(停机窗口):消息**未落库**,调用方不得上报"已送达"
61
+ * (2026-10-28 审查 C-P1-1:闭箱静默吞消息 + route 照常计入 delivered = 假送达)。 */
62
+ enqueue(message: A2AMessage): boolean {
63
+ if (this.closed) return false
62
64
  this.messageRepo.create({
63
65
  // 保留发送方 messageId 作为落库 id:API 返回的 messageId 才能与历史/状态一致关联
64
66
  id: message.messageId,
@@ -78,6 +80,7 @@ export class Mailbox {
78
80
  catch { /* 回调异常不阻断投递 */ }
79
81
  }
80
82
  this.onWake()
83
+ return true
81
84
  }
82
85
 
83
86
  /** 到信回调注册(即时唤醒阻塞等待方);返回注销函数 */
@@ -23,7 +23,7 @@ export abstract class ManagerA2A extends ManagerTasks {
23
23
  // 调用者若正在服务一次人类发起的调用,其委派必须继承**同一个**(并只收紧)作用域:
24
24
  // 否则普通成员 @Agent 后,该 Agent 再委派给兄弟 Agent,被委派者就变回"无人类上下文",
25
25
  // 等于用一次委派把成员权限洗成系统权限。
26
- const callerScope = this.activeInvocationScopes.get(callerAgentId) ?? null
26
+ const callerScope = this.activeScopeOf(callerAgentId)
27
27
  const inbound = parseWorkshopPermissionScope(input.metadata?.[WORKSHOP_PERMISSION_SCOPE_HEADER])
28
28
  const delegatedScope = callerScope
29
29
  ? intersectWorkshopPermissionScope(callerScope, inbound)
@@ -36,11 +36,13 @@ export abstract class ManagerA2A extends ManagerTasks {
36
36
  'x-aw-target-agent': target.id,
37
37
  'x-aw-from-agent': callerAgentId,
38
38
  })
39
- if (delegatedScope) this.activeInvocationScopes.set(target.id, delegatedScope)
39
+ if (delegatedScope) this.setInvocationScope(target.id, delegatedScope)
40
40
  // 真送达契约:route 回执必须包含目标(落库 enqueue 同步完成),否则向上抛错 ——
41
41
  // 消息绝不允许"报告已发送"却没进对方信箱
42
42
  const delivered = this.route(channelId, message)
43
43
  if (!delivered.includes(target.id)) {
44
+ // C-P1-2:未送达 → 被委派者身上的作用域必须撤销(否则一次失败委派永久收紧其权限)
45
+ this.clearInvocationScope(target.id, delegatedScope?.invocationId ?? null)
44
46
  throw new AppError(502, 'DELIVERY_FAILED', `消息未能投递到 ${target.name} 的信箱(成员装配失败),请重试或改投其他成员`)
45
47
  }
46
48
  return message
@@ -221,7 +221,7 @@ export abstract class ManagerAdminChannel extends ManagerNotifications {
221
221
  * 此前只有 REST/MCP 路径生效,群成员 @Agent 时的高危工具限制被绕过。
222
222
  */
223
223
  invocationScopeOf(agentId: string) {
224
- return this.activeInvocationScopes.get(agentId) ?? null
224
+ return this.activeScopeOf(agentId)
225
225
  }
226
226
 
227
227
  /** v18:Agent 实例 → 其频道绑定的产线 id(只读扩权依据;未绑/找不到 = null) */
@@ -364,11 +364,13 @@ export abstract class ManagerChat extends ManagerAccess {
364
364
  }),
365
365
  })
366
366
  // 记为该 Agent 当前人类调用作用域:工具调用与 Agent 间委派都要按它收紧(§13.3)。
367
- this.activeInvocationScopes.set(targetAgentId, parseWorkshopPermissionScope(
367
+ this.setInvocationScope(targetAgentId, parseWorkshopPermissionScope(
368
368
  message.metadata?.[WORKSHOP_PERMISSION_SCOPE_HEADER],
369
369
  ) as WorkshopPermissionScope)
370
370
  const delivered = this.route(channelId, message)
371
371
  if (!delivered.includes(targetAgentId)) {
372
+ // C-P1-2:投递失败 → 作用域立即撤销(否则一次失败投递会永久封锁该 Agent)
373
+ this.clearInvocationScope(targetAgentId, deliveryId)
372
374
  return finish('failed', {
373
375
  mailboxMessageId: message.messageId,
374
376
  error: `未投递到 ${targetAgentId} 的 mailbox`,
@@ -106,7 +106,7 @@ export abstract class ManagerHostTools extends ManagerA2A {
106
106
  // @Agent 不等于把 owner 权限委托给成员,发起者权限不得因 Agent 身份更高而放大。
107
107
  // 无作用域(系统/agent 自发调用)→ 放行,沿用既有授权(MCP token / REST 用户鉴权)。
108
108
  {
109
- const scope = this.activeInvocationScopes.get(input.agentId)
109
+ const scope = this.activeScopeOf(input.agentId)
110
110
  const verdict = checkToolAgainstScope(scope, input.tool)
111
111
  if (!verdict.allowed) throw new AppError(403, verdict.reason, verdict.message)
112
112
  }
@@ -113,13 +113,12 @@ export abstract class ManagerRuntimeWiring extends ManagerBus {
113
113
  // 人类调用已收口:解除该 Agent 的发起者作用域限制(§13.3)。
114
114
  // 仅当这次回复对应的正是当前记录的那次人类调用时清除(invocationId 比对),
115
115
  // 避免把随后到达的另一次调用的作用域误删;老路径无 deliveryId 时按"收口即清除"。
116
- {
117
- const active = this.activeInvocationScopes.get(agent.id)
118
- if (active && (!reply.deliveryId || active.invocationId === reply.deliveryId)) {
119
- this.activeInvocationScopes.delete(agent.id)
120
- }
121
- }
116
+ this.clearInvocationScope(agent.id, reply.deliveryId ?? null)
122
117
  },
118
+ // C-P1-2(2026-10-28 审查):回合结束(无论是否产生回执)都收口人类调用作用域。
119
+ // 无回执的回合(模型只调工具/报错/被停)不会再触发 platformReply,旧实现在这条
120
+ // 失败路径上让 scope 永久驻留 → lead 管理面/worker 写面被陈旧限制封锁。
121
+ onInvocationSettled: e => this.clearInvocationScope(e.agentId, e.deliveryId ?? null),
123
122
  // 监督信号(§7.1 supervise.watchdog)与 Harness 重建(§7.1 harness.restarted)
124
123
  // 必须落 Channel 共享记忆 —— watchdog 只在内存态可见等于"零观测"。
125
124
  onSupervisionSignal: e => this.recordSupervisionSignal(e),
@@ -136,9 +135,11 @@ export abstract class ManagerRuntimeWiring extends ManagerBus {
136
135
  return runtime
137
136
  }
138
137
 
139
- /** 按需装配实例运行时(幂等):已装配返回缓存,否则从实例行 wire;禁用/不存在返回 undefined */
138
+ /** 按需装配实例运行时(幂等):已装配返回缓存,否则从实例行 wire;禁用/不存在/停机中返回 undefined */
140
139
  protected ensureAgentRuntime(channelId: string, agentId: string): AgentRuntime | undefined {
141
140
  const key = runtimeKey(channelId, agentId)
141
+ // 停机窗口墓碑:既不返回半关闭的旧运行时,也不重建新运行时(否则消息进闭箱)
142
+ if (this.stoppingAgents.has(key)) return undefined
142
143
  const existing = this.agentIndex.get(key)
143
144
  if (existing) return existing
144
145
  const m = this.deps.repos.channelAgents.findByChannelAgent(channelId, agentId)
@@ -216,8 +217,9 @@ export abstract class ManagerRuntimeWiring extends ManagerBus {
216
217
  // 以前写死在 SchedulerLoop 构造默认值里,mock/规则引擎路径上最长要 10 分钟才可见。
217
218
  ...(Number.isFinite(workshopSettings().stall_ms) ? { stallMs: workshopSettings().stall_ms } : {}),
218
219
  ...options,
219
- // 停滞看门狗活性源:agent 最近一次工具 invoke 时刻(工具调用即健康推进)
220
- toolActivityOf: (agentId: string) => this.lastToolInvokeAt.get(agentId) ?? null,
220
+ // 停滞看门狗活性源:agent 最近一次工具 invoke 时刻(工具调用即健康推进)。
221
+ // C-P1-3:取 REST/MCP 回程与全引擎分发入口打点的并集 —— omp 原生/pi 从不过 REST。
222
+ toolActivityOf: (agentId: string) => this.toolActivityOf(agentId),
221
223
  // 调度快照的邮件上下文(lead 观察 worker 间通信的唯一来源;DB 为事实源)
222
224
  supervisionMail: limit => this.deps.repos.messages
223
225
  .listRecentByChannel(channelId, limit)
@@ -242,18 +244,28 @@ export abstract class ManagerRuntimeWiring extends ManagerBus {
242
244
  const key = runtimeKey(channelId, agentId)
243
245
  const runtime = this.agentIndex.get(key)
244
246
  if (!runtime) return
247
+ // C-P1-1(2026-10-28 审查):所有"摘除"动作在第一个 await 之前同步完成。
248
+ // 旧实现把 agentIndex.delete 放在 await stop() 之后,停机窗口内:
249
+ // route → ensureAgentRuntime 命中旧索引 → enqueue 进正在关闭的信箱 → 假送达 + 永久丢失;
250
+ // 即便先 detach,loader 也能按 agentIndex 缓存把旧运行时装回去。
251
+ // 现在的顺序 = 立墓碑 → 摘索引 → 摘频道成员 → 清人类调用作用域 → 后台停机。
252
+ this.stoppingAgents.add(key)
253
+ this.agentIndex.delete(key)
245
254
  const cr = this.channels.get(channelId)
246
- const scheduler = runtime.role === 'lead' ? cr?.scheduler : null
247
- if (scheduler) {
248
- await scheduler.stopAndWait()
249
- cr!.scheduler = null
250
- }
251
- // 先摘除再停机:runtime.stop() 会关闭 mailbox,若成员仍在 route 视野内,
252
- // 停机窗口内到达的消息会被 closed mailbox 静默吞掉但 delivered 照常上报
253
- // (发送方收到成功假象);先 detach 让 route 立即按"成员不存在"如实失败
254
255
  cr?.detachAgent(agentId)
255
- await runtime.stop()
256
- this.agentIndex.delete(key)
256
+ this.clearInvocationScope(agentId)
257
+ try {
258
+ const scheduler = runtime.role === 'lead' ? cr?.scheduler : null
259
+ if (scheduler) {
260
+ await scheduler.stopAndWait()
261
+ if (cr) cr.scheduler = null
262
+ }
263
+ await runtime.stop()
264
+ }
265
+ finally {
266
+ // 停机完成即允许惰性复活(新消息触发 wireMember 重建)
267
+ this.stoppingAgents.delete(key)
268
+ }
257
269
  if (cr && cr.getAgents().length === 0 && !cr.scheduler) {
258
270
  this.channels.delete(channelId)
259
271
  this.buses.delete(channelId)
@@ -20,6 +20,8 @@ import { TERMINAL_TASK_STATES } from '../../types/task'
20
20
  import { TaskEngine as TaskEngineImpl } from '../task-engine'
21
21
  import { createEnvEmbeddingProvider } from '../embedding-provider'
22
22
  import { log, buildMessage } from './helpers'
23
+ import { toolActivityAt } from '../../agents/host-tool-bridge'
24
+ import { settleInvocationScope } from '../permission-scope'
23
25
 
24
26
  import { rowToMessage } from '../mailbox'
25
27
  import { platformNoticeKey, newPlatformNoticeId, setPlatformNoticeSink, type PlatformNotice, type PlatformNoticeSink } from '../platform-notice'
@@ -75,6 +77,12 @@ export abstract class ManagerState extends ManagerContracts {
75
77
  protected channels = new Map<string, ChannelRuntime>()
76
78
  /** 键 = runtimeKey(channelId, 实例 id);每个实例一个独立运行时 */
77
79
  protected agentIndex = new Map<string, AgentRuntime>()
80
+ /**
81
+ * 正在停机卸载的实例(runtimeKey)。C-P1-1:stopAndDetach 必须在第一个 await 之前
82
+ * 同步摘除索引并立墓碑,否则 route 的按需装载会在停机窗口命中原运行时/重建新运行时,
83
+ * 把消息投进正在关闭的信箱(假送达 + 永久丢失)。
84
+ */
85
+ protected readonly stoppingAgents = new Set<string>()
78
86
  protected buses = new Map<string, ChannelBus>()
79
87
  protected taskEngine: TaskEngine | null = null
80
88
  protected idleSweeperTimer: NodeJS.Timeout | null = null
@@ -87,8 +95,21 @@ export abstract class ManagerState extends ManagerContracts {
87
95
  protected readonly reflectCounts = new Map<string, number>()
88
96
  /** shared task digest 节流:channel/task/event bucket -> last write time */
89
97
  protected readonly teamMemoryCheckpointAt = new Map<string, number>()
90
- /** agent 最近一次工具 invoke 时刻(调度器停滞看门狗的活性源;工具调用即健康推进) */
98
+ /**
99
+ * agent 最近一次工具 invoke 时刻(REST/MCP 回程路径)。
100
+ * C-P1-3:omp 原生/pi 扩展不经此处,必须与 host-tool-bridge 的统一活性注册表取并集
101
+ * (见 toolActivityOf;否则默认引擎下信号恒空)。
102
+ */
91
103
  protected readonly lastToolInvokeAt = new Map<string, number>()
104
+
105
+ /** 调度器活性信号:两条来源取最新(REST/MCP 回程 + 全引擎分发入口打点) */
106
+ protected toolActivityOf(agentId: string): number | null {
107
+ const a = this.lastToolInvokeAt.get(agentId) ?? 0
108
+ const b = toolActivityAt(agentId) ?? 0
109
+ const at = Math.max(a, b)
110
+ return at > 0 ? at : null
111
+ }
112
+
92
113
  /**
93
114
  * 每个 Agent 当前正在服务的**人类发起者作用域**(§13.3)。
94
115
  * 由群聊投递(deliverChatToAgent)写入,Agent 回复(platformReply)后清除。
@@ -97,6 +118,27 @@ export abstract class ManagerState extends ManagerContracts {
97
118
  * 而工具调用是由 harness 侧发起的往返,逐次透传需要改协议,取本表更保守(宁可收紧)。
98
119
  */
99
120
  protected readonly activeInvocationScopes = new Map<string, WorkshopPermissionScope>()
121
+
122
+ /** 读作用域(统一入口;所有读点经此,便于集中加失效语义) */
123
+ protected activeScopeOf(agentId: string): WorkshopPermissionScope | null {
124
+ return this.activeInvocationScopes.get(agentId) ?? null
125
+ }
126
+
127
+ /** 写作用域(人类调用投递时登记) */
128
+ protected setInvocationScope(agentId: string, scope: WorkshopPermissionScope): void {
129
+ this.activeInvocationScopes.set(agentId, scope)
130
+ }
131
+
132
+ /**
133
+ * 清作用域。C-P1-2(2026-10-28 审查):除"回复收口"外,回合结束未产生回执
134
+ * (模型只调工具/报错/被停)也必须清除 —— 旧实现只在 platformReply 里清,
135
+ * 失败回合会让陈旧 scope 永久封锁该 Agent 的管理面/写面(自增强卡死)。
136
+ * invocationId 给出时只清对应那次调用(不误删随后到达的新调用)。
137
+ */
138
+ protected clearInvocationScope(agentId: string, invocationId?: string | null): void {
139
+ settleInvocationScope(this.activeInvocationScopes, agentId, invocationId)
140
+ }
141
+
100
142
  /** v16 定时任务执行器(timer 唯一持有者;startScheduleRuntime 装配,shutdown 停止) */
101
143
  protected scheduleRuntime: ScheduleRuntime | null = null
102
144
  /**
@@ -81,3 +81,24 @@ export function scopeFromMetadata(
81
81
  if (!metadata) return null
82
82
  return parse(metadata[header])
83
83
  }
84
+
85
+ /**
86
+ * 人类调用作用域收口(C-P1-2,2026-10-28 审查)。
87
+ *
88
+ * 作用域必须随"该次人类调用收口"而失效:正常回执时按 invocationId 精确清除;
89
+ * 回合失败/无回执时由回合落定回调兜底清除 —— 否则陈旧 scope 永久封锁该 Agent 的
90
+ * 管理面/写面,且后续系统调用无从解开。invocationId 为空 = 无条件清除(老路径)。
91
+ *
92
+ * @returns true = 确实清除了一条作用域
93
+ */
94
+ export function settleInvocationScope(
95
+ map: Map<string, { invocationId?: string | null }>,
96
+ agentId: string,
97
+ invocationId?: string | null,
98
+ ): boolean {
99
+ const active = map.get(agentId)
100
+ if (!active) return false
101
+ if (invocationId && active.invocationId && active.invocationId !== invocationId) return false
102
+ map.delete(agentId)
103
+ return true
104
+ }
@@ -13,6 +13,8 @@ import { workshopSettings } from '../../settings'
13
13
  export abstract class SchedulerLoopTick extends SchedulerLoopState {
14
14
  /** HITL 等待豁免登记:同一根任务只发一次顺延日志(落定后清理) */
15
15
  protected hitlDeferred = new Set<string>()
16
+ /** 每个根被 HITL 顺延的次数(上限后恢复 ROOT_TIMEOUT,避免 hold 模式永久挂起) */
17
+ protected hitlDeferrals = new Map<string, number>()
16
18
  protected async runRound(): Promise<void> {
17
19
  this.running = true
18
20
  try {
@@ -65,19 +67,28 @@ export abstract class SchedulerLoopTick extends SchedulerLoopState {
65
67
  // 执行预算顺延一个 root_timeout,不做 ROOT_TIMEOUT 收口 —— 审批自身有 fail-closed
66
68
  // 超时(security.recipe_dispatch_timeout_ms),人类思考期不应处决整棵任务树。
67
69
  // 豁免仅在「有待批」时生效;审批落定(批准执行/超时关闭)后预算仍耗尽则照常收口。
70
+ // 2026-10-28:此前的 listPending() 无任何过滤 —— 任意 channel 的一张待批单都会让
71
+ // 本 channel 的每个超期根每 tick 顺延并提前 return(不派发/不判超时);hold 模式下
72
+ // 一张无人处理的单可让全平台调度永久停摆。现在只统计本 channel 成员的待批。
68
73
  const hitlPending = await (async () => {
69
74
  try {
70
75
  const { getToolApprovals } = await import('../../agents/tool-approvals')
71
- return getToolApprovals().listPending().length > 0
76
+ const memberIds = new Set(snapshot.members.map(m => m.agentId))
77
+ return getToolApprovals().listPending().some(a => memberIds.has(a.agentId))
72
78
  }
73
79
  catch { return false }
74
80
  })()
75
- if (hitlPending) {
81
+ const MAX_HITL_DEFERRALS = 3
82
+ const deferrable = hitlPending
83
+ ? expiredRoots.filter(r => (this.hitlDeferrals.get(r.id) ?? 0) < MAX_HITL_DEFERRALS)
84
+ : []
85
+ if (deferrable.length > 0 && deferrable.length === expiredRoots.length) {
76
86
  const budget = new Date(Date.now() + (Number(workshopSettings().root_timeout_ms) || 900_000)).toISOString()
77
- for (const root of expiredRoots) {
87
+ for (const root of deferrable) {
88
+ this.hitlDeferrals.set(root.id, (this.hitlDeferrals.get(root.id) ?? 0) + 1)
78
89
  if (!this.hitlDeferred.has(root.id)) {
79
90
  this.hitlDeferred.add(root.id)
80
- log.warn(`[SchedulerLoop:${this.lead.agentId}] 根任务 ${root.id.slice(0, 8)} 预算耗尽,但频道有 HITL 待批 —— 执行预算顺延至 ${budget}`)
91
+ log.warn(`[SchedulerLoop:${this.lead.agentId}] 根任务 ${root.id.slice(0, 8)} 预算耗尽,但本频道有 HITL 待批 —— 执行预算顺延至 ${budget}(第 ${this.hitlDeferrals.get(root.id)}/${MAX_HITL_DEFERRALS} 次)`)
81
92
  }
82
93
  try {
83
94
  // 能力探测(mirror execute.ts):极简 TaskEngine 替身可能缺 refreshDeadline,缺失时跳过
@@ -90,6 +101,7 @@ export abstract class SchedulerLoopTick extends SchedulerLoopState {
90
101
  return
91
102
  }
92
103
  this.hitlDeferred.clear()
104
+ this.hitlDeferrals.clear()
93
105
  for (const root of expiredRoots) {
94
106
  const closed = this.lead.taskEngine.timeoutTree(root.id, this.lead.agentId)
95
107
  for (const task of closed) {
@@ -35,6 +35,25 @@ export function setPeerVisibleLines(peer: WsPeer, visible: Set<string> | null):
35
35
  (peer as WsPeer & { __awVisibleLines?: Set<string> | null }).__awVisibleLines = visible
36
36
  }
37
37
 
38
+ /** S14:记录 peer 的用户身份,便于授权变更时就地刷新可见集(无需客户端重连) */
39
+ export function setPeerIdentity(peer: WsPeer, user: { id: string, role: string }): void {
40
+ (peer as WsPeer & { __awPeerUser?: { id: string, role: string } }).__awPeerUser = user
41
+ }
42
+
43
+ /** S14:授权变更后重算该用户所有在线 peer 的产线可见集(撤权立即生效) */
44
+ export async function refreshPeerVisibility(userId: string): Promise<void> {
45
+ const targets = [...peers()].filter(p => (p as WsPeer & { __awPeerUser?: { id: string } }).__awPeerUser?.id === userId)
46
+ if (targets.length === 0) return
47
+ try {
48
+ const { visibleLineIds } = await import('./permissions')
49
+ for (const peer of targets) {
50
+ const u = (peer as WsPeer & { __awPeerUser?: { id: string, role: string } }).__awPeerUser
51
+ if (u) setPeerVisibleLines(peer, visibleLineIds(u))
52
+ }
53
+ }
54
+ catch { /* 权限服务未就绪:保留旧可见集,下次重连刷新 */ }
55
+ }
56
+
38
57
  function peerSeesLine(peer: WsPeer, lineId: string): boolean {
39
58
  const visible = (peer as WsPeer & { __awVisibleLines?: Set<string> | null }).__awVisibleLines
40
59
  if (visible == null) return true
@@ -298,8 +298,8 @@ export const DAQ_DRIVERS: DaqDriverMeta[] = [
298
298
  { key: 'scale', label: '缩放系数', type: 'number', default: 1, hint: '原始值 × scale = 工程量(如 0.1)' },
299
299
  { key: 'byteOrder', label: '字节序', type: 'select', default: 'big', options: [
300
300
  { value: 'big', label: '大端(AB CD)' },
301
- { value: 'little', label: '小端(CD AB)' },
302
- { value: 'wordSwap', label: '字交换(CD AB / 交换单字)' },
301
+ { value: 'little', label: '小端(DC BA / 全字节反转)' },
302
+ { value: 'wordSwap', label: '字交换(CD AB / 仅换字序)' },
303
303
  ] },
304
304
  ],
305
305
  },
@@ -344,8 +344,8 @@ export const DAQ_DRIVERS: DaqDriverMeta[] = [
344
344
  { key: 'scale', label: '缩放系数', type: 'number', default: 1, hint: '原始值 × scale = 工程量(如 0.1)' },
345
345
  { key: 'byteOrder', label: '字节序', type: 'select', default: 'big', options: [
346
346
  { value: 'big', label: '大端(AB CD)' },
347
- { value: 'little', label: '小端(CD AB)' },
348
- { value: 'wordSwap', label: '字交换(CD AB / 交换单字)' },
347
+ { value: 'little', label: '小端(DC BA / 全字节反转)' },
348
+ { value: 'wordSwap', label: '字交换(CD AB / 仅换字序)' },
349
349
  ] },
350
350
  ],
351
351
  },
@@ -539,11 +539,30 @@ export interface AepDaqControllerState {
539
539
  }
540
540
 
541
541
  /** driverConfig 出口脱敏:键名含 password/secret/token/passwd/api_key 的值置换为 '******'(REST/AEP 投影用;持久化与驱动内部不经过此函数) */
542
+ export const MASKED_SECRET = '******'
543
+ const SECRET_KEY_RE = /pass(word)?|secret|token|passwd|api[-_]?key/i
542
544
  export function maskDriverConfigSecrets<T extends Record<string, unknown>>(cfg: T | undefined | null): T {
543
545
  if (!cfg || typeof cfg !== 'object') return {} as T
544
546
  const out: Record<string, unknown> = { ...cfg }
545
547
  for (const k of Object.keys(out)) {
546
- if (/pass(word)?|secret|token|passwd|api[-_]?key/i.test(k)) out[k] = '******'
548
+ if (SECRET_KEY_RE.test(k)) out[k] = MASKED_SECRET
549
+ }
550
+ return out as T
551
+ }
552
+
553
+ /**
554
+ * driverConfig 入口过滤(2026-10-28 审查 P0-2):剔除"回显回来的掩码值"键。
555
+ *
556
+ * 前端参数页把 GET 投影(含 `******`)整包 PATCH 回来时,若直接 merge,真实口令
557
+ * 会被字面量 `******` 覆盖 → 节点永久 offline 且无法从视图发现。掩码哨兵只允许
558
+ * 出现在出口方向;入口在 merge 之前单点剔除,新明文照常写入(掩码键给新值 = 修改)。
559
+ */
560
+ export function stripMaskedSecrets<T extends Record<string, unknown>>(cfg: T | undefined | null): T {
561
+ if (!cfg || typeof cfg !== 'object') return {} as T
562
+ const out: Record<string, unknown> = {}
563
+ for (const [k, v] of Object.entries(cfg)) {
564
+ if (v === MASKED_SECRET && SECRET_KEY_RE.test(k)) continue
565
+ out[k] = v
547
566
  }
548
567
  return out as T
549
568
  }