agentworkshop 0.7.41 → 0.7.44
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/B8uy9iRW.js +1 -0
- package/.output/public/_nuxt/BGTYYQH-.js +1 -0
- package/.output/public/_nuxt/BUmHEJ77.js +2 -0
- package/.output/public/_nuxt/BY7YrV_D.js +1 -0
- package/.output/public/_nuxt/Bqcgz95s.js +18 -0
- package/.output/public/_nuxt/C7UlqPeD.js +1 -0
- package/.output/public/_nuxt/CTmDn-YP.js +1 -0
- package/.output/public/_nuxt/Cs8pVeVG.js +1 -0
- package/.output/public/_nuxt/DKySmNPI.js +14 -0
- package/.output/public/_nuxt/Di0NY_ht.js +1 -0
- package/.output/public/_nuxt/Du9v6qGB.js +1 -0
- package/.output/public/_nuxt/{DGbd-UiG.js → EHlEMRii.js} +1 -1
- package/.output/public/_nuxt/Eke1fs-U.js +1 -0
- package/.output/public/_nuxt/{DeJykBFf.js → H0BcqZtT.js} +1 -1
- package/.output/public/_nuxt/{C4oW1HsE.js → HSYEkkce.js} +1 -1
- package/.output/public/_nuxt/{D_OTvQza.js → J9oU00D1.js} +1 -1
- package/.output/public/_nuxt/OmpTerminalPanel.BxR5-lvi.css +1 -0
- package/.output/public/_nuxt/SSyAPaHc.js +1 -0
- package/.output/public/_nuxt/TownView.li4EMQxK.css +1 -0
- package/.output/public/_nuxt/VdLk4jLE2.js +1 -0
- package/.output/public/_nuxt/_id_.BhsXoEuk.css +1 -0
- package/.output/public/_nuxt/_id_.DQyj-WKN.css +1 -0
- package/.output/public/_nuxt/{_wsId_.B4WiqkIS.css → _wsId_.CTx-BlfF.css} +1 -1
- package/.output/public/_nuxt/aShsv_rG.js +19 -0
- package/.output/public/_nuxt/{yF0aYMF1.js → aesgXKSr.js} +3 -3
- package/.output/public/_nuxt/agents.B21nUEUX.css +1 -0
- package/.output/public/_nuxt/aml.ekTjxFkf.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/b5506ef0-f48e-4f14-a6c0-d65e505d0b09.json +1 -0
- package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +1 -0
- package/.output/public/_nuxt/daq.BdVmoIfe.css +1 -0
- package/.output/public/_nuxt/dcw.DCgyzpWV.css +1 -0
- package/.output/public/_nuxt/{default.J32SgqFY.css → default.Bh3L2HU8.css} +1 -1
- package/.output/public/_nuxt/lHAxdSsj.js +1 -0
- package/.output/public/_nuxt/pages.j9rzRocr.css +1 -0
- package/.output/public/_nuxt/plugins.DhiZbHU8.css +1 -0
- package/.output/public/_nuxt/settings.BRx9rIDg.css +1 -0
- package/.output/public/_nuxt/teams.BLlOPB_Z.css +1 -0
- package/.output/public/_nuxt/tokens.De8xzszn.css +1 -0
- package/.output/public/_nuxt/workshop.D1jFNkrn.css +1 -0
- package/.output/server/chunks/_/nitro.mjs +748 -733
- package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs → OmpTerminalPanel-CVvxpsF7.mjs} +15 -15
- package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs.map → OmpTerminalPanel-CVvxpsF7.mjs.map} +1 -1
- package/.output/server/chunks/build/{TownView--otsnr_7.mjs → TownView-CJ6IsDCc.mjs} +226 -225
- package/.output/server/chunks/build/{TownView--otsnr_7.mjs.map → TownView-CJ6IsDCc.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-Brv-JSvr.mjs → _id_-CVD_H0ir.mjs} +143 -142
- package/.output/server/chunks/build/_id_-CVD_H0ir.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-D9pl1OZy.mjs → _id_-CtxMb3qp.mjs} +41 -41
- package/.output/server/chunks/build/_id_-CtxMb3qp.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-BASfDf0s.mjs → _wsId_-DpY3T-md.mjs} +514 -444
- package/.output/server/chunks/build/{_wsId_-BASfDf0s.mjs.map → _wsId_-DpY3T-md.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-CLQqJF2y.mjs → agents-DsCkuXDV.mjs} +49 -46
- package/.output/server/chunks/build/agents-DsCkuXDV.mjs.map +1 -0
- package/.output/server/chunks/build/{aml-_7FdFL2p.mjs → aml-CT0xKGKI.mjs} +122 -105
- package/.output/server/chunks/build/aml-CT0xKGKI.mjs.map +1 -0
- package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +117 -0
- package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -0
- package/.output/server/chunks/build/{channel-templates-p6SZNjlM.mjs → channel-templates-Dla9FbYa.mjs} +49 -37
- package/.output/server/chunks/build/channel-templates-Dla9FbYa.mjs.map +1 -0
- package/.output/server/chunks/build/{daq-DkMvD8sg.mjs → daq-DWWSKGAV.mjs} +89 -88
- package/.output/server/chunks/build/daq-DWWSKGAV.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-C1jgVJkO.mjs → dcw-CT8k17_E.mjs} +44 -43
- package/.output/server/chunks/build/dcw-CT8k17_E.mjs.map +1 -0
- package/.output/server/chunks/build/{default-BE4r0FZN.mjs → default-D3525UFX.mjs} +16 -15
- package/.output/server/chunks/build/{default-BE4r0FZN.mjs.map → default-D3525UFX.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-C0okQaXz.mjs → monitor-Dh2Kvaq5.mjs} +2 -2
- package/.output/server/chunks/build/{monitor-C0okQaXz.mjs.map → monitor-Dh2Kvaq5.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-B0bUWnC6.mjs → pages-BHcSBM2D.mjs} +24 -18
- package/.output/server/chunks/build/pages-BHcSBM2D.mjs.map +1 -0
- package/.output/server/chunks/build/{plugins-lGlUr1lj.mjs → plugins-BNgIi61W.mjs} +23 -17
- package/.output/server/chunks/build/plugins-BNgIi61W.mjs.map +1 -0
- package/.output/server/chunks/build/{settings-Ddd1qx2Z.mjs → settings-ipYBDWdn.mjs} +45 -44
- package/.output/server/chunks/build/settings-ipYBDWdn.mjs.map +1 -0
- package/.output/server/chunks/build/{teams-MEDLZdMn.mjs → teams-5lx-CwFa.mjs} +47 -44
- package/.output/server/chunks/build/teams-5lx-CwFa.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-C7fTM5C2.mjs → tokens-q-IDT8Io.mjs} +73 -53
- package/.output/server/chunks/build/tokens-q-IDT8Io.mjs.map +1 -0
- package/.output/server/chunks/build/{town-CoQdblbZ.mjs → town-QJWB5wng.mjs} +3 -2
- package/.output/server/chunks/build/town-QJWB5wng.mjs.map +1 -0
- package/.output/server/chunks/build/{workshop-BL5bhfs0.mjs → workshop-BIlCj_ZP.mjs} +45 -45
- package/.output/server/chunks/build/workshop-BIlCj_ZP.mjs.map +1 -0
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/chunks/virtual/entry.mjs +637 -86
- package/.output/server/chunks/virtual/entry.mjs.map +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +13 -3
- package/README.md +13 -3
- package/app/components/AppHeader.vue +961 -959
- package/app/components/workshop/A2aRpcDebugger.vue +237 -237
- package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
- package/app/components/workshop/AgentLanesView.vue +998 -998
- package/app/components/workshop/ArtifactCard.vue +120 -120
- package/app/components/workshop/AssetLibrary.vue +452 -452
- package/app/components/workshop/ChannelSessionList.vue +812 -804
- package/app/components/workshop/CommandPalette.vue +269 -269
- package/app/components/workshop/Composer.vue +827 -827
- package/app/components/workshop/DeviceTwinPanel.vue +643 -643
- package/app/components/workshop/EventBlock.vue +602 -602
- package/app/components/workshop/FileSelectorModal.vue +188 -188
- package/app/components/workshop/InspectorPanel.vue +301 -301
- package/app/components/workshop/MemoryPanel.vue +364 -364
- package/app/components/workshop/MultiChannelView.vue +189 -189
- package/app/components/workshop/TaskBoardView.vue +686 -686
- package/app/components/workshop/TaskInspectorDrawer.vue +390 -390
- package/app/components/workshop/TranscriptTimeline.vue +601 -601
- package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
- package/app/components/workshop/blocks/ClusterError.vue +85 -85
- package/app/components/workshop/blocks/ClusterLife.vue +87 -87
- package/app/components/workshop/blocks/ClusterMember.vue +95 -93
- package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
- package/app/components/workshop/blocks/ClusterOther.vue +67 -67
- package/app/components/workshop/blocks/ClusterStatus.vue +149 -147
- package/app/components/workshop/blocks/ClusterTool.vue +158 -158
- package/app/components/workshop/terminal/OmpTerminalPanel.vue +1011 -1011
- package/app/components/workshop/town/TownView.vue +56 -56
- package/app/middleware/locale-cookie.global.ts +22 -0
- package/app/pages/aml/index.vue +24 -2
- package/app/pages/daq/[id].vue +11 -11
- package/app/pages/daq/index.vue +2316 -2316
- package/app/pages/dcw/[id].vue +18 -19
- package/app/pages/dcw/index.vue +3 -3
- package/app/pages/index.vue +987 -981
- package/app/pages/plugins/index.vue +317 -311
- package/app/pages/settings.vue +1564 -1562
- package/app/pages/tokens.vue +15 -15
- package/app/pages/workshop/agents.vue +612 -609
- package/app/pages/workshop/channel-templates.vue +18 -11
- package/app/pages/workshop/index.vue +668 -668
- package/app/pages/workshop/teams.vue +743 -740
- package/app/pages/workshop/w/[wsId].vue +704 -704
- package/app/plugins/locale-restore.client.ts +16 -1
- package/app/utils/builtin-catalog.ts +104 -0
- package/config.yml +170 -170
- package/docs/full-test-plan.md +2 -3
- package/i18n/locales/en.ts +293 -35
- package/i18n/locales/zh-CN.ts +265 -7
- package/package.json +2 -2
- package/scripts/_dbg-perms-e2e.mjs +132 -109
- package/scripts/_demo-agentteam-capture.mjs +278 -0
- package/scripts/_e2e-ui-functional.mjs +371 -0
- package/scripts/_i18n-en-cjk-scan.mjs +28 -0
- package/scripts/_i18n-en-verify.mjs +107 -0
- package/scripts/_i18n-extra-checks.mjs +53 -0
- package/scripts/_i18n-scan-hardcoded.mjs +180 -0
- package/scripts/_i18n-verify-keys.mjs +50 -0
- package/scripts/_loop-biax-campaign.mjs +15 -9
- package/scripts/_sweep-button-contrast.mjs +87 -0
- package/scripts/ui/record-demo.mjs +557 -0
- package/server/plugins-builtin/diag-bridge/index.mjs +608 -661
- package/server/plugins-builtin/rag-bridge/index.mjs +57 -133
- package/server/services/workshop/daq/drivers.ts +11 -0
- package/.output/public/_nuxt/88GOowgO2.js +0 -1
- package/.output/public/_nuxt/BN16aRdQ.js +0 -1
- package/.output/public/_nuxt/BWVhZntO.js +0 -1
- package/.output/public/_nuxt/BXxH0UQE.js +0 -1
- package/.output/public/_nuxt/BsROmY89.js +0 -1
- package/.output/public/_nuxt/C0ZrS1KL.js +0 -19
- package/.output/public/_nuxt/C4s_niy-.js +0 -14
- package/.output/public/_nuxt/D6mbFA-a.js +0 -1
- package/.output/public/_nuxt/DS8qj8EL.js +0 -1
- package/.output/public/_nuxt/DUjW5Yq5.js +0 -1
- package/.output/public/_nuxt/Dap5PmTL.js +0 -2
- package/.output/public/_nuxt/Dqw1lpr-.js +0 -1
- package/.output/public/_nuxt/DvLxILQP.js +0 -1
- package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +0 -1
- package/.output/public/_nuxt/TownView.f-YOdXRs.css +0 -1
- package/.output/public/_nuxt/_id_.D9ymtHB5.css +0 -1
- package/.output/public/_nuxt/_id_.jf-_8E_c.css +0 -1
- package/.output/public/_nuxt/agents.ZD0rVdHE.css +0 -1
- package/.output/public/_nuxt/aml.BN8ZEW4h.css +0 -1
- package/.output/public/_nuxt/builds/meta/78a295bb-2072-48a4-8b46-6d545fdae743.json +0 -1
- package/.output/public/_nuxt/channel-templates.BOtahzPi.css +0 -1
- package/.output/public/_nuxt/daq.DjSgY94l.css +0 -1
- package/.output/public/_nuxt/dcw.8Y7FqCVN.css +0 -1
- package/.output/public/_nuxt/eqq3wWQF.js +0 -18
- package/.output/public/_nuxt/pages.D7aSDlUI.css +0 -1
- package/.output/public/_nuxt/plugins.sb4nDGqr.css +0 -1
- package/.output/public/_nuxt/qr6R6Ha1.js +0 -1
- package/.output/public/_nuxt/settings.BTgR-NiE.css +0 -1
- package/.output/public/_nuxt/teams.CxnxKVVs.css +0 -1
- package/.output/public/_nuxt/tokens.BFY7qv4f.css +0 -1
- package/.output/public/_nuxt/workshop.CR_QzeAN.css +0 -1
- package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +0 -1
- package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +0 -1
- package/.output/server/chunks/build/aml-_7FdFL2p.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-p6SZNjlM.mjs.map +0 -1
- package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +0 -1
- package/.output/server/chunks/build/dcw-C1jgVJkO.mjs.map +0 -1
- package/.output/server/chunks/build/pages-B0bUWnC6.mjs.map +0 -1
- package/.output/server/chunks/build/plugins-lGlUr1lj.mjs.map +0 -1
- package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +0 -1
- package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +0 -1
- package/.output/server/chunks/build/tokens-C7fTM5C2.mjs.map +0 -1
- package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +0 -1
- package/.output/server/chunks/build/workshop-BL5bhfs0.mjs.map +0 -1
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.continue.json +0 -1
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.json +0 -1
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/054bb4bc4e4217a2ab0b0c6b663747214a9af464f5568ea60e784704e340bb0f.source +0 -153
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/0554197c3d2bac203a1f5c48a5d52495d1336db1bd7aa4f4c19875810363e684.source +0 -153
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/05c203d12a954fffba942e1d626e3f5fec05b18a5aef2bac003b7884a4b77406.source +0 -431
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/145a6e6ba811af35aed31e0cc3211f53706a14980fa069ee9e3c1591139f96f0.source +0 -180
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/1ae0fb281f97899c025e368ca73f41349b01fb5f85b19e08d7ebccff49095ed5.source +0 -112
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/20ee9967aa0541c516db7575bdd4a1a745939bc7016bd10d7635903113edb8ec.source +0 -227
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/233751843aeba9d4c5263f275b8102de00443c06f286918cbd23e0b6729e01aa.source +0 -131
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/263c2e05b425e5c322e60c6475279f963bab3a873eb521b21df7fec935a46597.source +0 -160
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/2ac5eaac28eaaee2178c4b32014df8e732f9dd432a285a872f5b7f31c1395fa4.source +0 -157
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/33a0db0d2d95d2c11d0720090458e5fba1425376f5ceb5b422d1d3471be3226b.source +0 -348
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/359e474ae83db35e4615d3728de6bf694b64d4dce9b536730a3b6518f9c0494a.source +0 -296
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/37a4c3c31c4b70f6b719c53d1e9c4fcba603f31adfdd11bb6ef9f0d094923c56.source +0 -285
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/461feca1c14a695eb8b0f17a6f95fe97eeceabc0510faaea4ba6d25bde147d8d.source +0 -217
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/4cfadbda8591f29d5e1e230dc000e701b756e850441b3900fb596a4d2f6e98c2.source +0 -301
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/50ca2810cf0b248c96948994c0c404531bbad6e553166b6816b3b03bfa0f75d2.source +0 -151
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/52d9e8cb591a9ae86e238c3620a610cd386717bee222c0f37b25b2ac7e523dc4.source +0 -135
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/6c87daafa4c006560a954636a97333f6123153c9d34d96203f7ee3a14e556357.source +0 -156
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/77bc47854b577900621cacf1697f81fa2bc005af129d37614a97a3441c08f99f.source +0 -72
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/7bbb789d3a527817118cf8399ed1861d6a962ade10878fd07223435d333a7089.source +0 -469
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/86b7c1daafc8b60ba9982dde5bf1bcc74265709483c176e71f23ec6fc267451f.source +0 -259
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/8df66d62bd41984d0970e8236882080c5085ac937d07a29779463d6a03b37c76.source +0 -97
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/9962f2d1b3a85137bd40cc835725600b39f89ee02a27c06b2e0402e1eafde115.source +0 -277
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a1090ea066b792cb201739f4994e37bf7c12f52e805239a1eab87f18a537f266.source +0 -171
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a5241091650134d0919897e086a606a3846c8b2b54f8b4c84a8696c145ac3e9e.source +0 -425
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/af19fa4c6e34d5540028be4b17600161283af3a2a2dfc61a3fdc0834436a0c14.source +0 -99
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3713d78e2b2f93905ff6be8d58a8d0a2f42dea322eb67c987fda22ff6a084d5.source +0 -325
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3849943c3aff102d1af815631fc843a2a52c17646a4b04696ece323e483bbec.source +0 -332
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/bac735a3803a6650b7f1eb8f587e6dc5c68ea055000c3c99a11789438b78e837.source +0 -43
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d32c24d61eb8b33c73e5a8fc7e4dc7ef437aed238ee4371c2b00617e6104090a.source +0 -154
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d5170dc9a9879fda04fdf91d8a110d4efb6787ee38dda596d4da899883d36a72.source +0 -531
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/dc59bcd7bb397be63ba2f2c3fec23f3782dcb17d2d75a9981eeced761f33eb32.source +0 -439
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/eb5ff8cf66b92aec7bf6726781d8df74faa2b030c3aaac17d6b00c1fc04c9b32.source +0 -323
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f10615a6fae18436e70d1201b2d44fdae3174a3aa7bb4db97dea1b3ed291d721.source +0 -433
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f36d3e271668bc1260c2823542d22dbf1d0f7041ff16b1ac175bce2256e582ca.source +0 -280
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f94449380af09f31ac352152007146a75cc80bae8d6411ffe57b2c56f933b0ff.source +0 -117
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/fe4dfd81178fd6b648335f6405d9ede71319aebde796008e50db21f8adf44c99.source +0 -160
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/ffb4e3aa548cfdadabaa44ab599f9b6a3ca6288176a2cc7a4b2fc2a12b5fb848.source +0 -101
- package/scripts/.mimosa/hook-status/sess_aeaecb58-8833-4b83-b93c-050467d21e03-d944d71bb3.json +0 -14
- package/scripts/_dbg-audit-all-pages.mjs +0 -43
- package/scripts/_dbg-daq-frames-e2e.mjs +0 -118
- package/scripts/_dbg-daq-rowstyle-verify.mjs +0 -112
- package/scripts/_dbg-hitl-timeout-e2e.mjs +0 -93
- package/scripts/_dbg-live-line-e2e.mjs +0 -279
- package/scripts/_dbg-minimal-omp.mjs +0 -77
- package/scripts/_dbg-mode-nav.mjs +0 -173
- package/scripts/_dbg-real-scenario-timeline.log +0 -55
- package/scripts/_dbg-team-repro.mjs +0 -76
- package/scripts/_dbg-town-agents-probe.mjs +0 -60
- package/scripts/_dbg-visual-acceptance.mjs +0 -63
- package/scripts/_townview_style.css +0 -1185
- package/scripts/_townview_template.html +0 -1215
- package/server/data/agent-node-bindings.json +0 -554
- package/server/data/daq-templates.json +0 -16
- package/server/data/daqs.json +0 -7464
- package/server/data/dcw-lines.json +0 -1787
- package/server/data/dcw-products.json +0 -1626
- package/server/data/dcw-recipes.json +0 -4672
- package/server/data/dcw-rollback.json +0 -20754
- package/server/data/dcw-runs.json +0 -6354
- package/server/data/dcw-templates.json +0 -146
- package/server/data/dcw-writes.json +0 -15650
- package/server/data/dcws.json +0 -9033
- package/server/data/device-twins.json +0 -133
- package/server/data/line-runs.json +0 -322
- package/server/data/scene-layouts.json +0 -277
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentMemory P0 测试:repo(FTS/去重/touch/delete/team 预埋)→ 模块(排序/预算/格式)→ 运行时集成。
|
|
3
|
-
* 运行: npx tsx scripts/test-memory.ts
|
|
4
|
-
*/
|
|
5
|
-
import type { DatabaseSync } from 'node:sqlite'
|
|
6
|
-
import { openWorkshopDb } from '../server/services/workshop/db/database'
|
|
7
|
-
import { createMemoryRepo, TEAM_AGENT_ID } from '../server/services/workshop/db/memory.repo'
|
|
8
|
-
import { AgentMemory, buildMatchQuery, estimateTokens, segmentCJK } from '../server/services/workshop/runtime/memory'
|
|
9
|
-
import { randomUUID } from 'node:crypto'
|
|
10
|
-
import { createMessageRepo } from '../server/services/workshop/db/message.repo'
|
|
11
|
-
import { Mailbox } from '../server/services/workshop/runtime/mailbox'
|
|
12
|
-
import { createChannelRepo } from '../server/services/workshop/db/channel.repo'
|
|
13
|
-
import { createAgentRepo } from '../server/services/workshop/db/agent.repo'
|
|
14
|
-
import { createTeamRepo } from '../server/services/workshop/db/team.repo'
|
|
15
|
-
import { createTeamMemberRepo } from '../server/services/workshop/db/team-member.repo'
|
|
16
|
-
import { createChannelAgentRepo } from '../server/services/workshop/db/channel-agent.repo'
|
|
17
|
-
import { createTaskRepo } from '../server/services/workshop/db/task.repo'
|
|
18
|
-
import { createSubscriptionRepo } from '../server/services/workshop/db/subscription.repo'
|
|
19
|
-
import { createAgentChannelManager } from '../server/services/workshop/runtime/manager'
|
|
20
|
-
import type { AppError } from '../server/utils/errors'
|
|
21
|
-
import { AgentRuntime } from '../server/services/workshop/runtime/agent-runtime'
|
|
22
|
-
import type { ChannelBus, TaskEngine } from '../server/services/workshop/runtime/agent-runtime'
|
|
23
|
-
import type {
|
|
24
|
-
AgentEvent,
|
|
25
|
-
AgentInterface,
|
|
26
|
-
AgentInfo,
|
|
27
|
-
AgentRunContext,
|
|
28
|
-
AgentRunRequest,
|
|
29
|
-
SupervisionDecision,
|
|
30
|
-
SupervisionSnapshot,
|
|
31
|
-
AgentWorkspace,
|
|
32
|
-
} from '../server/services/workshop/agents/agent-interface'
|
|
33
|
-
import type { WorkspaceTask, TaskState, AgentTaskQueueView } from '../server/services/workshop/types/task'
|
|
34
|
-
import type { Part } from '../server/services/workshop/types/a2a'
|
|
35
|
-
|
|
36
|
-
let failures = 0
|
|
37
|
-
function check(name: string, ok: boolean, detail = ''): void {
|
|
38
|
-
console.log(` ${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
39
|
-
if (!ok) failures += 1
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const db: DatabaseSync = openWorkshopDb(':memory:')
|
|
43
|
-
const repo = createMemoryRepo(db)
|
|
44
|
-
const seg = (s: string): string => s.replace(/([\u4e00-\u9fff])/g, ' $1 ')
|
|
45
|
-
|
|
46
|
-
// ---- repo:写入 + CJK FTS 检索(title 经 titleFts 切分入索引;content 切分传入)----
|
|
47
|
-
repo.upsert({ channelId: 'ch1', agentId: 'a1', kind: 'episodic-task', title: '实现登录页面', titleFts: seg('实现登录页面'), content: seg('用OAuth2方案完成了登录鉴权'), importance: 0.8, taskId: 't1', dedupKey: 'task:t1' })
|
|
48
|
-
repo.upsert({ channelId: 'ch1', agentId: 'a1', kind: 'episodic-task', title: '数据库调研', titleFts: seg('数据库调研'), content: seg('最终选择SQLite因为轻量嵌入式'), importance: 0.8, taskId: 't2', dedupKey: 'task:t2' })
|
|
49
|
-
repo.upsert({ channelId: 'ch1', agentId: 'a2', kind: 'episodic-task', title: '他人记忆', titleFts: seg('他人记忆'), content: seg('别的agent的登录杂事'), importance: 0.8, taskId: 't3', dedupKey: 'task:t3' })
|
|
50
|
-
|
|
51
|
-
const hit = repo.search('a1', `${seg('登录').trim()} OR oauth`, 5)
|
|
52
|
-
check('FTS 中英混合检索命中(经 titleFts)', hit.length >= 1 && hit[0].title === '实现登录页面', `hits=${hit.length}`)
|
|
53
|
-
check('展示 title 保持原文(未切分)', hit[0]?.title === '实现登录页面')
|
|
54
|
-
check('FTS 不串他人私有记忆', hit.every(r => r.agentId === 'a1'))
|
|
55
|
-
|
|
56
|
-
// ---- repo:team 域预埋(search 恒含 team 行)----
|
|
57
|
-
repo.upsert({ channelId: 'ch1', agentId: TEAM_AGENT_ID, kind: 'semantic', title: 'channel 代码风格', titleFts: seg('channel 代码风格'), content: seg('全部使用TypeScript strict模式'), importance: 0.9, taskId: null, dedupKey: 'style:ts' })
|
|
58
|
-
const teamQuery = seg('代码风格').trim().split(/\s+/).join(' OR ') // → '代 OR 码 OR 风 OR 格'
|
|
59
|
-
const teamHit = repo.search('a1', teamQuery, 5)
|
|
60
|
-
check('team 记忆对所有 agent 可见(经 titleFts 命中)', teamHit.some(r => r.agentId === TEAM_AGENT_ID))
|
|
61
|
-
check('listRecent 严格本人(不含 team)', !repo.listRecent('a1', 10).some(r => r.agentId === TEAM_AGENT_ID))
|
|
62
|
-
|
|
63
|
-
// ---- repo:去重 upsert ----
|
|
64
|
-
repo.upsert({ channelId: 'ch1', agentId: 'a1', kind: 'episodic-task', title: '实现登录页面', titleFts: seg('实现登录页面'), content: seg('重跑后改用JWT方案'), importance: 0.8, taskId: 't1', dedupKey: 'task:t1' })
|
|
65
|
-
check('同 dedupKey 去重', repo.listByAgent('a1', 10).length === 2)
|
|
66
|
-
check('upsert 刷新后 FTS 同步', repo.search('a1', 'jwt', 5).length === 1)
|
|
67
|
-
const dedup = repo.findByAgentDedup('ch1', 'a1', 'task:t1')
|
|
68
|
-
check('findByAgentDedup 返回 id+rowid', dedup !== null && typeof dedup.rowid === 'number')
|
|
69
|
-
|
|
70
|
-
// ---- repo:touch + delete ----
|
|
71
|
-
repo.touch(dedup!.id)
|
|
72
|
-
check('touch 递增 access_count', repo.listByAgent('a1', 10).find(r => r.id === dedup!.id)!.accessCount >= 1)
|
|
73
|
-
const teamDel = repo.findByAgentDedup('ch1', TEAM_AGENT_ID, 'style:ts')!
|
|
74
|
-
check('team 行去重键独立', teamDel !== null)
|
|
75
|
-
check('delete 返回 true 且 FTS 同步清理', repo.delete(dedup!.id) === true && repo.search('a1', 'jwt', 5).length === 0)
|
|
76
|
-
check('listMemoryAgentIds 排除 team', repo.listMemoryAgentIds().includes(TEAM_AGENT_ID) === false)
|
|
77
|
-
|
|
78
|
-
// ═══════════ 模块:AgentMemory ═══════════
|
|
79
|
-
console.log('\n--- AgentMemory 模块 ---')
|
|
80
|
-
|
|
81
|
-
check('segmentCJK 汉字间加空格', segmentCJK('登录oauth') === ' 登 录 oauth')
|
|
82
|
-
check('buildMatchQuery OR 连接 + 剔除操作符/保留词(引号包裹)', buildMatchQuery('登录 (页面) AND not') === '"登" OR "录" OR "页" OR "面"')
|
|
83
|
-
check('buildMatchQuery 空查询 null', buildMatchQuery(' ') === null)
|
|
84
|
-
check('buildMatchQuery ASCII 单字词过滤', buildMatchQuery('a b oauth') === '"oauth"')
|
|
85
|
-
check('estimateTokens 中英混合', estimateTokens('登录ab') === 3)
|
|
86
|
-
check('buildMatchQuery 剥除路径标点', buildMatchQuery('读取 D:/codes/x/.tmp-a/input.txt 内容') !== null && !buildMatchQuery('读取 D:/codes/x/.tmp-a/input.txt 内容')!.includes('.'))
|
|
87
|
-
check('buildMatchQuery 词项全部引号包裹', (() => {
|
|
88
|
-
const q = buildMatchQuery('登录 oauth 页面')
|
|
89
|
-
return q === null || q.split(' OR ').every(t => t.startsWith('"') && t.endsWith('"'))
|
|
90
|
-
})())
|
|
91
|
-
// 前段 delete 了 task:t1 行,重新播种供模块段使用(fresh insert,access_count=0)
|
|
92
|
-
repo.upsert({ channelId: 'ch1', agentId: 'a1', kind: 'episodic-task', title: '实现登录页面', titleFts: seg('实现登录页面'), content: seg('用OAuth2方案完成了登录鉴权'), importance: 0.8, taskId: 't1', dedupKey: 'task:t1' })
|
|
93
|
-
|
|
94
|
-
const memA = new AgentMemory(repo, { channelId: 'ch1', agentId: 'a1' })
|
|
95
|
-
await memA.recall('数据库', { touch: false })
|
|
96
|
-
check('touch:false 不改 access_count', repo.listByAgent('a1', 10).find(r => r.title === '数据库调研')!.accessCount === 0)
|
|
97
|
-
|
|
98
|
-
// 查询同时含本人记忆与 team 记忆的词(两者都经 FTS 命中;listRecent 兜底不含 team)
|
|
99
|
-
const block = await memA.recall('登录页面 代码风格')
|
|
100
|
-
check('recall 含本人相关记忆', block !== null && block.includes('实现登录页面'))
|
|
101
|
-
check('recall 含 team 共享记忆', block !== null && block.includes('channel 代码风格'), block?.slice(0, 100))
|
|
102
|
-
check('recall 不含他人私有记忆', block !== null && !block.includes('他人记忆'))
|
|
103
|
-
const t1row = repo.listByAgent('a1', 10).find(r => r.title === '实现登录页面')
|
|
104
|
-
check('recall 默认 touch', t1row !== undefined && t1row.accessCount >= 1)
|
|
105
|
-
|
|
106
|
-
const memTiny = new AgentMemory(repo, { channelId: 'ch1', agentId: 'a1', budgetTokens: 5 })
|
|
107
|
-
const tiny = await memTiny.recall('登录 数据库')
|
|
108
|
-
check('极小预算整行取舍', tiny === null || tiny.split('\n').length <= 3)
|
|
109
|
-
|
|
110
|
-
const memEmpty = new AgentMemory(repo, { channelId: 'ch1', agentId: 'nobody' })
|
|
111
|
-
check('无记忆 recall 返回 null', (await memEmpty.recall('任意')) === null)
|
|
112
|
-
|
|
113
|
-
const pathResult = await new AgentMemory(repo, { channelId: 'ch1', agentId: 'a1' })
|
|
114
|
-
.recall('配置在 D:/codes/.tmp-x/config.json 怎么改')
|
|
115
|
-
.then(v => v ?? null, () => '__THROWN__')
|
|
116
|
-
check('路径文本 recall 不炸', pathResult !== '__THROWN__')
|
|
117
|
-
|
|
118
|
-
// ═══════════ 运行时集成:召回注入 + 结束沉淀 ═══════════
|
|
119
|
-
console.log('\n--- AgentRuntime 记忆集成 ---')
|
|
120
|
-
|
|
121
|
-
/** 落库一个 channel(messages.channel_id 外键依赖;照抄 test-agent-runtime.ts) */
|
|
122
|
-
function seedChannel(database: DatabaseSync, id: string): void {
|
|
123
|
-
const now = new Date().toISOString()
|
|
124
|
-
database.prepare(
|
|
125
|
-
`INSERT INTO channels (id, name, description, lead_agent_id, enabled, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
126
|
-
).run(id, id, '', null, 1, now, now)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
seedChannel(db, 'ch-mem')
|
|
130
|
-
|
|
131
|
-
const memBus: ChannelBus = {
|
|
132
|
-
emit: () => {}, onEvent: () => () => {}, notifyTask: () => {}, onTaskEvent: () => () => {},
|
|
133
|
-
notifyAgent: () => {}, onAgentStatus: () => () => {}, wakeScheduler: () => {},
|
|
134
|
-
}
|
|
135
|
-
// fake engine(签名对齐 agent-runtime.ts 的 TaskEngine 契约;完成态经 transition 驱动)
|
|
136
|
-
const memTasks = new Map<string, WorkspaceTask>()
|
|
137
|
-
const fakeEngine: TaskEngine = {
|
|
138
|
-
create: (input: { channelId: string, creatorId: string, assigneeId: string, title: string }) => {
|
|
139
|
-
const t: WorkspaceTask = { id: randomUUID(), channelId: input.channelId, assigneeId: input.assigneeId, creatorId: input.creatorId, title: input.title, state: 'SUBMITTED', progress: 0, retryCount: 0, artifacts: [], history: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }
|
|
140
|
-
memTasks.set(t.id, t)
|
|
141
|
-
return t
|
|
142
|
-
},
|
|
143
|
-
dispatch: () => { throw new Error('unused') },
|
|
144
|
-
transition: (taskId: string, state: TaskState, _by: string) => {
|
|
145
|
-
const t = memTasks.get(taskId)
|
|
146
|
-
if (t) t.state = state
|
|
147
|
-
return t!
|
|
148
|
-
},
|
|
149
|
-
// 对齐真实 TaskEngine.applyEvent:非 append artifact 落 task.artifacts(终态 harvest 数据源)
|
|
150
|
-
applyEvent: (taskId: string, event: AgentEvent) => {
|
|
151
|
-
const t = memTasks.get(taskId)
|
|
152
|
-
if (t && event.kind === 'artifact') t.artifacts = [...t.artifacts, event.artifact]
|
|
153
|
-
},
|
|
154
|
-
list: () => [...memTasks.values()],
|
|
155
|
-
get: (id: string) => memTasks.get(id),
|
|
156
|
-
complete: () => { throw new Error('unused') },
|
|
157
|
-
reassign: () => { throw new Error('unused') },
|
|
158
|
-
updateTask: () => { throw new Error('unused') },
|
|
159
|
-
cancel: () => { throw new Error('unused') },
|
|
160
|
-
onChildCompleted: () => {},
|
|
161
|
-
redeliverAssign: () => { throw new Error('unused') },
|
|
162
|
-
queueViewOf: (channelId: string, agentId: string): AgentTaskQueueView => ({ agentId, channelId, queued: [], completed: [] }),
|
|
163
|
-
}
|
|
164
|
-
// workspace stub:EchoImpl 的 completeTask 经 transition 落 COMPLETED(终态判定依赖)
|
|
165
|
-
const wsStub = (agentId: string): AgentWorkspace => ({
|
|
166
|
-
completeTask: async (taskId: string, artifacts) => {
|
|
167
|
-
const t = memTasks.get(taskId)
|
|
168
|
-
if (t) {
|
|
169
|
-
t.artifacts = [...t.artifacts, ...artifacts]
|
|
170
|
-
t.progress = 100
|
|
171
|
-
fakeEngine.transition(taskId, 'COMPLETED', agentId)
|
|
172
|
-
}
|
|
173
|
-
return t!
|
|
174
|
-
},
|
|
175
|
-
}) as AgentWorkspace
|
|
176
|
-
void wsStub
|
|
177
|
-
|
|
178
|
-
class MemoryEchoImpl implements AgentInterface {
|
|
179
|
-
readonly captured: AgentRunRequest[] = []
|
|
180
|
-
async* run(request: AgentRunRequest, ctx: AgentRunContext): AsyncGenerator<AgentEvent, void, unknown> {
|
|
181
|
-
this.captured.push(request)
|
|
182
|
-
const taskId = request.taskId ?? (request.message.metadata?.['x-aw-task-id'] as string | undefined)
|
|
183
|
-
if (taskId && ctx.role === 'worker') {
|
|
184
|
-
yield { kind: 'status', status: { state: 'WORKING', timestamp: new Date().toISOString() } }
|
|
185
|
-
await (ctx.workspace as { completeTask: (id: string, arts: unknown[]) => Promise<unknown> }).completeTask(taskId, [])
|
|
186
|
-
yield { kind: 'artifact', artifact: { artifactId: randomUUID(), name: 'deliverable', parts: [{ text: `memory-seen:${request.memory ?? 'NONE'}` }] }, lastChunk: true, totalChunks: 1 }
|
|
187
|
-
yield { kind: 'done', final: { taskId } }
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const mkRt = (id: string): { rt: AgentRuntime, echo: MemoryEchoImpl } => {
|
|
193
|
-
const info: AgentInfo = { id, channelId: 'ch-mem', name: id, harness: 'mock', role: 'worker', config: {} }
|
|
194
|
-
const echo = new MemoryEchoImpl()
|
|
195
|
-
const rt = new AgentRuntime(info, echo, {
|
|
196
|
-
mailbox: new Mailbox(createMessageRepo(db), 'ch-mem', id, () => {}),
|
|
197
|
-
taskEngine: fakeEngine,
|
|
198
|
-
bus: memBus,
|
|
199
|
-
workspace: wsStub(id),
|
|
200
|
-
memory: new AgentMemory(repo, { channelId: 'ch-mem', agentId: id }),
|
|
201
|
-
})
|
|
202
|
-
rt.start()
|
|
203
|
-
return { rt, echo }
|
|
204
|
-
}
|
|
205
|
-
const mkAssign = (task: WorkspaceTask): Part[] => [{ text: task.title }]
|
|
206
|
-
const enqueueAssign = (rt: AgentRuntime, task: WorkspaceTask): void => {
|
|
207
|
-
rt.enqueue({ messageId: randomUUID(), contextId: 'ch-mem', role: 'ROLE_AGENT', taskId: task.id, parts: mkAssign(task), metadata: { 'x-aw-from-agent': 'lead', 'x-aw-task-kind': 'assign', 'x-aw-task-id': task.id } })
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const { rt: rtA, echo: echoA } = mkRt('w-mem')
|
|
211
|
-
const taskA = fakeEngine.create({ channelId: 'ch-mem', creatorId: 'lead', assigneeId: 'w-mem', title: '实现登录页面' })
|
|
212
|
-
enqueueAssign(rtA, taskA)
|
|
213
|
-
await new Promise<void>(r => setTimeout(r, 300))
|
|
214
|
-
check('任务 A 首跑无记忆注入', echoA.captured.at(-1)?.memory === undefined)
|
|
215
|
-
const aRow = repo.listByAgent('w-mem', 10).find(r => r.taskId === taskA.id)
|
|
216
|
-
check('任务 A 完成后沉淀记忆(含 deliverable)', aRow !== undefined && aRow.content.includes('memory-seen:NONE'), aRow?.content.slice(0, 60))
|
|
217
|
-
check('任务 A 记忆 importance=0.8(COMPLETED)', aRow?.importance === 0.8)
|
|
218
|
-
|
|
219
|
-
const taskB = fakeEngine.create({ channelId: 'ch-mem', creatorId: 'lead', assigneeId: 'w-mem', title: '登录模块优化' })
|
|
220
|
-
enqueueAssign(rtA, taskB)
|
|
221
|
-
await new Promise<void>(r => setTimeout(r, 300))
|
|
222
|
-
const secondReq = echoA.captured.at(-1)
|
|
223
|
-
check('任务 B 召回注入 A 的记忆(经 title_fts 相关命中)', secondReq?.memory !== undefined && secondReq.memory.includes('实现登录页面'), secondReq?.memory?.slice(0, 80))
|
|
224
|
-
check('记忆块带 prompt 标题头', (secondReq?.memory ?? '').startsWith('## 相关记忆'))
|
|
225
|
-
|
|
226
|
-
const { rt: rtO, echo: echoO } = mkRt('w-other')
|
|
227
|
-
const taskC = fakeEngine.create({ channelId: 'ch-mem', creatorId: 'lead', assigneeId: 'w-other', title: '登录鉴权怎么做' })
|
|
228
|
-
enqueueAssign(rtO, taskC)
|
|
229
|
-
await new Promise<void>(r => setTimeout(r, 300))
|
|
230
|
-
check('跨 agent 记忆隔离(w-other 召回不到 w-mem)', echoO.captured.at(-1)?.memory === undefined)
|
|
231
|
-
|
|
232
|
-
await rtA.stop()
|
|
233
|
-
await rtO.stop()
|
|
234
|
-
|
|
235
|
-
// ═══════════ 团队共享记忆域(manager 策展 + recall 通道隔离)═══════════
|
|
236
|
-
console.log('\n--- Team 共享记忆域 ---')
|
|
237
|
-
|
|
238
|
-
// 最小 manager 装配(照 test-dual-drive.ts;:memory: db;impl 仅占位)
|
|
239
|
-
const stubImpl = (_agent: AgentInfo): AgentInterface => {
|
|
240
|
-
throw new Error('impl unused')
|
|
241
|
-
}
|
|
242
|
-
/** 捕获 AppError code(未抛返回空串) */
|
|
243
|
-
function captureCode(fn: () => unknown): string {
|
|
244
|
-
try {
|
|
245
|
-
fn()
|
|
246
|
-
}
|
|
247
|
-
catch (e) {
|
|
248
|
-
return (e as AppError).code
|
|
249
|
-
}
|
|
250
|
-
return ''
|
|
251
|
-
}
|
|
252
|
-
const tmRepos = {
|
|
253
|
-
channels: createChannelRepo(db),
|
|
254
|
-
agents: createAgentRepo(db),
|
|
255
|
-
teams: createTeamRepo(db),
|
|
256
|
-
teamMembers: createTeamMemberRepo(db),
|
|
257
|
-
channelAgents: createChannelAgentRepo(db),
|
|
258
|
-
messages: createMessageRepo(db),
|
|
259
|
-
subscriptions: createSubscriptionRepo(db),
|
|
260
|
-
tasks: createTaskRepo(db),
|
|
261
|
-
memories: repo,
|
|
262
|
-
}
|
|
263
|
-
const teamManager = createAgentChannelManager({ repos: tmRepos, implFactory: stubImpl, db })
|
|
264
|
-
const chTeam = tmRepos.channels.create({ name: 'ch-team' })
|
|
265
|
-
const leadInst = tmRepos.channelAgents.create({ channelId: chTeam.id, templateId: null, name: 'lead', harness: 'mock', role: 'lead' })
|
|
266
|
-
const workerInst = tmRepos.channelAgents.create({ channelId: chTeam.id, templateId: null, name: 'worker', harness: 'mock', role: 'worker' })
|
|
267
|
-
|
|
268
|
-
const teamList = teamManager.addTeamMemory(chTeam.id, leadInst.id, { title: '部署规范', content: '全部使用pnpm并开启strict', dedupKey: 'deploy:pnpm' })
|
|
269
|
-
check('lead 写团队记忆成功入列', teamList.length === 1 && teamList[0].agentId === TEAM_AGENT_ID && teamList[0].channelId === chTeam.id)
|
|
270
|
-
const teamErrWrite = captureCode(() => teamManager.addTeamMemory(chTeam.id, workerInst.id, { title: 'x', content: 'y' }))
|
|
271
|
-
check('worker 写团队记忆抛 SCOPE_VIOLATION', teamErrWrite === 'SCOPE_VIOLATION', `code=${teamErrWrite}`)
|
|
272
|
-
|
|
273
|
-
teamManager.addTeamMemory(chTeam.id, leadInst.id, { title: '部署规范', content: '改用bun并保留strict', dedupKey: 'deploy:pnpm' })
|
|
274
|
-
const refreshed = teamManager.listTeamMemories(chTeam.id)
|
|
275
|
-
check('同 dedupKey 二次写幂等刷新(仍 1 条)', refreshed.length === 1 && refreshed[0].content.includes('bun') && !refreshed[0].content.includes('pnpm'))
|
|
276
|
-
|
|
277
|
-
const workerBlock = await new AgentMemory(repo, { channelId: chTeam.id, agentId: workerInst.id }).recall('部署规范')
|
|
278
|
-
check('worker recall 可见团队行', workerBlock !== null && workerBlock.includes('部署规范'))
|
|
279
|
-
|
|
280
|
-
const teamErrDelWorker = captureCode(() => teamManager.deleteTeamMemory(chTeam.id, workerInst.id, refreshed[0].id))
|
|
281
|
-
check('worker 删团队记忆抛 SCOPE_VIOLATION', teamErrDelWorker === 'SCOPE_VIOLATION', `code=${teamErrDelWorker}`)
|
|
282
|
-
|
|
283
|
-
teamManager.deleteTeamMemory(chTeam.id, leadInst.id, refreshed[0].id)
|
|
284
|
-
check('lead 删团队记忆成功', teamManager.listTeamMemories(chTeam.id).length === 0)
|
|
285
|
-
const teamErrDelMiss = captureCode(() => teamManager.deleteTeamMemory(chTeam.id, leadInst.id, 'no-such-id'))
|
|
286
|
-
check('删不存在团队记忆抛 NOT_FOUND', teamErrDelMiss === 'NOT_FOUND', `code=${teamErrDelMiss}`)
|
|
287
|
-
|
|
288
|
-
// ---- recall 通道隔离:team 行(FTS 来源)按本 channel 过滤 ----
|
|
289
|
-
repo.upsert({ channelId: 'ch-other', agentId: TEAM_AGENT_ID, kind: 'semantic', title: '他通道金丝雀', titleFts: seg('他通道金丝雀'), content: seg('跨通道独占水印金丝雀'), importance: 0.9, taskId: null, dedupKey: 'iso:other' })
|
|
290
|
-
repo.upsert({ channelId: 'ch1', agentId: TEAM_AGENT_ID, kind: 'semantic', title: '本通道金丝雀', titleFts: seg('本通道金丝雀'), content: seg('本通道共享金丝雀约定'), importance: 0.9, taskId: null, dedupKey: 'iso:ch1' })
|
|
291
|
-
const isoBlock = await new AgentMemory(repo, { channelId: 'ch1', agentId: 'a1' }).recall('金丝雀')
|
|
292
|
-
check('recall 不泄漏他通道团队记忆', isoBlock === null || !isoBlock.includes('他通道金丝雀'), isoBlock?.slice(0, 80))
|
|
293
|
-
check('recall 可见本通道团队记忆', isoBlock !== null && isoBlock.includes('本通道金丝雀'), isoBlock?.slice(0, 80))
|
|
294
|
-
|
|
295
|
-
// ═══════════ Agent 私有记忆策展(本人或 lead 写/删)═══════════
|
|
296
|
-
console.log('\n--- Agent 私有记忆策展 ---')
|
|
297
|
-
|
|
298
|
-
const otherInst = tmRepos.channelAgents.create({ channelId: chTeam.id, templateId: null, name: 'other', harness: 'mock', role: 'worker' })
|
|
299
|
-
|
|
300
|
-
teamManager.addAgentMemory(chTeam.id, workerInst.id, workerInst.id, { title: 'worker 私有笔记', content: '部署前必须跑全量测试', dedupKey: 'self:note' })
|
|
301
|
-
const selfRow = repo.listByAgent(workerInst.id, 50).find(r => r.title === 'worker 私有笔记')
|
|
302
|
-
check('本人写私有记忆成功入列', selfRow !== undefined && selfRow.agentId === workerInst.id && selfRow.channelId === chTeam.id && selfRow.kind === 'semantic' && selfRow.importance === 0.9)
|
|
303
|
-
|
|
304
|
-
const selfBlock = await new AgentMemory(repo, { channelId: chTeam.id, agentId: workerInst.id }).recall('私有笔记')
|
|
305
|
-
check('本人 recall 命中策展记忆', selfBlock !== null && selfBlock.includes('worker 私有笔记'), selfBlock?.slice(0, 80))
|
|
306
|
-
|
|
307
|
-
const agentErrOther = captureCode(() => teamManager.addAgentMemory(chTeam.id, otherInst.id, workerInst.id, { title: 'x', content: 'y' }))
|
|
308
|
-
check('非 lead 他人写抛 SCOPE_VIOLATION', agentErrOther === 'SCOPE_VIOLATION', `code=${agentErrOther}`)
|
|
309
|
-
check('他人写被拒后未落库', !repo.listByAgent(workerInst.id, 50).some(r => r.title === 'x'))
|
|
310
|
-
|
|
311
|
-
teamManager.addAgentMemory(chTeam.id, leadInst.id, workerInst.id, { title: 'lead 代写规范', content: '提交信息用中文', dedupKey: 'lead:note' })
|
|
312
|
-
check('lead 代写 worker 记忆成功', repo.listByAgent(workerInst.id, 50).some(r => r.title === 'lead 代写规范' && r.agentId === workerInst.id))
|
|
313
|
-
|
|
314
|
-
const agentErrDelOther = captureCode(() => teamManager.deleteAgentMemory(chTeam.id, otherInst.id, workerInst.id, selfRow!.id))
|
|
315
|
-
check('非 lead 他人删抛 SCOPE_VIOLATION', agentErrDelOther === 'SCOPE_VIOLATION', `code=${agentErrDelOther}`)
|
|
316
|
-
|
|
317
|
-
const agentErrDelMiss = captureCode(() => teamManager.deleteAgentMemory(chTeam.id, workerInst.id, workerInst.id, 'no-such-id'))
|
|
318
|
-
check('删不存在记忆抛 NOT_FOUND', agentErrDelMiss === 'NOT_FOUND', `code=${agentErrDelMiss}`)
|
|
319
|
-
|
|
320
|
-
teamManager.deleteAgentMemory(chTeam.id, workerInst.id, workerInst.id, selfRow!.id)
|
|
321
|
-
check('本人删自己的记忆成功', !repo.listByAgent(workerInst.id, 50).some(r => r.id === selfRow!.id))
|
|
322
|
-
|
|
323
|
-
const agentErrTargetMiss = captureCode(() => teamManager.addAgentMemory(chTeam.id, leadInst.id, 'no-such-agent', { title: 'x', content: 'y' }))
|
|
324
|
-
check('目标实例不存在抛 NOT_FOUND', agentErrTargetMiss === 'NOT_FOUND', `code=${agentErrTargetMiss}`)
|
|
325
|
-
|
|
326
|
-
// ═══════════ supervise 路径记忆注入(ctx.memory;快照驱动召回)═══════════
|
|
327
|
-
console.log('\n--- supervise 记忆注入 ---')
|
|
328
|
-
|
|
329
|
-
/** lead echo:捕获 supervise 收到的 ctx.memory */
|
|
330
|
-
class MemoryEchoSuperviseImpl implements AgentInterface {
|
|
331
|
-
capturedMemory: string | undefined
|
|
332
|
-
async* run(): AsyncGenerator<AgentEvent, void, unknown> {}
|
|
333
|
-
async supervise(_snapshot: SupervisionSnapshot, ctx: AgentRunContext): Promise<SupervisionDecision[]> {
|
|
334
|
-
this.capturedMemory = ctx.memory
|
|
335
|
-
return []
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/** lead runtime(supervise 由测试直调;withMemory=false 验证向后兼容) */
|
|
340
|
-
const mkLeadRt = (id: string, withMemory: boolean): { rt: AgentRuntime, echo: MemoryEchoSuperviseImpl } => {
|
|
341
|
-
const echo = new MemoryEchoSuperviseImpl()
|
|
342
|
-
const rt = new AgentRuntime(
|
|
343
|
-
{ id, channelId: 'ch-mem', name: id, harness: 'mock', role: 'lead', config: {} },
|
|
344
|
-
echo,
|
|
345
|
-
{
|
|
346
|
-
mailbox: new Mailbox(createMessageRepo(db), 'ch-mem', id, () => {}),
|
|
347
|
-
taskEngine: fakeEngine,
|
|
348
|
-
bus: memBus,
|
|
349
|
-
workspace: wsStub(id),
|
|
350
|
-
...(withMemory ? { memory: new AgentMemory(repo, { channelId: 'ch-mem', agentId: id }) } : {}),
|
|
351
|
-
},
|
|
352
|
-
)
|
|
353
|
-
return { rt, echo }
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// 预置 w-lead-sup 一条记忆(标题含"调度");快照含同标题 SUBMITTED 任务
|
|
357
|
-
repo.upsert({ channelId: 'ch-mem', agentId: 'w-lead-sup', kind: 'episodic-task', title: '调度优化登录任务', titleFts: seg('调度优化登录任务'), content: seg('上次把登录类任务优先派给空闲worker'), importance: 0.8, taskId: 'sup-t1', dedupKey: 'task:sup-t1' })
|
|
358
|
-
const supTask = fakeEngine.create({ channelId: 'ch-mem', creatorId: 'boss', assigneeId: 'w-mem', title: '调度优化登录任务' })
|
|
359
|
-
const supSnapshot: SupervisionSnapshot = {
|
|
360
|
-
tick: 1,
|
|
361
|
-
now: Date.now(),
|
|
362
|
-
tasks: [fakeEngine.get(supTask.id)!],
|
|
363
|
-
members: [
|
|
364
|
-
{ agentId: 'w-lead-sup', name: 'w-lead-sup', role: 'lead', state: 'idle' },
|
|
365
|
-
{ agentId: 'w-mem', name: 'w-mem', role: 'worker', state: 'idle' },
|
|
366
|
-
],
|
|
367
|
-
pendingChildren: {},
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const supBefore = repo.listByAgent('w-lead-sup', 10).find(r => r.title === '调度优化登录任务')!
|
|
371
|
-
const supLead = mkLeadRt('w-lead-sup', true)
|
|
372
|
-
const supDecisions = await supLead.rt.supervise(supSnapshot)
|
|
373
|
-
check('supervise 返回 echo 决策(空数组)', Array.isArray(supDecisions) && supDecisions.length === 0)
|
|
374
|
-
check('supervise 注入记忆块(## 相关记忆 + 预置标题)', supLead.echo.capturedMemory !== undefined && supLead.echo.capturedMemory.includes('## 相关记忆') && supLead.echo.capturedMemory.includes('调度优化登录任务'), supLead.echo.capturedMemory?.slice(0, 80))
|
|
375
|
-
const supAfter = repo.listByAgent('w-lead-sup', 10).find(r => r.title === '调度优化登录任务')!
|
|
376
|
-
check('supervise 召回 touch:false 不增 access_count', supAfter.accessCount === supBefore.accessCount, `before=${supBefore.accessCount} after=${supAfter.accessCount}`)
|
|
377
|
-
|
|
378
|
-
const plainLead = mkLeadRt('w-lead-plain', false)
|
|
379
|
-
const plainDecisions = await plainLead.rt.supervise(supSnapshot)
|
|
380
|
-
check('无 memory deps 的 runtime supervise 正常(不注入不抛错)', Array.isArray(plainDecisions) && plainLead.echo.capturedMemory === undefined)
|
|
381
|
-
|
|
382
|
-
// ═══════════ 最终审查修复:env 守卫(I2)+ 策展向量化静默降级(M2)═══════════
|
|
383
|
-
console.log('\n--- 最终审查修复(I2/M2)---')
|
|
384
|
-
|
|
385
|
-
const restoreEnv = (name: string, value: string | undefined): void => {
|
|
386
|
-
if (value === undefined) Reflect.deleteProperty(process.env, name)
|
|
387
|
-
else process.env[name] = value
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// I2:AW_MEMORY_BUDGET_TOKENS 非法/非正 → envNum 回退 800,recall 正常(不抛/不清空)
|
|
391
|
-
const savedBudget = process.env.AW_MEMORY_BUDGET_TOKENS
|
|
392
|
-
process.env.AW_MEMORY_BUDGET_TOKENS = 'garbage'
|
|
393
|
-
const garbageBudget = await new AgentMemory(repo, { channelId: 'ch-mem', agentId: 'w-mem' }).recall('登录')
|
|
394
|
-
check('AW_MEMORY_BUDGET_TOKENS 非法回退 800(recall 正常返回)', typeof garbageBudget === 'string' && garbageBudget.length > 0,
|
|
395
|
-
garbageBudget?.slice(0, 60))
|
|
396
|
-
process.env.AW_MEMORY_BUDGET_TOKENS = '0'
|
|
397
|
-
const zeroBudget = await new AgentMemory(repo, { channelId: 'ch-mem', agentId: 'w-mem' }).recall('登录')
|
|
398
|
-
check('AW_MEMORY_BUDGET_TOKENS=0 拒绝并回退(recall 正常返回)', typeof zeroBudget === 'string' && zeroBudget.length > 0)
|
|
399
|
-
restoreEnv('AW_MEMORY_BUDGET_TOKENS', savedBudget)
|
|
400
|
-
|
|
401
|
-
// I2 + M2:MAINTENANCE_MS=0(非法定时器间隔)+ 不可达 env provider → 构造/策展/shutdown 全干净
|
|
402
|
-
const savedMs = process.env.AW_MEMORY_MAINTENANCE_MS
|
|
403
|
-
const savedUrl = process.env.AW_MEMORY_EMBED_BASE_URL
|
|
404
|
-
const savedModel = process.env.AW_MEMORY_EMBED_MODEL
|
|
405
|
-
process.env.AW_MEMORY_MAINTENANCE_MS = '0'
|
|
406
|
-
process.env.AW_MEMORY_EMBED_BASE_URL = 'http://127.0.0.1:1' // 立即拒连,零等待
|
|
407
|
-
process.env.AW_MEMORY_EMBED_MODEL = 'fix-wave'
|
|
408
|
-
const fixManager = createAgentChannelManager({ repos: tmRepos, implFactory: stubImpl, db })
|
|
409
|
-
const fixCh = tmRepos.channels.create({ name: 'ch-fix' })
|
|
410
|
-
const fixLead = tmRepos.channelAgents.create({ channelId: fixCh.id, templateId: null, name: 'fixlead', harness: 'mock', role: 'lead' })
|
|
411
|
-
let curatedCrash = ''
|
|
412
|
-
try {
|
|
413
|
-
fixManager.addTeamMemory(fixCh.id, fixLead.id, { title: '修复波团队规范', content: '修复波内容统一水印', dedupKey: 'team:fix' })
|
|
414
|
-
fixManager.addAgentMemory(fixCh.id, fixLead.id, fixLead.id, { title: '修复波私有笔记', content: '修复波私有内容', dedupKey: 'self:fix' })
|
|
415
|
-
}
|
|
416
|
-
catch (e) {
|
|
417
|
-
curatedCrash = String(e)
|
|
418
|
-
}
|
|
419
|
-
check('dummy env 下策展写不崩(向量化 fire-and-forget 静默失败)',
|
|
420
|
-
curatedCrash === '' && repo.listByAgentChannel(fixCh.id, TEAM_AGENT_ID, 10).some(r => r.title === '修复波团队规范'),
|
|
421
|
-
curatedCrash)
|
|
422
|
-
await new Promise<void>(r => setTimeout(r, 300)) // 等 fire-and-forget 向量化失败落定
|
|
423
|
-
const shutdownStart = Date.now()
|
|
424
|
-
await fixManager.shutdown()
|
|
425
|
-
check('AW_MEMORY_MAINTENANCE_MS=0 构造 + shutdown 干净快速(<2s)', Date.now() - shutdownStart < 2000,
|
|
426
|
-
`${Date.now() - shutdownStart}ms`)
|
|
427
|
-
restoreEnv('AW_MEMORY_MAINTENANCE_MS', savedMs)
|
|
428
|
-
restoreEnv('AW_MEMORY_EMBED_BASE_URL', savedUrl)
|
|
429
|
-
restoreEnv('AW_MEMORY_EMBED_MODEL', savedModel)
|
|
430
|
-
console.log(`\n${failures === 0 ? 'ALL PASS' : `${failures} FAILURE(S)`}`)
|
|
431
|
-
process.exit(failures === 0 ? 0 : 1)
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 真实 omp harness 场景测试 — tmp 文件夹作为 workspace。
|
|
3
|
-
*
|
|
4
|
-
* 场景(简单):
|
|
5
|
-
* 1. 在 .tmp-omp-e2e/ 建 workspace,放入 input.txt(一段简单内容)
|
|
6
|
-
* 2. createChannel(workspace=tmp, leadAgent=omp)→ lead 模板 + 克隆实例
|
|
7
|
-
* 3. createAgent(omp) 模板 → addAgentToChannel 克隆 worker 实例
|
|
8
|
-
* 4. ensureChannelActive + monitorChannel 挂监控
|
|
9
|
-
* 5. submitChannelTask:lead 分配,worker 用 omp 原生 read 工具读取 input.txt,回写成果
|
|
10
|
-
* 6. 监控执行过程(dispatch/working/progress/artifact/completed)
|
|
11
|
-
*
|
|
12
|
-
* 运行: npx tsx scripts/e2e-omp-workspace.ts
|
|
13
|
-
*/
|
|
14
|
-
import type { DatabaseSync } from 'node:sqlite'
|
|
15
|
-
import { mkdirSync, writeFileSync, rmSync } from 'node:fs'
|
|
16
|
-
import { resolve } from 'node:path'
|
|
17
|
-
import { openWorkshopDb } from '../server/services/workshop/db/database'
|
|
18
|
-
import { createChannelRepo } from '../server/services/workshop/db/channel.repo'
|
|
19
|
-
import { createAgentRepo } from '../server/services/workshop/db/agent.repo'
|
|
20
|
-
import { createChannelAgentRepo } from '../server/services/workshop/db/channel-agent.repo'
|
|
21
|
-
import { createTaskRepo } from '../server/services/workshop/db/task.repo'
|
|
22
|
-
import { createMemoryRepo } from '../server/services/workshop/db/memory.repo'
|
|
23
|
-
import { createChannelEventRepo } from '../server/services/workshop/db/channel-event.repo'
|
|
24
|
-
import { createTeamRepo } from '../server/services/workshop/db/team.repo'
|
|
25
|
-
import { createTeamMemberRepo } from '../server/services/workshop/db/team-member.repo'
|
|
26
|
-
import { createMessageRepo } from '../server/services/workshop/db/message.repo'
|
|
27
|
-
import { createSubscriptionRepo } from '../server/services/workshop/db/subscription.repo'
|
|
28
|
-
import { createAgentChannelManager } from '../server/services/workshop/runtime/manager'
|
|
29
|
-
import type { AgentChannelManager } from '../server/services/workshop/runtime/manager'
|
|
30
|
-
import { createAgentImpl } from '../server/services/workshop/agents/factory'
|
|
31
|
-
import { monitorChannel } from '../server/services/workshop/runtime/monitor'
|
|
32
|
-
|
|
33
|
-
let failures = 0
|
|
34
|
-
|
|
35
|
-
function check(name: string, ok: boolean, detail = ''): void {
|
|
36
|
-
console.log(` ${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
37
|
-
if (!ok) failures += 1
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function makeManager(db: DatabaseSync): AgentChannelManager {
|
|
41
|
-
return createAgentChannelManager({
|
|
42
|
-
repos: {
|
|
43
|
-
channels: createChannelRepo(db),
|
|
44
|
-
agents: createAgentRepo(db),
|
|
45
|
-
channelAgents: createChannelAgentRepo(db),
|
|
46
|
-
messages: createMessageRepo(db),
|
|
47
|
-
subscriptions: createSubscriptionRepo(db),
|
|
48
|
-
tasks: createTaskRepo(db),
|
|
49
|
-
memories: createMemoryRepo(db),
|
|
50
|
-
|
|
51
|
-
channelEvents: createChannelEventRepo(db),
|
|
52
|
-
teams: createTeamRepo(db),
|
|
53
|
-
teamMembers: createTeamMemberRepo(db),
|
|
54
|
-
},
|
|
55
|
-
implFactory: createAgentImpl,
|
|
56
|
-
db,
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms))
|
|
61
|
-
|
|
62
|
-
async function main(): Promise<void> {
|
|
63
|
-
console.log('━━━ 真实 omp harness:tmp workspace 任务执行 + 监控 ━━━')
|
|
64
|
-
|
|
65
|
-
const ws = resolve(process.cwd(), '.tmp-omp-e2e')
|
|
66
|
-
mkdirSync(ws, { recursive: true })
|
|
67
|
-
const payload = '真实场景作业:omp 读取此文件并原样回写成果'
|
|
68
|
-
writeFileSync(resolve(ws, 'input.txt'), payload, 'utf8')
|
|
69
|
-
|
|
70
|
-
const db = openWorkshopDb(':memory:')
|
|
71
|
-
const manager = makeManager(db)
|
|
72
|
-
try {
|
|
73
|
-
// ---- 1. 创建 Channel(tmp workspace + omp lead)----
|
|
74
|
-
console.log('\n--- 1. 创建 Channel(workspace=tmp)---')
|
|
75
|
-
const ch = await manager.createChannel({
|
|
76
|
-
name: 'OMP 真实场景',
|
|
77
|
-
description: 'tmp workspace 简单作业',
|
|
78
|
-
workspace: ws,
|
|
79
|
-
leadAgent: { name: 'lead-主理人', harness: 'omp', config: {} },
|
|
80
|
-
})
|
|
81
|
-
check('Channel 创建成功(workspace 生效)', ch.workspace === ws && !!ch.leadAgentId)
|
|
82
|
-
|
|
83
|
-
// ---- 2. Agent 模板 → 克隆 worker 实例 ----
|
|
84
|
-
console.log('\n--- 2. Agent 模板克隆 worker 实例 ---')
|
|
85
|
-
const workerTpl = await manager.createAgent({ name: 'worker-执行者', harness: 'omp', config: {} })
|
|
86
|
-
const worker = await manager.addAgentToChannel({ channelId: ch.channelId, agentId: workerTpl.id, role: 'worker' })
|
|
87
|
-
check('worker 克隆出独立实例 id', worker.id !== workerTpl.id)
|
|
88
|
-
check('成员结构 1 lead + 1 worker', (await manager.listChannelAgents(ch.channelId)).length === 2)
|
|
89
|
-
|
|
90
|
-
// ---- 3. 激活 + 挂监控 ----
|
|
91
|
-
console.log('\n--- 3. 激活 channel + 挂监控 ---')
|
|
92
|
-
manager.ensureChannelActive(ch.channelId, { tickMs: 3000, stallMs: 60_000 })
|
|
93
|
-
const mon = monitorChannel(manager, ch.channelId)
|
|
94
|
-
check('监控器已启动', mon.events.some(e => e.kind === 'lifecycle'))
|
|
95
|
-
|
|
96
|
-
// ---- 4. 提交任务(lead 真实 omp 分配)----
|
|
97
|
-
console.log('\n--- 4. 提交任务 ---')
|
|
98
|
-
const task = await manager.submitChannelTask({
|
|
99
|
-
channelId: ch.channelId,
|
|
100
|
-
title: '读取并复述 input.txt',
|
|
101
|
-
description: '读取工作目录里的 input.txt 文件,把内容原样写在任务成果里,然后完成任务。简短回答。',
|
|
102
|
-
})
|
|
103
|
-
check('任务已提交(SUBMITTED)', task.state === 'SUBMITTED', `state=${task.state}`)
|
|
104
|
-
console.log(` task=${task.id.slice(0, 8)}…`)
|
|
105
|
-
|
|
106
|
-
// ---- 5. 监控执行(定向等待主任务 COMPLETED;omp 真实推理,给足时间)----
|
|
107
|
-
console.log('\n--- 5. 监控执行过程(真实 omp)---')
|
|
108
|
-
const started = Date.now()
|
|
109
|
-
const done = await mon.waitFor(e => e.kind === 'task.status' && e.taskId === task.id && e.state === 'COMPLETED', 240_000)
|
|
110
|
-
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
111
|
-
console.log(` ⏱ 耗时 ${elapsed}s`)
|
|
112
|
-
check('主任务执行完成(COMPLETED)', done !== null)
|
|
113
|
-
|
|
114
|
-
const seenAssigned = mon.events.some(e => e.kind === 'task.status' && e.state === 'ASSIGNED')
|
|
115
|
-
const seenWorking = mon.events.some(e => e.kind === 'task.status' && e.state === 'WORKING')
|
|
116
|
-
const seenProgress = mon.events.some(e => e.kind === 'task.progress' && e.progress > 0)
|
|
117
|
-
const seenArtifact = mon.events.some(e => e.kind === 'agent.event' && e.event.kind === 'artifact')
|
|
118
|
-
const seenBusy = mon.events.some(e => e.kind === 'agent.status' && e.state === 'busy')
|
|
119
|
-
const seenIdle = mon.events.some(e => e.kind === 'agent.status' && e.state === 'idle')
|
|
120
|
-
check('监控到任务进入 WORKING', seenWorking)
|
|
121
|
-
check('监控到进度上报', seenProgress)
|
|
122
|
-
check('监控到成员 busy → idle', seenBusy && seenIdle)
|
|
123
|
-
if (!seenAssigned) console.log(' 信息:未捕获 ASSIGNED 事件(omp host tool 分发路径可能跳过显式 ASSIGNED 状态事件)')
|
|
124
|
-
if (!seenArtifact) console.log(' 信息:未捕获 artifact 事件(成果可能直接落入任务 artifacts,不经过 agent.event)')
|
|
125
|
-
|
|
126
|
-
// ---- 6. 终态校验(成果包含 payload)----
|
|
127
|
-
const engine = (manager as unknown as {
|
|
128
|
-
getTaskEngine(): { list(channelId: string): Array<{ id: string, parentId: string | null, state: string, artifacts: Array<{ parts: Array<{ text?: string }> }> }> }
|
|
129
|
-
}).getTaskEngine()
|
|
130
|
-
const allTasks = engine.list(ch.channelId)
|
|
131
|
-
const children = allTasks.filter(t => t.parentId === task.id)
|
|
132
|
-
const finalTask = allTasks.find(t => t.id === task.id)
|
|
133
|
-
const artifactTexts = (finalTask?.artifacts ?? []).flatMap(a => a.parts.filter(p => 'text' in p).map(p => (p as { text: string }).text)).join('\n')
|
|
134
|
-
const childTexts = children.flatMap(c => c.artifacts.flatMap(a => a.parts.filter(p => 'text' in p).map(p => (p as { text: string }).text))).join('\n')
|
|
135
|
-
check('lead 分配了子任务', children.length >= 1, `子任务=${children.length}`)
|
|
136
|
-
check('主任务 COMPLETED + 有成果', finalTask?.state === 'COMPLETED' && (finalTask?.artifacts.length ?? 0) > 0, `state=${finalTask?.state} artifacts=${finalTask?.artifacts.length}`)
|
|
137
|
-
check('成果包含 input.txt 内容(主任务或子任务)', artifactTexts.includes(payload) || childTexts.includes(payload), `parent=${artifactTexts.slice(0, 50)}… | child=${childTexts.slice(0, 50)}…`)
|
|
138
|
-
|
|
139
|
-
// ---- 7. 监控时间线 ----
|
|
140
|
-
console.log('\n--- 监控时间线 ---')
|
|
141
|
-
console.log(mon.summary())
|
|
142
|
-
mon.stop()
|
|
143
|
-
|
|
144
|
-
// ---- 8. 释放 omp 子进程 ----
|
|
145
|
-
console.log('\n--- 收尾:卸载 omp 子进程 ---')
|
|
146
|
-
for (let i = 0; i < 20; i++) {
|
|
147
|
-
await manager.unloadIdleAgents()
|
|
148
|
-
if (manager.runtimeStatus().wiredAgents.length === 0) break
|
|
149
|
-
await sleep(500)
|
|
150
|
-
}
|
|
151
|
-
check('完成后卸载(释放 omp 子进程)', manager.runtimeStatus().wiredAgents.length === 0)
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
await manager.shutdown()
|
|
155
|
-
db.close()
|
|
156
|
-
// Windows 收尾竞态:omp 子进程(含终端镜像)可能仍持有 cwd 目录句柄,
|
|
157
|
-
// 立即 rmSync 会 EPERM —— 带宽限重试,仍失败则告警保留(不掩盖功能断言结果)。
|
|
158
|
-
for (let attempt = 0; attempt < 5; attempt++) {
|
|
159
|
-
try {
|
|
160
|
-
rmSync(ws, { recursive: true, force: true })
|
|
161
|
-
break
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
if (attempt === 4) {
|
|
165
|
-
console.warn(`[e2e-omp-workspace] 临时工作区清理失败(Windows 句柄占用),已保留: ${ws}`)
|
|
166
|
-
break
|
|
167
|
-
}
|
|
168
|
-
await sleep(500)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
console.log(`\n${failures === 0 ? 'ALL PASS' : `${failures} FAILED`}`)
|
|
174
|
-
process.exit(failures === 0 ? 0 : 1)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
main().catch((e) => {
|
|
178
|
-
console.error('e2e 异常:', e)
|
|
179
|
-
process.exit(1)
|
|
180
|
-
})
|