agentworkshop 0.7.50 → 0.7.52
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.
- package/.AgentWorkShop/prompts/host-tools.json +2028 -1955
- package/.output/.AgentWorkShop/prompts/host-tools.json +2028 -1955
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{CdIsmU5a2.js → 4wrEQ3np2.js} +1 -1
- package/.output/public/_nuxt/{asmJ99nX2.js → B8zP7kaj2.js} +1 -1
- package/.output/public/_nuxt/{D89BUtx6.js → B9cGf8mg.js} +1 -1
- package/.output/public/_nuxt/{CP0I2l-6.js → BVzw5a_0.js} +1 -1
- package/.output/public/_nuxt/{CFQSjB1h.js → B_opxrHJ.js} +1 -1
- package/.output/public/_nuxt/{BGAaKBeo2.js → BiUhxH3t2.js} +1 -1
- package/.output/public/_nuxt/{B7jlMWsz.js → C00Rd6Cg.js} +1 -1
- package/.output/public/_nuxt/{BOmDHp5L.js → CGQvF-Gg.js} +3 -3
- package/.output/public/_nuxt/CTvh8v4T.js +1 -0
- package/.output/public/_nuxt/{B9QzrmgE.js → Cbeifv55.js} +1 -1
- package/.output/public/_nuxt/{BWU1pvLL.js → CcGuXiOa.js} +1 -1
- package/.output/public/_nuxt/{zh-S_7rN2.js → CkPxL4UX2.js} +1 -1
- package/.output/public/_nuxt/{DzfVS8N72.js → D_Ce9Fon2.js} +1 -1
- package/.output/public/_nuxt/{DClNt1uS.js → Da90gveK.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/c64014ca-3595-4b7a-a5ca-055687c83ba2.json +1 -0
- package/.output/public/_nuxt/channel-templates.DkNZg-lF.css +1 -0
- package/.output/public/_nuxt/{JAM4aNC_.js → evt5VGDh.js} +1 -1
- package/.output/public/_nuxt/{DhFv8S2Z.js → mNnuUoSy.js} +1 -1
- package/.output/public/_nuxt/{CU-mhG0-.js → mgWHSRc8.js} +1 -1
- package/.output/public/_nuxt/{jRTz0hjN2.js → qPsch_ny2.js} +1 -1
- package/.output/public/_nuxt/{lWYQbIR3.js → ty5W5QwO.js} +1 -1
- package/.output/public/_nuxt/{DMYYS5sK.js → xQX5r6tW.js} +1 -1
- package/.output/server/chunks/_/character-asset.repo.mjs +1 -1
- package/.output/server/chunks/_/entity.mjs +1 -1
- package/.output/server/chunks/_/host-bindings.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +1123 -881
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/scene-layout.repo.mjs +1 -1
- package/.output/server/chunks/build/TownView-fP9BiALv.mjs +1 -1
- package/.output/server/chunks/build/{_wsId_-DPy2BXRd.mjs → _wsId_-C4L9fVHC.mjs} +2 -2
- package/.output/server/chunks/build/{_wsId_-DPy2BXRd.mjs.map → _wsId_-C4L9fVHC.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-CDLcboCv.mjs → agents-ydYu-Ol9.mjs} +6 -6
- package/.output/server/chunks/build/{agents-CDLcboCv.mjs.map → agents-ydYu-Ol9.mjs.map} +1 -1
- package/.output/server/chunks/build/{channel-templates-DjEsj0_v.mjs → channel-templates-DP1ndA5K.mjs} +114 -25
- package/.output/server/chunks/build/channel-templates-DP1ndA5K.mjs.map +1 -0
- package/.output/server/chunks/build/daq-N69AOtgw.mjs +1 -1
- package/.output/server/chunks/build/dcw-CN9d3Rs7.mjs +1 -1
- package/.output/server/chunks/build/{pages-rZKH72eC.mjs → pages-BzmrmLL_.mjs} +3 -3
- package/.output/server/chunks/build/{pages-rZKH72eC.mjs.map → pages-BzmrmLL_.mjs.map} +1 -1
- package/.output/server/chunks/build/{schedules-lo4bfgVt.mjs → schedules-BYR-lA1k.mjs} +2 -2
- package/.output/server/chunks/build/{schedules-lo4bfgVt.mjs.map → schedules-BYR-lA1k.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-CAJWvul5.mjs → settings-UyZ78w2e.mjs} +2 -2
- package/.output/server/chunks/build/{settings-CAJWvul5.mjs.map → settings-UyZ78w2e.mjs.map} +1 -1
- package/.output/server/chunks/build/{teams-DBBvIToa.mjs → teams-D-Y5mzU7.mjs} +8 -8
- package/.output/server/chunks/build/{teams-DBBvIToa.mjs.map → teams-D-Y5mzU7.mjs.map} +1 -1
- package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
- package/.output/server/chunks/build/{useAgentHarnessOptions-D6SJ_uUr.mjs → useAgentHarnessOptions-CRyQYYY0.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentHarnessOptions-D6SJ_uUr.mjs.map → useAgentHarnessOptions-CRyQYYY0.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateActions-CdG4g_PF.mjs → useAgentTemplateActions-CaRsOBmB.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentTemplateActions-CdG4g_PF.mjs.map → useAgentTemplateActions-CaRsOBmB.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateForm-a5y-N5sB.mjs → useAgentTemplateForm-ChiIuPYr.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentTemplateForm-a5y-N5sB.mjs.map → useAgentTemplateForm-ChiIuPYr.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplatesCatalog-pBVcUuN3.mjs → useAgentTemplatesCatalog-Bf5hk6Jf.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentTemplatesCatalog-pBVcUuN3.mjs.map → useAgentTemplatesCatalog-Bf5hk6Jf.mjs.map} +1 -1
- package/.output/server/chunks/build/useDaqAddNode-kW7uTSMQ.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailNode-D_vhG0jF.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailParams-JfqHvTxr.mjs +1 -1
- package/.output/server/chunks/build/useDaqFilters-BUQtZ82a.mjs +1 -1
- package/.output/server/chunks/build/useDaqStream-wOxZcMDA.mjs +1 -1
- package/.output/server/chunks/build/useDaqTemplates-BqQRdrIm.mjs +1 -1
- package/.output/server/chunks/build/{useDashboardData-Do_Vsepa.mjs → useDashboardData-CNOY4Bi3.mjs} +2 -2
- package/.output/server/chunks/build/{useDashboardData-Do_Vsepa.mjs.map → useDashboardData-CNOY4Bi3.mjs.map} +1 -1
- package/.output/server/chunks/build/useDcwAddNode-C1HDP3yG.mjs +1 -1
- package/.output/server/chunks/build/useDcwStream-D04kCV3J.mjs +1 -1
- package/.output/server/chunks/build/{useTeamActions-C4n-taBk.mjs → useTeamActions-C51ciLb0.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamActions-C4n-taBk.mjs.map → useTeamActions-C51ciLb0.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamAddMemberDialog-Cp_z3nMa.mjs → useTeamAddMemberDialog-DthpCMP8.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamAddMemberDialog-Cp_z3nMa.mjs.map → useTeamAddMemberDialog-DthpCMP8.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamCreateDialog-C8LKUoe2.mjs → useTeamCreateDialog-Dc2KTwyg.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamCreateDialog-C8LKUoe2.mjs.map → useTeamCreateDialog-Dc2KTwyg.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamDeployDialog-wdBVsNWY.mjs → useTeamDeployDialog-CXpvPnJD.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamDeployDialog-wdBVsNWY.mjs.map → useTeamDeployDialog-CXpvPnJD.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamPluginDialog-CIk8G8nI.mjs → useTeamPluginDialog-DTs-7SSm.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamPluginDialog-CIk8G8nI.mjs.map → useTeamPluginDialog-DTs-7SSm.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamsCatalog-D6TULzmS.mjs → useTeamsCatalog-CcpetYyW.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamsCatalog-D6TULzmS.mjs.map → useTeamsCatalog-CcpetYyW.mjs.map} +1 -1
- package/.output/server/chunks/build/{useWorkshopApi-B1B9vd4m.mjs → useWorkshopApi-CO3ophqt.mjs} +5 -2
- package/.output/server/chunks/build/{useWorkshopApi-B1B9vd4m.mjs.map → useWorkshopApi-CO3ophqt.mjs.map} +1 -1
- package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/grant.post.mjs +54 -0
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/grant.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/revoke.post.mjs +51 -0
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/revoke.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/agent-tools/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id/preview.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/entities/prune.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/env/recheck.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/env/uv.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/env/venv.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index3.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index4.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index6.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index7.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/cancel.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/logs.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/retry.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id/predict.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id/promote.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id/train.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/twin/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +5 -4
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/chat/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/twin-profile.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/twin-profile.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +4 -4
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/mark-good.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id/control.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id/telemetry.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +2 -2
- package/.output/server/chunks/routes/renderer.mjs +2 -2
- package/.output/server/chunks/virtual/entry.mjs +29 -19
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +829 -829
- package/README.md +839 -839
- package/app/composables/workshop/useWorkshopApi.ts +4 -2
- package/app/pages/workshop/channel-templates.vue +43 -4
- package/config.yml +1 -1
- package/docs/cli.en.md +447 -447
- package/docs/cli.md +412 -412
- package/docs/mcp.en.md +27 -0
- package/docs/mcp.md +108 -0
- package/docs/plugins.en.md +1 -1
- package/docs/plugins.md +1 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/i18n/dicts/ctpl.json +37 -32
- package/i18n/locales/en.ts +5 -0
- package/i18n/locales/zh-CN.ts +5 -0
- package/mcp/aw-mcp-server.mjs +796 -0
- package/mcp/discovery.mjs +227 -0
- package/package.json +4 -1
- package/scripts/_audit/kb-team-mock-e2e.mjs +146 -0
- package/scripts/_audit/kb-team-omp-e2e.mjs +229 -0
- package/server/api/workshop/agent-tools/bindings/grant.post.ts +45 -0
- package/server/api/workshop/agent-tools/bindings/revoke.post.ts +42 -0
- package/server/api/workshop/channel-templates/[id]/instantiate.post.ts +2 -1
- package/server/services/workshop/agents/host-tool-bridge/dispatch.ts +6 -1
- package/server/services/workshop/agents/host-tool-bridge/tools/tasks.ts +15 -2
- package/server/services/workshop/agents/host-tool-bridge/tools/team-delegation.ts +104 -0
- package/server/services/workshop/agents/node-bindings.repo.ts +10 -1
- package/server/services/workshop/agents/node-delegation.ts +150 -0
- package/server/services/workshop/agents/tool-classes.ts +2 -0
- package/server/services/workshop/db/database/seed.ts +20 -0
- package/server/services/workshop/runtime/manager/channel-templates.ts +26 -2
- package/server/services/workshop/runtime/manager/types.ts +2 -0
- package/.output/public/_nuxt/8FXd9As6.js +0 -1
- package/.output/public/_nuxt/builds/meta/08ba670b-7512-426b-a0ae-b10cbeb1c985.json +0 -1
- package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +0 -1
- package/.output/server/chunks/build/channel-templates-DjEsj0_v.mjs.map +0 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mcp/discovery.mjs —— AgentWorkShop 运行实例的端口自动发现。
|
|
3
|
+
*
|
|
4
|
+
* 发现优先级(逐级探测,GET /api/health 返回 data.status==='ok' 才算命中):
|
|
5
|
+
* 1. env AW_BASE_URL 显式指定平台地址(如 http://127.0.0.1:3005)
|
|
6
|
+
* 2. env AW_PORT 显式钉住端口(多实例并存时消歧)
|
|
7
|
+
* 3. 锁文件 .runtime/aw.lock { pid, port, mode }——scripts/start.mjs 端口顺延后回写,
|
|
8
|
+
* 是"实例真实监听端口"的唯一权威记录。依次找:
|
|
9
|
+
* a. cwd 向上逐级找 .AgentWorkShop/.runtime/aw.lock(repo 级)
|
|
10
|
+
* b. env AW_HOME/.runtime/aw.lock 与 AW_HOME/.AgentWorkShop/.runtime/aw.lock
|
|
11
|
+
* c. ~/.AgentWorkShop/.runtime/aw.lock(home 级)
|
|
12
|
+
* 4. env PORT / NUXT_PORT 启动惯例变量(scripts/start.mjs 同款优先级)
|
|
13
|
+
* 5. config.yml server.dev.port / server.prod.port(轻量正则,不引 YAML 依赖)
|
|
14
|
+
* 6. 默认端口 3000(dev)/ 3001(prod)
|
|
15
|
+
*
|
|
16
|
+
* 刻意零依赖:本模块被 stdio MCP server(cli 子进程)与 CLI 命令共享,
|
|
17
|
+
* 不得假设仓库 node_modules 的解析路径。
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
20
|
+
import { homedir } from 'node:os'
|
|
21
|
+
import { dirname, join, resolve } from 'node:path'
|
|
22
|
+
|
|
23
|
+
const LOCK_REL = join('.AgentWorkShop', '.runtime', 'aw.lock')
|
|
24
|
+
const HEALTH_PATH = '/api/health'
|
|
25
|
+
const HEALTH_TIMEOUT_MS = 800
|
|
26
|
+
|
|
27
|
+
/** cwd 向上逐级查找含 target 相对路径的目录;找不到返回 null */
|
|
28
|
+
export function walkUpFind(cwd, target) {
|
|
29
|
+
let dir = resolve(cwd)
|
|
30
|
+
for (;;) {
|
|
31
|
+
const hit = join(dir, target)
|
|
32
|
+
if (existsSync(hit)) return hit
|
|
33
|
+
const parent = dirname(dir)
|
|
34
|
+
if (parent === dir) return null
|
|
35
|
+
dir = parent
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 读锁文件 → { pid, port, mode, startedAt } | null(损坏/越界端口一律忽略) */
|
|
40
|
+
export function readLock(lockPath) {
|
|
41
|
+
try {
|
|
42
|
+
const raw = JSON.parse(readFileSync(lockPath, 'utf8'))
|
|
43
|
+
const port = Number(raw?.port)
|
|
44
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535) return null
|
|
45
|
+
return { pid: raw?.pid ?? null, port, mode: raw?.mode ?? null, startedAt: raw?.startedAt ?? null }
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** GET /api/health 探活;命中返回 data(含 status/app/version/mode),未命中返回 null */
|
|
53
|
+
export async function probeHealth(base, timeoutMs = HEALTH_TIMEOUT_MS) {
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(`${base.replace(/\/$/, '')}${HEALTH_PATH}`, { signal: AbortSignal.timeout(timeoutMs) })
|
|
56
|
+
if (!res.ok) return null
|
|
57
|
+
const json = await res.json().catch(() => null)
|
|
58
|
+
const data = json?.data
|
|
59
|
+
return data?.status === 'ok' ? data : null
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function pushCandidate(list, seen, cand) {
|
|
67
|
+
const key = cand.base ?? `port:${cand.port}`
|
|
68
|
+
if (seen.has(key)) return
|
|
69
|
+
seen.add(key)
|
|
70
|
+
list.push(cand)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 轻量解析 config.yml 的 server.dev.port / server.prod.port(嵌套格式,零依赖)。
|
|
75
|
+
* 只关心 server: 块下 dev:/prod: 子块的 port:,不引 YAML 依赖。
|
|
76
|
+
*/
|
|
77
|
+
export function parseServerPorts(text) {
|
|
78
|
+
const ports = {}
|
|
79
|
+
let inServer = false
|
|
80
|
+
let sub = null
|
|
81
|
+
for (const line of String(text).split(/\r?\n/)) {
|
|
82
|
+
const t = line.trim()
|
|
83
|
+
if (!t || t.startsWith('#')) continue
|
|
84
|
+
const indent = line.length - line.trimStart().length
|
|
85
|
+
if (indent === 0) {
|
|
86
|
+
inServer = t === 'server:'
|
|
87
|
+
sub = null
|
|
88
|
+
continue
|
|
89
|
+
}
|
|
90
|
+
if (!inServer) continue
|
|
91
|
+
if (indent <= 2) {
|
|
92
|
+
sub = /^(dev|prod):$/.test(t) ? t.slice(0, -1) : null
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
if (sub && t.startsWith('port:')) {
|
|
96
|
+
const p = Number(t.slice(5).trim())
|
|
97
|
+
if (Number.isInteger(p) && p > 0) ports[sub] = p
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return ports
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 组装候选(按优先级,不去重探测)。返回 [{ kind, source, base?, port? }]
|
|
105
|
+
* kind: 'url' | 'lock' | 'envport' | 'config' | 'default'
|
|
106
|
+
*/
|
|
107
|
+
export function buildCandidates({ cwd = process.cwd(), env = process.env, defaults } = {}) {
|
|
108
|
+
const cands = []
|
|
109
|
+
const seen = new Set()
|
|
110
|
+
const envUrl = env.AW_BASE_URL?.trim()
|
|
111
|
+
if (envUrl) pushCandidate(cands, seen, { kind: 'url', source: 'env:AW_BASE_URL', base: envUrl.replace(/\/$/, '') })
|
|
112
|
+
|
|
113
|
+
const pinPort = Number(env.AW_PORT)
|
|
114
|
+
if (Number.isInteger(pinPort) && pinPort > 0) pushCandidate(cands, seen, { kind: 'envport', source: 'env:AW_PORT', port: pinPort })
|
|
115
|
+
|
|
116
|
+
const lockPaths = []
|
|
117
|
+
const awHome = env.AW_HOME?.trim()
|
|
118
|
+
const repoLock = walkUpFind(cwd, LOCK_REL)
|
|
119
|
+
if (repoLock) lockPaths.push({ path: repoLock, source: `lock:${repoLock}` })
|
|
120
|
+
if (awHome) {
|
|
121
|
+
// 显式 AW_HOME = 操作者指定的配置根:优先于隐式的 cwd 向上发现,
|
|
122
|
+
// 且不再回落全局 ~/.AgentWorkShop(多实例隔离语义)
|
|
123
|
+
lockPaths.unshift(
|
|
124
|
+
{ path: join(resolve(awHome), '.runtime', 'aw.lock'), source: `lock(AW_HOME):${awHome}` },
|
|
125
|
+
{ path: join(resolve(awHome), LOCK_REL), source: `lock(AW_HOME):${join(awHome, '.AgentWorkShop')}` },
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const homeLock = join(homedir(), LOCK_REL)
|
|
130
|
+
lockPaths.push({ path: homeLock, source: `lock:${homeLock}` })
|
|
131
|
+
}
|
|
132
|
+
for (const { path, source } of lockPaths) {
|
|
133
|
+
const lock = existsSync(path) ? readLock(path) : null
|
|
134
|
+
if (lock) pushCandidate(cands, seen, { kind: 'lock', source, port: lock.port, lock })
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const key of ['PORT', 'NUXT_PORT']) {
|
|
138
|
+
const p = Number(env[key])
|
|
139
|
+
if (Number.isInteger(p) && p > 0) pushCandidate(cands, seen, { kind: 'envport', source: `env:${key}`, port: p })
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const configYaml = walkUpFind(cwd, 'config.yml')
|
|
143
|
+
if (configYaml) {
|
|
144
|
+
try {
|
|
145
|
+
const ports = parseServerPorts(readFileSync(configYaml, 'utf8'))
|
|
146
|
+
if (ports.dev) pushCandidate(cands, seen, { kind: 'config', source: `config:server.dev.port(${configYaml})`, port: ports.dev })
|
|
147
|
+
if (ports.prod) pushCandidate(cands, seen, { kind: 'config', source: `config:server.prod.port(${configYaml})`, port: ports.prod })
|
|
148
|
+
}
|
|
149
|
+
catch { /* 读不了就跳过 */ }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const p of defaults ?? ['3000', '3001']) {
|
|
153
|
+
const port = Number(p)
|
|
154
|
+
if (Number.isInteger(port) && port > 0) pushCandidate(cands, seen, { kind: 'default', source: `default:${port}`, port })
|
|
155
|
+
}
|
|
156
|
+
return cands
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 自动发现运行中的 AgentWorkShop 实例。
|
|
161
|
+
* 返回 { base, port|null, source, health, candidates } | null。
|
|
162
|
+
* candidates 里每项附 ok 与 health,便于 aw_status/诊断展示"为什么没连上"。
|
|
163
|
+
*/
|
|
164
|
+
export async function discoverInstance(opts = {}) {
|
|
165
|
+
const timeoutMs = opts.timeoutMs ?? HEALTH_TIMEOUT_MS
|
|
166
|
+
const cands = buildCandidates(opts)
|
|
167
|
+
const probed = []
|
|
168
|
+
for (const cand of cands) {
|
|
169
|
+
const base = cand.base ?? `http://127.0.0.1:${cand.port}`
|
|
170
|
+
const health = await probeHealth(base, timeoutMs)
|
|
171
|
+
probed.push({ ...cand, base, ok: health != null, health })
|
|
172
|
+
if (health) {
|
|
173
|
+
return { base, port: cand.port ?? (Number(new URL(base).port) || null), source: cand.source, health, candidates: probed }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** 供 aw_status 展示:只探测不命中也算信息(列出全部候选与失败原因形态) */
|
|
180
|
+
export async function discoverWithReport(opts = {}) {
|
|
181
|
+
const cands = buildCandidates(opts)
|
|
182
|
+
const probed = []
|
|
183
|
+
for (const cand of cands) {
|
|
184
|
+
const base = cand.base ?? `http://127.0.0.1:${cand.port}`
|
|
185
|
+
const health = await probeHealth(base, opts.timeoutMs ?? HEALTH_TIMEOUT_MS)
|
|
186
|
+
probed.push({ source: cand.source, base, ok: health != null, health: health ?? null })
|
|
187
|
+
}
|
|
188
|
+
const hit = probed.find(p => p.ok)
|
|
189
|
+
return { hit: hit ?? null, candidates: probed }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 解析本实例的配置根(与锁文件发现同优先级):
|
|
194
|
+
* env AW_HOME > cwd 向上找 .AgentWorkShop > ~/.AgentWorkShop
|
|
195
|
+
*/
|
|
196
|
+
export function findConfigRoot(opts = {}) {
|
|
197
|
+
const env = opts.env ?? process.env
|
|
198
|
+
const cwd = opts.cwd ?? process.cwd()
|
|
199
|
+
const awHome = env.AW_HOME?.trim()
|
|
200
|
+
if (awHome) return resolve(awHome)
|
|
201
|
+
const repoRoot = walkUpFind(cwd, join('.AgentWorkShop', '.runtime', 'aw.lock'))
|
|
202
|
+
if (repoRoot) return dirname(dirname(repoRoot))
|
|
203
|
+
const repoRootDir = walkUpFind(cwd, join('.AgentWorkShop', 'runtime-settings.json'))
|
|
204
|
+
if (repoRootDir) return dirname(repoRootDir)
|
|
205
|
+
return join(homedir(), '.AgentWorkShop')
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 读 MCP 集成开关(系统设置 → MCP 集成 → 启动 MCP 集成)。
|
|
210
|
+
* 优先级与平台一致:env AW_MCP_ENABLED > <configRoot>/runtime-settings.json 的 overrides["mcp.enabled"] > 默认 false。
|
|
211
|
+
* 文件缺失/损坏一律视为 false(fail-closed:开关语义宁可拒绝)。
|
|
212
|
+
*/
|
|
213
|
+
export function readMcpEnabled(opts = {}) {
|
|
214
|
+
const env = opts.env ?? process.env
|
|
215
|
+
const envVal = env.AW_MCP_ENABLED?.trim()
|
|
216
|
+
if (envVal !== undefined && envVal !== '') {
|
|
217
|
+
return /^(1|true|on|yes)$/i.test(envVal)
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const root = opts.configRoot ?? findConfigRoot(opts)
|
|
221
|
+
const raw = JSON.parse(readFileSync(join(root, 'runtime-settings.json'), 'utf8'))
|
|
222
|
+
return raw?.overrides?.['mcp.enabled'] === true
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentworkshop",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgentWorkShop \u8f6f\u4ef6\u5f00\u53d1\u7cfb\u7edf - \u57fa\u4e8e Nuxt 4 \u7684\u914d\u7f6e\u9a71\u52a8\u8fd0\u884c\u8303\u5f0f",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"scripts",
|
|
37
37
|
".AgentWorkShop/prompts",
|
|
38
38
|
"server/plugins-builtin",
|
|
39
|
+
"mcp",
|
|
39
40
|
"config.yml",
|
|
40
41
|
"nuxt.config.ts",
|
|
41
42
|
"tsconfig.json",
|
|
@@ -50,6 +51,8 @@
|
|
|
50
51
|
"docs/sdk.md",
|
|
51
52
|
"docs/sdk.en.md",
|
|
52
53
|
"docs/aml.md",
|
|
54
|
+
"docs/mcp.md",
|
|
55
|
+
"docs/mcp.en.md",
|
|
53
56
|
"docs/tui.md",
|
|
54
57
|
"docs/full-test-plan.md",
|
|
55
58
|
"docs/multi-harness-architecture.md",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/_audit/kb-team-mock-e2e.mjs —— 知识库集成 Channel + lead→worker 节点委派(mock 阶段验收)。
|
|
4
|
+
*
|
|
5
|
+
* 覆盖(无 LLM 依赖):
|
|
6
|
+
* 1) 模板知识库能力面:chtpl-generic-optimize-default / chtpl-aml-optimization-default 标记 knowledgeBaseCapable;
|
|
7
|
+
* 2) 实例化 enableKnowledgeBase=true → 频道插件开关含 rag-bridge=on,场景提示词含 KB 作业段;
|
|
8
|
+
* 3) 实例化 enableKnowledgeBase=false → rag-bridge=off;缺省 → 无显式行(平台默认);
|
|
9
|
+
* 4) 委派 REST:lead 绑定面内节点授予 worker(grantedByAgentId 溯源);
|
|
10
|
+
* 5) fail-closed:越面授予 403 / 授予 lead 自己 400;
|
|
11
|
+
* 6) 执行中授予:worker 有在飞任务时仍可被授予新节点;
|
|
12
|
+
* 7) 撤销:收回授予的节点;非 lead 授予且 lead 不持有的绑定不被误撤。
|
|
13
|
+
*
|
|
14
|
+
* 用法:AW_BASE=http://127.0.0.1:3456 node scripts/_audit/kb-team-mock-e2e.mjs
|
|
15
|
+
* 前置:全新实例(首注册即 admin)。脚本自行注册 admin 账号并搭一条 mock 产线。
|
|
16
|
+
*/
|
|
17
|
+
const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3456'
|
|
18
|
+
const ADMIN = { email: process.env.AW_ADMIN_EMAIL ?? 'admin@awshop.local', password: process.env.AW_ADMIN_PASSWORD ?? 'Awshop@123!' }
|
|
19
|
+
|
|
20
|
+
let pass = 0
|
|
21
|
+
const fails = []
|
|
22
|
+
const ok = (name, cond, detail = '') => {
|
|
23
|
+
if (cond) { pass++; console.log(` PASS ${name}${detail ? ` — ${detail}` : ''}`) }
|
|
24
|
+
else { fails.push(name); console.log(` FAIL ${name}${detail ? ` — ${detail}` : ''}`) }
|
|
25
|
+
}
|
|
26
|
+
const section = t => console.log(`\n━━━ ${t} ━━━`)
|
|
27
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
28
|
+
|
|
29
|
+
async function api(method, path, { body, token } = {}) {
|
|
30
|
+
const res = await fetch(BASE + path, {
|
|
31
|
+
method,
|
|
32
|
+
headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) },
|
|
33
|
+
body: body != null ? JSON.stringify(body) : undefined,
|
|
34
|
+
signal: AbortSignal.timeout(30000),
|
|
35
|
+
})
|
|
36
|
+
const json = await res.json().catch(() => null)
|
|
37
|
+
return { status: res.status, json }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const TAG = `kbmock-${new Date().toISOString().slice(5, 16).replace(/[-T:]/g, '')}`
|
|
41
|
+
|
|
42
|
+
async function main() {
|
|
43
|
+
section('S0 管理员就绪(全新实例首注册即 admin)')
|
|
44
|
+
const reg = await api('POST', '/api/users/register', { body: { ...ADMIN, name: 'kb-mock-admin' } })
|
|
45
|
+
const token = reg.json?.data?.token ?? (await api('POST', '/api/users/login', { body: { email: ADMIN.email, password: ADMIN.password } })).json?.data?.token
|
|
46
|
+
ok('管理员 token 可用', Boolean(token))
|
|
47
|
+
const auth = { token }
|
|
48
|
+
|
|
49
|
+
section('S1 模板知识库能力面')
|
|
50
|
+
const tplList = (await api('GET', '/api/workshop/channel-templates', auth)).json?.data ?? []
|
|
51
|
+
const generic = tplList.find(t => t.id === 'chtpl-generic-optimize-default')
|
|
52
|
+
const optTpl = tplList.find(t => t.id === 'chtpl-aml-optimization-default')
|
|
53
|
+
ok('内置模板存在「通用闭环优化频道(标准)」', Boolean(generic), generic?.name)
|
|
54
|
+
ok('通用模板标记 knowledgeBaseCapable', generic?.knowledgeBaseCapable === true)
|
|
55
|
+
ok('工艺优化通道标记 knowledgeBaseCapable', optTpl?.knowledgeBaseCapable === true)
|
|
56
|
+
ok('其他模板不带该标记', tplList.find(t => t.id === 'chtpl-default-fullstack')?.knowledgeBaseCapable !== true)
|
|
57
|
+
|
|
58
|
+
section('S2 实例化:enableKnowledgeBase=true → rag-bridge 开 + KB 作业段')
|
|
59
|
+
const inst1 = (await api('POST', '/api/workshop/channel-templates/chtpl-generic-optimize-default/instantiate', { body: { name: `${TAG}-kb-on`, enableKnowledgeBase: true }, ...auth })).json?.data
|
|
60
|
+
ok('通用频道实例化成功', Boolean(inst1?.channelId), `agents=${inst1?.agentCount}`)
|
|
61
|
+
const ch1 = (await api('GET', `/api/workshop/channels/${inst1.channelId}`, auth)).json?.data
|
|
62
|
+
ok('场景提示词含知识库作业段', String(ch1?.scenarioPrompt ?? '').includes('知识库集成') && String(ch1?.scenarioPrompt ?? '').includes('kb_agent'))
|
|
63
|
+
const plug1 = (await api('GET', `/api/workshop/channels/${inst1.channelId}/plugins`, auth)).json?.data
|
|
64
|
+
const rb1 = (Array.isArray(plug1?.plugins) ? plug1.plugins : plug1 ?? []).find?.(p => p?.name === 'rag-bridge')
|
|
65
|
+
?? (Array.isArray(plug1) ? plug1 : []).find(p => p?.name === 'rag-bridge')
|
|
66
|
+
ok('频道插件开关含 rag-bridge=on', Boolean(rb1 && rb1.enabled !== false), JSON.stringify(rb1 ?? plug1 ?? {}).slice(0, 120))
|
|
67
|
+
const members1 = (await api('GET', `/api/workshop/channels/${inst1.channelId}/agents`, auth)).json?.data ?? []
|
|
68
|
+
ok('频道含 lead + worker', members1.some(m => m.role === 'lead') && members1.filter(m => m.role === 'worker').length >= 2, `members=${members1.length}`)
|
|
69
|
+
|
|
70
|
+
section('S3 实例化:enableKnowledgeBase=false 与缺省行为')
|
|
71
|
+
const inst2 = (await api('POST', '/api/workshop/channel-templates/chtpl-aml-optimization-default/instantiate', { body: { name: `${TAG}-kb-off`, enableKnowledgeBase: false }, ...auth })).json?.data
|
|
72
|
+
const plug2 = (await api('GET', `/api/workshop/channels/${inst2.channelId}/plugins`, auth)).json?.data
|
|
73
|
+
const flat2 = Array.isArray(plug2?.plugins) ? plug2.plugins : (Array.isArray(plug2) ? plug2 : [])
|
|
74
|
+
ok('显式 false → rag-bridge=off', flat2.some(p => p?.name === 'rag-bridge' && p.enabled === false), JSON.stringify(flat2).slice(0, 120))
|
|
75
|
+
const inst3 = (await api('POST', '/api/workshop/channel-templates/chtpl-aml-optimization-default/instantiate', { body: { name: `${TAG}-kb-default` }, ...auth })).json?.data
|
|
76
|
+
const plug3 = (await api('GET', `/api/workshop/channels/${inst3.channelId}/plugins`, auth)).json?.data
|
|
77
|
+
const flat3 = Array.isArray(plug3?.plugins) ? plug3.plugins : (Array.isArray(plug3) ? plug3 : [])
|
|
78
|
+
const rb3 = flat3.find(p => p?.name === 'rag-bridge')
|
|
79
|
+
ok('缺省 → rag-bridge 保持平台默认(未被显式停用)', !rb3 || rb3.enabled !== false, JSON.stringify(flat3).slice(0, 120))
|
|
80
|
+
|
|
81
|
+
section('S4 搭 mock 产线(建线 + 模板 + 数采/数控节点)')
|
|
82
|
+
const line = (await api('POST', '/api/workshop/dcw/lines', { body: { name: `KBMock线-${TAG}`, description: 'kb-team mock e2e' }, ...auth })).json?.data
|
|
83
|
+
const lineId = line?.line?.id ?? line?.id
|
|
84
|
+
ok('建线成功', Boolean(lineId), lineId)
|
|
85
|
+
const dcwTpl = (await api('POST', '/api/workshop/dcw/templates', { body: { name: `kbmock-sp-tpl-${TAG}`, ch: '温度设定', unit: '℃', min: 150, max: 220, decimals: 1 }, ...auth })).json?.data
|
|
86
|
+
const daqTpl = (await api('POST', '/api/workshop/daq/templates', { body: { name: `kbmock-temp-tpl-${TAG}`, ch: '温度', unit: '℃', min: 0, max: 300, decimals: 1 }, ...auth })).json?.data
|
|
87
|
+
const dcwTplRef = dcwTpl?.template?.key ?? dcwTpl?.key
|
|
88
|
+
const daqTplRef = daqTpl?.template?.key ?? daqTpl?.key
|
|
89
|
+
ok('节点模板创建成功', Boolean(dcwTplRef) && Boolean(daqTplRef), `dcw=${dcwTplRef} daq=${daqTplRef}`)
|
|
90
|
+
const dcw = (await api('POST', '/api/workshop/dcw', { body: { name: `kbmock-sp-${TAG}`, templateRef: dcwTplRef, driver: 'mock', unit: '℃', min: 150, max: 220, stepLimit: 5, lineId }, ...auth })).json
|
|
91
|
+
const daq = (await api('POST', '/api/workshop/daq', { body: { name: `kbmock-temp-${TAG}`, templateRef: daqTplRef, driver: 'mock', intervalMs: 1000, publishIntervalMs: 0, lineId }, ...auth })).json
|
|
92
|
+
const dcwId = dcw?.data?.node?.id
|
|
93
|
+
const daqId = daq?.data?.node?.id
|
|
94
|
+
ok('数采/数控节点创建成功', Boolean(daqId) && Boolean(dcwId), `daq=${daqId} dcw=${dcwId}`)
|
|
95
|
+
|
|
96
|
+
section('S5 lead 绑定 + 委派核心面')
|
|
97
|
+
const lead = members1.find(m => m.role === 'lead')
|
|
98
|
+
const worker = members1.find(m => m.role === 'worker')
|
|
99
|
+
const b1 = await api('POST', '/api/workshop/agent-tools/bindings', { body: { agentId: lead.id, nodeId: dcwId, kind: 'dcw', mode: 'manual' }, ...auth })
|
|
100
|
+
const b2 = await api('POST', '/api/workshop/agent-tools/bindings', { body: { agentId: lead.id, nodeId: daqId, kind: 'daq', mode: 'auto' }, ...auth })
|
|
101
|
+
ok('lead 绑定 dcw+daq 成功', b1.status === 200 && b2.status === 200, `dcw=${b1.status} daq=${b2.status}`)
|
|
102
|
+
|
|
103
|
+
const grant = await api('POST', '/api/workshop/agent-tools/bindings/grant', { body: { channelId: inst1.channelId, agentId: worker.id, nodeIds: [dcwId, daqId] }, ...auth })
|
|
104
|
+
ok('委派授予成功(整单)', grant.status === 200, JSON.stringify(grant.json?.data ?? grant.json).slice(0, 120))
|
|
105
|
+
const granted = grant.json?.data?.granted ?? []
|
|
106
|
+
ok('授予绑定带 lead 溯源', granted.length === 2 && granted.every(g => g.grantedByAgentId === lead.id), `n=${granted.length}`)
|
|
107
|
+
ok('dcw 委派缺省随 lead 的 manual 模式', granted.find(g => g.kind === 'dcw')?.mode === 'manual')
|
|
108
|
+
const workerBindings = (await api('GET', `/api/workshop/agent-tools/bindings?agentId=${worker.id}`, auth)).json?.data
|
|
109
|
+
const wbFlat = Array.isArray(workerBindings) ? workerBindings : (workerBindings?.bindings ?? [])
|
|
110
|
+
ok('worker 绑定面已含委派节点', wbFlat.some(b => b.nodeId === dcwId) && wbFlat.some(b => b.nodeId === daqId), `n=${wbFlat.length}`)
|
|
111
|
+
|
|
112
|
+
section('S6 fail-closed:越面授予 / 授予 lead 自己')
|
|
113
|
+
const bad = await api('POST', '/api/workshop/agent-tools/bindings/grant', { body: { channelId: inst1.channelId, agentId: worker.id, nodeIds: [`ghost-${TAG}`] }, ...auth })
|
|
114
|
+
const badCode = bad.json?.code ?? bad.json?.data?.code
|
|
115
|
+
ok('越面授予被拒(403 DELEGATION_NOT_OWNED)', bad.status === 403 && badCode === 'DELEGATION_NOT_OWNED', `status=${bad.status} code=${badCode}`)
|
|
116
|
+
const self = await api('POST', '/api/workshop/agent-tools/bindings/grant', { body: { channelId: inst1.channelId, agentId: lead.id, nodeIds: [dcwId] }, ...auth })
|
|
117
|
+
ok('授予 lead 自己被拒(400)', self.status === 400, `status=${self.status}`)
|
|
118
|
+
|
|
119
|
+
section('S7 执行中授予(worker 有在飞任务时仍可授权)')
|
|
120
|
+
const task = (await api('POST', `/api/workshop/channels/${inst1.channelId}/tasks`, { body: { title: `巡检-${TAG}`, description: '读取授权节点并汇报(委派执行中授予验证)', assigneeId: worker.id }, ...auth })).json?.data
|
|
121
|
+
ok('worker 根任务已创建', Boolean(task?.id), task?.id)
|
|
122
|
+
await sleep(500)
|
|
123
|
+
const midGrant = await api('POST', '/api/workshop/agent-tools/bindings/grant', { body: { channelId: inst1.channelId, agentId: worker.id, nodeIds: [dcwId], mode: 'auto' }, ...auth })
|
|
124
|
+
ok('执行中授予成功(模式覆盖 auto)', midGrant.status === 200, JSON.stringify(midGrant.json?.data ?? {}).slice(0, 100))
|
|
125
|
+
await api('POST', `/api/workshop/channels/${inst1.channelId}/tasks/${task.id}/cancel`, { ...auth }).catch(() => {})
|
|
126
|
+
|
|
127
|
+
section('S8 撤销')
|
|
128
|
+
const revoke = await api('POST', '/api/workshop/agent-tools/bindings/revoke', { body: { channelId: inst1.channelId, agentId: worker.id, nodeIds: [daqId] }, ...auth })
|
|
129
|
+
const results = revoke.json?.data?.results ?? []
|
|
130
|
+
ok('撤销 daq 委派成功', revoke.status === 200 && results.every(r => r.revoked), JSON.stringify(results).slice(0, 100))
|
|
131
|
+
const afterRevoke = (await api('GET', `/api/workshop/agent-tools/bindings?agentId=${worker.id}`, auth)).json?.data
|
|
132
|
+
const arFlat = Array.isArray(afterRevoke) ? afterRevoke : (afterRevoke?.bindings ?? [])
|
|
133
|
+
ok('撤销后 worker 不再持有该节点', !arFlat.some(b => b.nodeId === daqId), `剩余=${arFlat.length}`)
|
|
134
|
+
|
|
135
|
+
console.log(`\n════ 结果: ${pass} PASS / ${fails.length} FAIL ════`)
|
|
136
|
+
if (fails.length) {
|
|
137
|
+
console.log('失败项:')
|
|
138
|
+
for (const f of fails) console.log(` - ${f}`)
|
|
139
|
+
process.exit(1)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
main().catch((err) => {
|
|
144
|
+
console.error('E2E 致命异常:', err)
|
|
145
|
+
process.exit(1)
|
|
146
|
+
})
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/_audit/kb-team-omp-e2e.mjs —— 知识库集成通用闭环优化频道 × 真实 omp × 多 PLC 模拟场景。
|
|
4
|
+
*
|
|
5
|
+
* 每个 scenario:
|
|
6
|
+
* ① 模拟器建场(ensureScenarioLine) + 平台建线开跑(provisionScenarioLine,真实协议链路);
|
|
7
|
+
* ② 实例化「通用闭环优化频道(标准)」模板,enableKnowledgeBase=true(频道加载 rag-bridge);
|
|
8
|
+
* ③ lead 绑定 PV 数采节点 + 调节数控节点(auto);
|
|
9
|
+
* ④ 提交优化 goal(要求:先查知识库 → lead 委派节点权限 → 治理小步下发复测 → 判定 → 经验沉淀);
|
|
10
|
+
* ⑤ 期间轮询任务/消息/事件,全程转录落盘(transcript.md + 原始 JSON);
|
|
11
|
+
* ⑥ 验收:根任务收口 + worker 确实被授予节点(grantedBy 溯源) + 发生过治理写入
|
|
12
|
+
* + PV 回带 + 知识库经验新增(kb_agent 异步落库)。
|
|
13
|
+
*
|
|
14
|
+
* 用法:
|
|
15
|
+
* node scripts/_audit/kb-team-omp-e2e.mjs --base http://127.0.0.1:3456 --scenarios injection,wwtp
|
|
16
|
+
* 前置:目标实例为本仓库构建(内置通用模板 + rag-bridge);rag-bridge token 已配置(启 KB 鉴权时);
|
|
17
|
+
* omp CLI 在 PATH 且可用;模拟器可自举(bench/lib/sim.mjs ensureSimulator)。
|
|
18
|
+
*/
|
|
19
|
+
import { join, dirname, resolve } from 'node:path'
|
|
20
|
+
import { writeFileSync, appendFileSync } from 'node:fs'
|
|
21
|
+
import { fileURLToPath } from 'node:url'
|
|
22
|
+
import { makeApi, ensureDir, sleep } from '../../bench/lib/util.mjs'
|
|
23
|
+
import { ensureSimulator, simUp } from '../../bench/lib/sim.mjs'
|
|
24
|
+
import { SCENARIOS, ensureScenarioLine, provisionScenarioLine, readPvMean } from '../../bench/lib/scenarios.mjs'
|
|
25
|
+
|
|
26
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
27
|
+
const REPO = resolve(HERE, '../..')
|
|
28
|
+
const args = process.argv.slice(2)
|
|
29
|
+
const arg = (k, d) => { const i = args.indexOf(`--${k}`); return i >= 0 ? (args[i + 1] ?? '') : d }
|
|
30
|
+
|
|
31
|
+
const BASE = arg('base', process.env.AW_BASE ?? 'http://127.0.0.1:3456')
|
|
32
|
+
const list = (arg('scenarios', 'injection') || 'injection').split(',').map(s => s.trim()).filter(id => SCENARIOS[id])
|
|
33
|
+
const TASK_TIMEOUT_MS = Number(arg('timeout-min', '32')) * 60_000
|
|
34
|
+
const KB_WAIT_MS = Number(arg('kb-wait-min', '6')) * 60_000
|
|
35
|
+
const POLL_MS = 20_000
|
|
36
|
+
const sfx = arg('sfx', `kb${new Date().toISOString().slice(5, 16).replace(/[-T:]/g, '')}`)
|
|
37
|
+
|
|
38
|
+
const rid = `kb-team-omp-${new Date().toISOString().slice(0, 19).replace(/[-T:]/g, '')}`
|
|
39
|
+
const outDir = ensureDir(join(REPO, 'bench', 'results', rid))
|
|
40
|
+
console.log(`=== kb-team × omp 闭环优化 × 多场景 ===\nbase=${BASE} scenarios=${list.join(',')} out=${outDir}\n`)
|
|
41
|
+
|
|
42
|
+
// ── 自举 ──
|
|
43
|
+
if (!(await simUp())) {
|
|
44
|
+
const r = await ensureSimulator({ log: console.log })
|
|
45
|
+
if (!r.started && !(await simUp())) { console.error(`✘ 模拟器自举失败: ${r.reason}`); process.exit(1) }
|
|
46
|
+
}
|
|
47
|
+
const api = makeApi(BASE)
|
|
48
|
+
const login = await api.login(process.env.AW_ADMIN_EMAIL ?? 'admin@awshop.local', process.env.AW_ADMIN_PASSWORD ?? 'Awshop@123!')
|
|
49
|
+
if (!login.ok) { console.error('✘ 平台登录失败'); process.exit(1) }
|
|
50
|
+
console.log(`平台就绪(${login.how},role=${login.role});模拟器就绪\n`)
|
|
51
|
+
|
|
52
|
+
const globalResult = { rid, base: BASE, scenarios: [] }
|
|
53
|
+
|
|
54
|
+
for (const id of list) {
|
|
55
|
+
const scen = SCENARIOS[id]
|
|
56
|
+
const dir = ensureDir(join(outDir, id))
|
|
57
|
+
const res = { scenario: id, zh: scen.zh, checks: {}, errors: [] }
|
|
58
|
+
globalResult.scenarios.push(res)
|
|
59
|
+
const log = (line) => { console.log(`[${id}] ${line}`); appendFileSync(join(dir, 'transcript.md'), `${line}\n`) }
|
|
60
|
+
writeFileSync(join(dir, 'transcript.md'), `# ${scen.zh}(kb-team × omp)作业转录\n\n- 开始: ${new Date().toISOString()}\n- 频道模板: chtl-generic-optimize-default(enableKnowledgeBase=true)\n\n`)
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// ── ① 建场 + 建线 ──
|
|
64
|
+
log(`① 模拟器建场 ${scen.key} …`)
|
|
65
|
+
const ensure = await ensureScenarioLine(scen)
|
|
66
|
+
log(` 设备=${ensure.verified.devices}/${ensure.verified.expectDevices} SP=${ensure.verified.sp} PV=${ensure.verified.pv} 协议=${ensure.verified.protocols.join('/')}`)
|
|
67
|
+
const line = await provisionScenarioLine(api, scen, { sfx: `${sfx}-${id}` })
|
|
68
|
+
if (line.errors?.length || !line.ids?.lineId) throw new Error(`建线失败: ${line.errors?.join(';')}`)
|
|
69
|
+
res.line = { lineId: line.ids.lineId, recipeId: line.ids.recipeId, started: line.ids.started, reused: line.reused }
|
|
70
|
+
log(`① 平台建线 ${line.lineName} started=${line.ids.started} dcw=${Object.keys(line.dcw).length} daq=${Object.keys(line.daq).length}`)
|
|
71
|
+
|
|
72
|
+
// ── ② 实例化 KB 通用频道 ──
|
|
73
|
+
const inst = (await api.call('POST', '/api/workshop/channel-templates/chtpl-generic-optimize-default/instantiate', {
|
|
74
|
+
name: `KB闭环优化-${scen.zh}-${sfx}-${id}`,
|
|
75
|
+
enableKnowledgeBase: true,
|
|
76
|
+
}))
|
|
77
|
+
const channelId = inst.data?.channelId
|
|
78
|
+
if (!channelId) throw new Error(`频道实例化失败: ${inst.message}`)
|
|
79
|
+
const agents = (await api.call('GET', `/api/workshop/channels/${channelId}/agents`)).data ?? []
|
|
80
|
+
const lead = agents.find(a => a.role === 'lead')
|
|
81
|
+
res.channel = { channelId, leadId: lead?.id, leadHarness: lead?.harness }
|
|
82
|
+
log(`② 频道 ${channelId} 已实例化(lead=${lead?.name}/${lead?.harness},成员=${agents.length})`)
|
|
83
|
+
|
|
84
|
+
// ── ③ lead 绑定 PV 数采 + 调节数控(auto) ──
|
|
85
|
+
const pvNode = line.daq[scen.pv.sig]
|
|
86
|
+
const knobSigs = (scen.knobs ?? []).map(k => k.sig).slice(0, 2)
|
|
87
|
+
const knobNodes = knobSigs.map(s => line.dcw[s]).filter(Boolean)
|
|
88
|
+
if (!pvNode || !knobNodes.length) throw new Error(`关键节点缺失: pv=${pvNode} knobs=${knobNodes}`)
|
|
89
|
+
for (const [nodeId, kind] of [[pvNode, 'daq'], ...knobNodes.map(n => [n, 'dcw'])]) {
|
|
90
|
+
const r = await api.call('POST', '/api/workshop/agent-tools/bindings', { agentId: lead.id, nodeId, kind, mode: 'auto' })
|
|
91
|
+
if (r.status !== 200) throw new Error(`lead 绑定失败 ${kind} ${nodeId}: ${r.message}`)
|
|
92
|
+
}
|
|
93
|
+
res.leadBindings = { pvNode, knobNodes }
|
|
94
|
+
log(`③ lead 绑定:daq(${scen.pv.sig})=${pvNode} dcw=${knobNodes.join(',')}`)
|
|
95
|
+
|
|
96
|
+
// ── ④ 提交 goal ──
|
|
97
|
+
const knobText = (scen.knobs ?? []).slice(0, 2).map(k => `${k.sig}`).join('、')
|
|
98
|
+
const guardText = (scen.guards ?? []).map(g => `${g.label}≤${g.max}`).join('、') || '无'
|
|
99
|
+
const goal = [
|
|
100
|
+
`【产线闭环优化】${scen.zh}:${scen.story}`,
|
|
101
|
+
`目标:把「${scen.pv.label}(${scen.pv.sig})」稳定控制回 ${scen.pv.target}±${scen.pv.tol}${scen.pv.unit} 目标带;守卫约束:${guardText}。`,
|
|
102
|
+
`可调手段(数控节点,治理下发):${knobText}。`,
|
|
103
|
+
`作业要求:`,
|
|
104
|
+
`1. 开工先查知识库:lead 用 kb_agent(mode=sync) 检索「${scen.zh} 数据分析」「${scen.pv.label} 物理机理与设定影响」,把检索结论作为寻优方向与步长依据;`,
|
|
105
|
+
`2. lead 把自己已绑定的节点授权授予 worker(dispatch_task 带 grant_node_ids 随任务授予,或 team_grant_nodes),确保工艺工程师与数据分析师各自拿到所需节点权限,不要无权限空转;`,
|
|
106
|
+
`3. 工艺工程师单变量小步调整(每步 ≤ 量程 2%,尊重单步限幅与写入间隔),每次调整后等 ${Math.round(scen.settleMs / 1000)}s 再 daq_query 复测「${scen.pv.label}」;`,
|
|
107
|
+
`4. 进入目标带且复测稳定后 dcw_judge 落判定(keep),lead 用 kb_agent(mode=async) 把本次经验沉淀入知识库(含调整前后参数值与响应数据),随后完成收口汇报。`,
|
|
108
|
+
].join('\n')
|
|
109
|
+
const task = (await api.call('POST', `/api/workshop/channels/${channelId}/tasks`, { title: `${scen.zh}(KB 闭环)`, description: goal })).data
|
|
110
|
+
if (!task?.id) throw new Error(`根任务提交失败`)
|
|
111
|
+
res.rootTaskId = task.id
|
|
112
|
+
log(`④ 根任务 ${task.id} 已提交,开始作业(超时 ${Math.round(TASK_TIMEOUT_MS / 60000)}min)…\n`)
|
|
113
|
+
|
|
114
|
+
// ── ⑤ 轮询转录 ──
|
|
115
|
+
let seenMsg = new Set()
|
|
116
|
+
let seenTaskState = new Map()
|
|
117
|
+
let lastEventSeq = 0
|
|
118
|
+
let toolEvidence = { kbAgent: 0, grant: 0 }
|
|
119
|
+
const t0 = Date.now()
|
|
120
|
+
let terminal = null
|
|
121
|
+
const dump = async () => {
|
|
122
|
+
const msgs = (await api.call('GET', `/api/workshop/channels/${channelId}/messages?limit=200`)).data ?? []
|
|
123
|
+
for (const m of [...msgs].reverse()) {
|
|
124
|
+
const key = m.id ?? `${m.createdAt}:${m.fromAgentName}`
|
|
125
|
+
if (seenMsg.has(key)) continue
|
|
126
|
+
seenMsg.add(key)
|
|
127
|
+
const who = m.fromAgentName ?? m.from ?? '?'
|
|
128
|
+
const text = String(m.text ?? m.preview ?? '').slice(0, 600)
|
|
129
|
+
if (text.trim()) appendFileSync(join(dir, 'transcript.md'), `\n> **${who}** @ ${m.createdAt ?? ''}\n>\n${text.split('\n').map(l => `> ${l}`).join('\n')}\n`)
|
|
130
|
+
}
|
|
131
|
+
const tasks = (await api.call('GET', `/api/workshop/channels/${channelId}/tasks`)).data ?? []
|
|
132
|
+
const arr = Array.isArray(tasks) ? tasks : (tasks.tasks ?? [])
|
|
133
|
+
for (const t of arr) {
|
|
134
|
+
const st = `${t.id}:${t.state}:${t.progress ?? 0}`
|
|
135
|
+
if (seenTaskState.get(t.id) !== st) {
|
|
136
|
+
seenTaskState.set(t.id, st)
|
|
137
|
+
appendFileSync(join(dir, 'transcript.md'), `\n- [任务] ${t.id} 「${t.title}」 state=${t.state} progress=${t.progress ?? 0}% assignee=${t.assigneeId ?? '-'}`)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// 事件流:记录工具调用证据(kb_agent / team_grant_nodes / grant_node_ids)与 lead 进度报告
|
|
141
|
+
const ev = (await api.call('GET', `/api/workshop/channels/${channelId}/events?limit=200&afterSeq=${lastEventSeq}`)).data ?? {}
|
|
142
|
+
for (const item of (ev.items ?? [])) {
|
|
143
|
+
lastEventSeq = Math.max(lastEventSeq, Number(item.seq ?? 0))
|
|
144
|
+
const text = String(item.payload?.text ?? item.payload?.summary ?? '')
|
|
145
|
+
if (!text) continue
|
|
146
|
+
const kbHits = (text.match(/kb_agent/g) ?? []).length
|
|
147
|
+
const grantHits = (text.match(/team_grant_nodes|grant_node_ids/g) ?? []).length
|
|
148
|
+
toolEvidence.kbAgent += kbHits
|
|
149
|
+
toolEvidence.grant += grantHits
|
|
150
|
+
if (kbHits || grantHits) {
|
|
151
|
+
appendFileSync(join(dir, 'transcript.md'), `\n- [工具证据] @ ${(item.at ?? item.createdAt ?? '').slice(11, 19)} ${text.replace(/\n+/g, ' ').slice(0, 220)}`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { tasks: arr, msgs }
|
|
155
|
+
}
|
|
156
|
+
while (Date.now() - t0 < TASK_TIMEOUT_MS) {
|
|
157
|
+
await sleep(POLL_MS)
|
|
158
|
+
try {
|
|
159
|
+
const { tasks } = await dump()
|
|
160
|
+
const root = tasks.find(t => t.id === task.id)
|
|
161
|
+
if (root && ['COMPLETED', 'FAILED', 'CANCELED'].includes(root.state)) { terminal = root; break }
|
|
162
|
+
log(`… 作业中(${Math.round((Date.now() - t0) / 60000)}min)root=${root?.state ?? '?'}`)
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
log(`… 轮询抖动(忽略): ${err?.message ?? err}`)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
await dump()
|
|
169
|
+
if (!terminal) { res.errors.push(`根任务超时(${Math.round(TASK_TIMEOUT_MS / 60000)}min)未收口`) }
|
|
170
|
+
else if (terminal.state !== 'COMPLETED') { res.errors.push(`根任务终态=${terminal.state}`) }
|
|
171
|
+
res.checks.rootTaskClosed = terminal?.state === 'COMPLETED'
|
|
172
|
+
log(`\n⑤ 根任务终态=${terminal?.state ?? 'TIMEOUT'}`)
|
|
173
|
+
|
|
174
|
+
// ── ⑥ 验收 ──
|
|
175
|
+
const workers = agents.filter(a => a.role === 'worker')
|
|
176
|
+
let delegated = 0
|
|
177
|
+
for (const w of workers) {
|
|
178
|
+
const bs = (await api.call('GET', `/api/workshop/agent-tools/bindings?agentId=${w.id}`)).data
|
|
179
|
+
const flat = Array.isArray(bs) ? bs : (bs?.bindings ?? [])
|
|
180
|
+
if (flat.some(b => b.grantedByAgentId === lead.id)) delegated++
|
|
181
|
+
}
|
|
182
|
+
res.checks.workerDelegated = delegated > 0
|
|
183
|
+
log(`⑥ 委派核验:${delegated}/${workers.length} 个 worker 持有 lead 授予的节点授权`)
|
|
184
|
+
|
|
185
|
+
const dcwNow = (await api.call('GET', '/api/workshop/dcw')).data?.nodes ?? []
|
|
186
|
+
const myDcw = dcwNow.filter(n => Object.values(line.dcw).includes(n.id))
|
|
187
|
+
const wrote = myDcw.some(n => n.lastWriteAt)
|
|
188
|
+
res.checks.governedWriteHappened = wrote
|
|
189
|
+
res.dcAfter = myDcw.map(n => ({ id: n.id, name: n.name, value: n.value, lastWriteAt: n.lastWriteAt ?? null }))
|
|
190
|
+
log(`⑥ 治理写入核验:${wrote ? '发生过 dcw 治理写入' : '无 dcw 写入痕迹'}`)
|
|
191
|
+
|
|
192
|
+
const pvMean = await readPvMean(api, pvNode, 5).catch(() => null)
|
|
193
|
+
const inBand = pvMean != null && Math.abs(Number(pvMean) - scen.pv.target) <= scen.pv.tol
|
|
194
|
+
res.checks.pvInBand = inBand
|
|
195
|
+
res.pvMean = pvMean
|
|
196
|
+
log(`⑥ PV 复测:均值=${pvMean} 目标带=${scen.pv.target}±${scen.pv.tol} → ${inBand ? '达标' : '未达标'}`)
|
|
197
|
+
|
|
198
|
+
res.toolEvidence = toolEvidence
|
|
199
|
+
res.checks.kbToolUsed = toolEvidence.kbAgent > 0
|
|
200
|
+
res.checks.grantToolUsed = toolEvidence.grant > 0 || delegated > 0
|
|
201
|
+
log(`⑥ 工具证据(事件流):kb_agent×${toolEvidence.kbAgent} 委派×${toolEvidence.grant}`)
|
|
202
|
+
|
|
203
|
+
log(`⑥ 等待知识库经验沉淀可检索(≤${Math.round(KB_WAIT_MS / 60000)}min)…`)
|
|
204
|
+
const kbQuery = encodeURIComponent(`${scen.zh.slice(0, 6)} ${scen.pv.label} 经验`)
|
|
205
|
+
const kbDeadline = Date.now() + KB_WAIT_MS
|
|
206
|
+
let kbHit = false
|
|
207
|
+
while (Date.now() < kbDeadline) {
|
|
208
|
+
await sleep(45_000)
|
|
209
|
+
const sr = (await api.call('GET', `/api/plugins/rag-bridge/search?q=${kbQuery}&top_k=5`).catch(() => null))?.data
|
|
210
|
+
if ((sr?.total_results ?? 0) > 0 || (sr?.results ?? []).length > 0) { kbHit = true; break }
|
|
211
|
+
log(` … 检索暂未命中(累计等待 ${Math.round((kbDeadline - Date.now()) / 60000)}min)`)
|
|
212
|
+
}
|
|
213
|
+
res.checks.kbExperience = kbHit
|
|
214
|
+
log(`⑥ 知识库经验检索:${kbHit ? '已检出本场景经验内容' : '超时未检出(检索面后端索引漂移时以转录工具证据为准)'}`)
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
res.errors.push(String(err?.message ?? err))
|
|
218
|
+
log(`✘ 异常: ${err?.message ?? err}`)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
res.ok = res.errors.length === 0 && Object.values(res.checks).every(Boolean)
|
|
222
|
+
log(`\n════ [${id}] 结果: ${res.ok ? 'PASS' : 'FAIL'} checks=${JSON.stringify(res.checks)}${res.errors.length ? ` errors=${res.errors.join(';')}` : ''}`)
|
|
223
|
+
writeFileSync(join(dir, 'final.json'), JSON.stringify(res, null, 2))
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
writeFileSync(join(outDir, 'kb-team-omp-result.json'), JSON.stringify(globalResult, null, 2))
|
|
227
|
+
const allOk = globalResult.scenarios.every(s => s.ok)
|
|
228
|
+
console.log(`\n════ 总结果: ${allOk ? '✅ 全场景 PASS' : '❌ 存在 FAIL'}(明细: ${outDir})`)
|
|
229
|
+
process.exit(allOk ? 0 : 1)
|