agentworkshop 0.7.6 → 0.7.7
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 +1 -1
- package/.output/.AgentWorkShop/prompts/host-tools.json +1 -1
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{rzGB_ojX.js → 3612gD2V.js} +2 -2
- package/.output/public/_nuxt/8eIQAVlb.js +1 -0
- package/.output/public/_nuxt/Ah9qOv1m.js +1 -0
- package/.output/public/_nuxt/{DdPeifHR.js → B4Fia4FB.js} +1 -1
- package/.output/public/_nuxt/B7aqqzkY.js +1 -0
- package/.output/public/_nuxt/BJ-fxrSw.js +1 -0
- package/.output/public/_nuxt/{DPDqAD1e.js → BSXx2vqR.js} +1 -1
- package/.output/public/_nuxt/{CdSgfcYR.js → BVZjSVs2.js} +1 -1
- package/.output/public/_nuxt/{DP6bdyIp.js → BVzDoF8t.js} +1 -1
- package/.output/public/_nuxt/BYL0Rjqj.js +7 -0
- package/.output/public/_nuxt/{CXmkVXS5.js → BjVLsC3M.js} +1 -1
- package/.output/public/_nuxt/{7ZABjMRh2.js → BkfchkK62.js} +1 -1
- package/.output/public/_nuxt/BnVoQ74r.js +1 -0
- package/.output/public/_nuxt/Bnp4vIAf.js +1 -0
- package/.output/public/_nuxt/BsMpJf7S.js +70 -0
- package/.output/public/_nuxt/{Z8k0uMSI.js → Bt_HDizI.js} +1 -1
- package/.output/public/_nuxt/Bu-P92Pe.js +18 -0
- package/.output/public/_nuxt/ButLXkjo.js +2 -0
- package/.output/public/_nuxt/C-GarxYC.js +1 -0
- package/.output/public/_nuxt/{D8DDDzV62.js → C2iWjzEC2.js} +1 -1
- package/.output/public/_nuxt/{HWXLx5Hz.js → C8dxXc6K.js} +5 -5
- package/.output/public/_nuxt/CGdPdJPP.js +1 -0
- package/.output/public/_nuxt/{CyygA0q4.js → CQiKcuoG.js} +4 -4
- package/.output/public/_nuxt/{CN7Zh8GQ.js → CRGlf1Gb.js} +1 -1
- package/.output/public/_nuxt/{DSBfvCi5.js → CblLEiCZ.js} +7 -7
- package/.output/public/_nuxt/Cd15nPnG2.js +1 -0
- package/.output/public/_nuxt/{OwJ1LJkF.js → CsCpu2Zh.js} +1 -1
- package/.output/public/_nuxt/CvbHIGQY2.js +1 -0
- package/.output/public/_nuxt/Cyk3du4K2.js +1 -0
- package/.output/public/_nuxt/{DVQAz9WI.js → D4qXvjuV.js} +2 -2
- package/.output/public/_nuxt/{CfIXY-ET.js → DCEyMaN-.js} +1 -1
- package/.output/public/_nuxt/{BFYNhG9S.js → DCXIGxzT.js} +1 -1
- package/.output/public/_nuxt/{B9aPoQvt.js → DM8DS9MA.js} +1 -1
- package/.output/public/_nuxt/{ChnfyQHz.js → DNzaswei.js} +1 -1
- package/.output/public/_nuxt/{CrFGZYVB2.js → DYZBdSSg2.js} +1 -1
- package/.output/public/_nuxt/{dObsRdSQ.js → DZk24ego.js} +1 -1
- package/.output/public/_nuxt/DeYsUzhr.js +1 -0
- package/.output/public/_nuxt/Dk6pq9JB2.js +1 -0
- package/.output/public/_nuxt/{ByGNWgAr.js → Do88WZG2.js} +3 -3
- package/.output/public/_nuxt/{R_WxTozV2.js → Du-eSpo2.js} +1 -1
- package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +1 -0
- package/.output/public/_nuxt/{Bsc69G4x.js → VnIWJ0ts.js} +3 -3
- package/.output/public/_nuxt/_id_.BzKMZnLe.css +1 -0
- package/.output/public/_nuxt/_id_.CbpDAWRh.css +1 -0
- package/.output/public/_nuxt/{_wsId_.DET_GinP.css → _wsId_.COUzMXpP.css} +1 -1
- package/.output/public/_nuxt/agents.BlejnOOC.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/14df171d-89cb-43ec-ad6f-9df72d6da7f5.json +1 -0
- package/.output/public/_nuxt/daq.zZ8GR433.css +1 -0
- package/.output/public/_nuxt/{default.D17kXlD6.css → default.V3_hUJq1.css} +1 -1
- package/.output/public/_nuxt/{B4GHHF6d2.js → fQUs58kX2.js} +1 -1
- package/.output/public/_nuxt/p7wSGMtc.js +1 -0
- package/.output/public/_nuxt/tRBT-plo.js +1 -0
- package/.output/server/chunks/_/SearchOutlined.mjs +4 -4
- package/.output/server/chunks/_/SearchOutlined.mjs.map +1 -1
- package/.output/server/chunks/_/character-asset.repo.mjs +3 -6
- package/.output/server/chunks/_/character-asset.repo.mjs.map +1 -1
- package/.output/server/chunks/_/host-bindings.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +4967 -2035
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/scene-layout.repo.mjs +4 -10
- package/.output/server/chunks/_/scene-layout.repo.mjs.map +1 -1
- package/.output/server/chunks/build/{Col-D7fjGDF6.mjs → Col-biaNnEPf.mjs} +2 -2
- package/.output/server/chunks/build/{Col-D7fjGDF6.mjs.map → Col-biaNnEPf.mjs.map} +1 -1
- package/.output/server/chunks/build/{OmpTerminalPanel-B_SYhmgb.mjs → OmpTerminalPanel-COphA1br.mjs} +20 -19
- package/.output/server/chunks/build/OmpTerminalPanel-COphA1br.mjs.map +1 -0
- package/.output/server/chunks/build/{TownView-Bl8Gkcxi.mjs → TownView-B5KifIaT.mjs} +4 -4
- package/.output/server/chunks/build/{TownView-Bl8Gkcxi.mjs.map → TownView-B5KifIaT.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-GnRJPykX.mjs → _id_-C-ewLgfl.mjs} +119 -119
- package/.output/server/chunks/build/{_id_-GnRJPykX.mjs.map → _id_-C-ewLgfl.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-CWLFGZjV.mjs → _id_-Ct4Jhvl1.mjs} +42 -38
- package/.output/server/chunks/build/_id_-Ct4Jhvl1.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-BhQMjyHG.mjs → _wsId_-CanI_6Su.mjs} +591 -53
- package/.output/server/chunks/build/{_wsId_-BhQMjyHG.mjs.map → _wsId_-CanI_6Su.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-D4l1icvn.mjs → agents-DSOptxXA.mjs} +146 -91
- package/.output/server/chunks/build/agents-DSOptxXA.mjs.map +1 -0
- package/.output/server/chunks/build/{alert-gH-s7yIh.mjs → alert-qJnjyRcP.mjs} +3 -3
- package/.output/server/chunks/build/{alert-gH-s7yIh.mjs.map → alert-qJnjyRcP.mjs.map} +1 -1
- package/.output/server/chunks/build/{button-BhbX5ACI.mjs → button-DEcZFcqN.mjs} +3 -3
- package/.output/server/chunks/build/{button-BhbX5ACI.mjs.map → button-DEcZFcqN.mjs.map} +1 -1
- package/.output/server/chunks/build/{card-l4BbX67w.mjs → card-Dipvaykh.mjs} +5 -5
- package/.output/server/chunks/build/{card-l4BbX67w.mjs.map → card-Dipvaykh.mjs.map} +1 -1
- package/.output/server/chunks/build/{channel-templates-DqjtC-R0.mjs → channel-templates-BK0KuPOd.mjs} +13 -12
- package/.output/server/chunks/build/channel-templates-BK0KuPOd.mjs.map +1 -0
- package/.output/server/chunks/build/{checkbox-BF11p3dH.mjs → checkbox-dzcpU_Cd.mjs} +4 -4
- package/.output/server/chunks/build/{checkbox-BF11p3dH.mjs.map → checkbox-dzcpU_Cd.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-BOgpn9sl.mjs → daq-D9RA_uze.mjs} +102 -102
- package/.output/server/chunks/build/{daq-BOgpn9sl.mjs.map → daq-D9RA_uze.mjs.map} +1 -1
- package/.output/server/chunks/build/dcw-BTM5YU-B.mjs +1 -1
- package/.output/server/chunks/build/{default-CKJWhkot.mjs → default-BfWGTlbj.mjs} +81 -23
- package/.output/server/chunks/build/{default-CKJWhkot.mjs.map → default-BfWGTlbj.mjs.map} +1 -1
- package/.output/server/chunks/build/{drawer-CVValqoh.mjs → drawer-Cmh90erI.mjs} +3 -3
- package/.output/server/chunks/build/{drawer-CVValqoh.mjs.map → drawer-Cmh90erI.mjs.map} +1 -1
- package/.output/server/chunks/build/{dropdown-ByCsEWlQ.mjs → dropdown-CGQ0KAzA.mjs} +2 -2
- package/.output/server/chunks/build/{dropdown-ByCsEWlQ.mjs.map → dropdown-CGQ0KAzA.mjs.map} +1 -1
- package/.output/server/chunks/build/{dropdown-Dex8PXu6.mjs → dropdown-vhn1NPFs.mjs} +4 -4
- package/.output/server/chunks/build/{dropdown-Dex8PXu6.mjs.map → dropdown-vhn1NPFs.mjs.map} +1 -1
- package/.output/server/chunks/build/{input-number-BBlRoAJ7.mjs → input-number-Bd55elNN.mjs} +5 -5
- package/.output/server/chunks/build/{input-number-BBlRoAJ7.mjs.map → input-number-Bd55elNN.mjs.map} +1 -1
- package/.output/server/chunks/build/{input--KMeLYqZ.mjs → input-sf_BT6o6.mjs} +3 -3
- package/.output/server/chunks/build/{input--KMeLYqZ.mjs.map → input-sf_BT6o6.mjs.map} +1 -1
- package/.output/server/chunks/build/logs-Dyk0I2I9.mjs +1 -1
- package/.output/server/chunks/build/{menu-xN-SXb9Q.mjs → menu-WCrlpYVh.mjs} +3 -3
- package/.output/server/chunks/build/{menu-xN-SXb9Q.mjs.map → menu-WCrlpYVh.mjs.map} +1 -1
- package/.output/server/chunks/build/{modal-DrCOcjrl.mjs → modal-yvUquPj5.mjs} +4 -4
- package/.output/server/chunks/build/{modal-DrCOcjrl.mjs.map → modal-yvUquPj5.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-KW8WLOJd.mjs → monitor-MIOvsyLa.mjs} +21 -21
- package/.output/server/chunks/build/{monitor-KW8WLOJd.mjs.map → monitor-MIOvsyLa.mjs.map} +1 -1
- package/.output/server/chunks/build/pages-DR2bzczM.mjs +1 -1
- package/.output/server/chunks/build/{plugins-B-ZNey0Z.mjs → plugins-DnOBIWGg.mjs} +7 -7
- package/.output/server/chunks/build/{plugins-B-ZNey0Z.mjs.map → plugins-DnOBIWGg.mjs.map} +1 -1
- package/.output/server/chunks/build/{popconfirm-C5H1Qoh3.mjs → popconfirm-CRaHcz5O.mjs} +4 -4
- package/.output/server/chunks/build/{popconfirm-C5H1Qoh3.mjs.map → popconfirm-CRaHcz5O.mjs.map} +1 -1
- package/.output/server/chunks/build/{useWorkshopApi-Bh5VUHWu.mjs → segmented-DPe9ZzLO.mjs} +7 -78
- package/.output/server/chunks/build/segmented-DPe9ZzLO.mjs.map +1 -0
- package/.output/server/chunks/build/{select-D7H2ysgT.mjs → select-D-CeE5vY.mjs} +3 -3
- package/.output/server/chunks/build/{select-D7H2ysgT.mjs.map → select-D-CeE5vY.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-Cr06lbRy.mjs → settings-DA8IkGLo.mjs} +11 -11
- package/.output/server/chunks/build/{settings-Cr06lbRy.mjs.map → settings-DA8IkGLo.mjs.map} +1 -1
- package/.output/server/chunks/build/{space-DCwIlnkI.mjs → space-CCBYAkXt.mjs} +2 -2
- package/.output/server/chunks/build/{space-DCwIlnkI.mjs.map → space-CCBYAkXt.mjs.map} +1 -1
- package/.output/server/chunks/build/{switch-VC0m9ede.mjs → switch-CelRXpcy.mjs} +3 -3
- package/.output/server/chunks/build/{switch-VC0m9ede.mjs.map → switch-CelRXpcy.mjs.map} +1 -1
- package/.output/server/chunks/build/{table-BksLg3Q_.mjs → table-Cswq-l2r.mjs} +10 -10
- package/.output/server/chunks/build/{table-BksLg3Q_.mjs.map → table-Cswq-l2r.mjs.map} +1 -1
- package/.output/server/chunks/build/{tabs-CzUfD3iX.mjs → tabs-BM3tAh_J.mjs} +5 -5
- package/.output/server/chunks/build/{tabs-CzUfD3iX.mjs.map → tabs-BM3tAh_J.mjs.map} +1 -1
- package/.output/server/chunks/build/{tag-oQI3VtqM.mjs → tag-NO9jEqKy.mjs} +4 -4
- package/.output/server/chunks/build/{tag-oQI3VtqM.mjs.map → tag-NO9jEqKy.mjs.map} +1 -1
- package/.output/server/chunks/build/{teams-QR3BBF7A.mjs → teams-BhSKBBBM.mjs} +15 -14
- package/.output/server/chunks/build/teams-BhSKBBBM.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-DFvr_TLj.mjs → tokens-D_eyxmcv.mjs} +16 -16
- package/.output/server/chunks/build/{tokens-DFvr_TLj.mjs.map → tokens-D_eyxmcv.mjs.map} +1 -1
- package/.output/server/chunks/build/town-BUqRobze.mjs +1 -1
- package/.output/server/chunks/build/{town-_4tEEEVW.mjs → town-DPtOPRRO.mjs} +4 -4
- package/.output/server/chunks/build/{town-_4tEEEVW.mjs.map → town-DPtOPRRO.mjs.map} +1 -1
- package/.output/server/chunks/build/useLocalTime-9a4B-aHv.mjs +42 -0
- package/.output/server/chunks/build/useLocalTime-9a4B-aHv.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs +81 -0
- package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkshopWs-B3j5wIAK.mjs +1 -1
- package/.output/server/chunks/build/user-Dk-ytEUV.mjs +1 -1
- package/.output/server/chunks/build/{users-4FZ0yFUO.mjs → users-CmVvb1oq.mjs} +16 -16
- package/.output/server/chunks/build/{users-4FZ0yFUO.mjs.map → users-CmVvb1oq.mjs.map} +1 -1
- package/.output/server/chunks/build/{workshop-k0XZTFKg.mjs → workshop-iCzNRifn.mjs} +9 -9
- package/.output/server/chunks/build/{workshop-k0XZTFKg.mjs.map → workshop-iCzNRifn.mjs.map} +1 -1
- package/.output/server/chunks/build/workspaces-IfORCDJE.mjs +1 -1
- package/.output/server/chunks/routes/api/health.get.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
- package/.output/server/chunks/routes/api/metrics.mjs +1 -1
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +1 -1
- package/.output/server/chunks/routes/api/plugins/client/_name_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/plugins/manifest.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/config.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/index.get.mjs +3 -3
- package/.output/server/chunks/routes/api/system/index.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/index.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/system/monitor/terminal/ws.mjs +1 -1
- package/.output/server/chunks/routes/api/system/monitor/terminate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/system/monitor.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/settings/events.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/settings/reset.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/users/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/_id_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/users/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/login.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/logout.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/me.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/register.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/tokens/_id/token.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/tokens/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/users/tokens/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/_...path_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/a2a/send.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/approvals/_id/decide.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +1 -1
- 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 +6 -14
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs +39 -0
- package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/agents/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agents/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agents/subscribe.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/approvals/_id/decide.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/character/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/devices/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/assets/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index2.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/audit.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/caller.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/from-channel.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/_memoryId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/search.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/model.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/position.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/range.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/events.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/channels/_id/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index3.get.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.get.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/memories/_memoryId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/queue.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/channels/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs +9 -2
- package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +6 -4
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/test.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/mark-good.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +2 -2
- 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 +2 -2
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +247 -0
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +27 -0
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +19 -7
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index10.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/index4.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/index4.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/index5.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index8.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index8.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/mailbox/all.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/mailbox.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/memories/maintenance.post.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/plugins/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/runtime.get.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/scene/layouts.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/complete.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/dispatch.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/report.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/retry.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/teams/_id/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/members/_agentId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/users/me.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/users/register.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/chunks/virtual/entry.mjs +123 -113
- package/.output/server/chunks/virtual/entry.mjs.map +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/index.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/app/components/AppHeader.vue +56 -2
- package/app/components/workshop/ChannelSessionList.vue +703 -593
- package/app/components/workshop/terminal/OmpTerminalPanel.vue +2 -1
- package/app/composables/workshop/useLocalTime.ts +16 -0
- package/app/composables/workshop/useSceneLayouts.ts +2 -1
- package/app/composables/workshop/useWorkshopApi.ts +249 -224
- package/app/pages/daq/[id].vue +7 -6
- package/app/pages/daq/index.vue +2 -2
- package/app/pages/dcw/[id].vue +3 -3
- package/app/pages/workshop/agents.vue +432 -382
- package/bin/aw.mjs +0 -0
- package/cli/aw.mjs +8 -1
- package/cli/commands/stop.mjs +7 -4
- package/cli/commands/tui.mjs +10 -1
- package/cli/core/registry.mjs +5 -3
- package/config.yml +134 -134
- package/docs/tui.md +1 -1
- package/i18n/locales/en.ts +5 -0
- package/i18n/locales/zh-CN.ts +5 -0
- package/package.json +148 -140
- package/scripts/_dbg-catalog-smoke.ts +20 -0
- package/scripts/_dbg-codex-probe.mjs +58 -0
- package/scripts/_dbg-dsh-probe.mjs +31 -0
- package/scripts/_dbg-interval-scenario.mjs +126 -0
- package/scripts/_dbg-line-spawn.ts +31 -0
- package/scripts/_dbg-live-engine-probe.mjs +80 -0
- package/scripts/_dbg-mcp-bridge-probe.mjs +147 -0
- package/scripts/_dbg-opencode-sse.mjs +101 -0
- package/scripts/_dbg-opencode-start.ts +57 -0
- package/scripts/_dbg-plugin-matrix.mjs +212 -0
- package/scripts/_dbg-protocol-matrix.mjs +425 -0
- package/scripts/_dbg-real-scenario-timeline.log +123 -0
- package/scripts/_dbg-registry-smoke.ts +14 -0
- package/scripts/_dbg-sim-keeper.mjs +100 -0
- package/scripts/_dbg-team-drill.mjs +173 -0
- package/scripts/dev-guard.mjs +11 -3
- package/scripts/dev-protocol-simulators.mjs +28 -1
- package/scripts/e2e-multi-harness.ts +374 -0
- package/scripts/e2e-multiharness-team.mjs +319 -0
- package/scripts/e2e-plc-scenario.mjs +292 -0
- package/scripts/e2e-real-scenario.mjs +452 -0
- package/scripts/migrate-local-time.mjs +123 -0
- package/scripts/start.mjs +30 -2
- package/server/api/system/settings/index.get.ts +3 -3
- package/server/api/workshop/agent-tools/invoke.post.ts +27 -41
- package/server/api/workshop/agent-tools/list.get.ts +25 -0
- package/server/api/workshop/channels/[id]/index.patch.ts +38 -30
- package/server/api/workshop/daq/[id]/samples.get.ts +6 -3
- package/server/api/workshop/harnesses/[id]/providers.get.ts +16 -0
- package/server/api/workshop/harnesses.get.ts +12 -0
- package/server/api/workshop/hitl/respond.post.ts +87 -67
- package/server/api/workshop/ws.ts +13 -0
- package/server/data/daqs.json +1366 -16
- package/server/data/dcw-lines.json +266 -0
- package/server/data/dcw-products.json +203 -0
- package/server/data/dcw-recipes.json +784 -0
- package/server/data/dcw-rollback.json +1144 -1
- package/server/data/dcw-runs.json +983 -791
- package/server/data/dcw-writes.json +1080 -0
- package/server/data/dcws.json +3278 -203
- package/server/data/line-runs.json +52 -2
- package/server/harness/aw-mcp-bridge.mjs +153 -0
- package/server/plugins/00-local-time.ts +10 -0
- package/server/plugins/aw-plugins.ts +3 -0
- package/server/plugins/backup.ts +116 -97
- package/server/plugins/dev-stability-guard.ts +24 -15
- package/server/plugins/retention.ts +1 -0
- package/server/plugins/security.ts +24 -3
- package/server/plugins/workshop.ts +156 -153
- package/server/repositories/user.repository.ts +414 -391
- package/server/services/system-config.ts +30 -6
- package/server/services/workshop/.mimosa/hook-state/sess_fed429fb-c8b7-4a50-bbd6-45afc580893f.json +1 -0
- package/server/services/workshop/.mimosa/hook-status/sess_fed429fb-c8b7-4a50-bbd6-45afc580893f-e6b75bbfe6.json +14 -0
- package/server/services/workshop/agents/adapters/line-spawn.ts +108 -0
- package/server/services/workshop/agents/adapters/stdio-jsonrpc.ts +316 -0
- package/server/services/workshop/agents/agent-interface.ts +17 -0
- package/server/services/workshop/agents/codex-agent.ts +822 -0
- package/server/services/workshop/agents/dsh-agent.ts +670 -0
- package/server/services/workshop/agents/factory.ts +4 -16
- package/server/services/workshop/agents/harness-env.ts +54 -0
- package/server/services/workshop/agents/harness-models.ts +270 -0
- package/server/services/workshop/agents/host-tool-bridge.ts +592 -0
- package/server/services/workshop/agents/industrial-tools.ts +5 -1
- package/server/services/workshop/agents/node-bindings.repo.ts +4 -5
- package/server/services/workshop/agents/omp-agent.ts +1178 -1957
- package/server/services/workshop/agents/opencode-agent.ts +987 -0
- package/server/services/workshop/agents/prompt-builder.ts +393 -0
- package/server/services/workshop/agents/registry.ts +125 -0
- package/server/services/workshop/assets/character-asset.repo.ts +3 -5
- package/server/services/workshop/assets/device-twin.repo.ts +3 -5
- package/server/services/workshop/daq/daq-controller.ts +8 -4
- package/server/services/workshop/daq/daq-node.repo.ts +3 -5
- package/server/services/workshop/daq/daq-node.ts +2 -2
- package/server/services/workshop/daq/daq-templates.ts +3 -5
- package/server/services/workshop/daq/drivers.ts +41 -12
- package/server/services/workshop/daq/storage/sqlite.adapter.ts +313 -309
- package/server/services/workshop/db/backup-registry.ts +33 -0
- package/server/services/workshop/db/channel.repo.ts +104 -101
- package/server/services/workshop/db/database.ts +956 -949
- package/server/services/workshop/dcw/dcw-controller.ts +3 -1
- package/server/services/workshop/dcw/dcw-line.repo.ts +4 -11
- package/server/services/workshop/dcw/dcw-node.repo.ts +3 -5
- package/server/services/workshop/dcw/dcw-node.ts +13 -1
- package/server/services/workshop/dcw/dcw-product.repo.ts +3 -4
- package/server/services/workshop/dcw/dcw-recipe.repo.ts +3 -19
- package/server/services/workshop/dcw/dcw-templates.ts +3 -5
- package/server/services/workshop/dcw/drivers.ts +37 -13
- package/server/services/workshop/dcw/line-run.ts +3 -5
- package/server/services/workshop/dcw/recipe-rollback.repo.ts +4 -10
- package/server/services/workshop/json-store.mjs +72 -0
- package/server/services/workshop/plugins/host.mjs +2 -1
- package/server/services/workshop/runtime/agent-runtime.ts +44 -0
- package/server/services/workshop/runtime/manager.ts +2953 -2868
- package/server/services/workshop/scene/scene-layout.repo.ts +4 -11
- package/server/services/workshop/settings.ts +7 -0
- package/shared/config/schema.json +5 -0
- package/shared/config/single-instance.mjs +5 -1
- package/shared/local-time.mjs +36 -0
- package/shared/workshop-protocol.ts +11 -5
- package/tui/aw-tui.mjs +28 -10
- package/tui/components/hitl-card.mjs +47 -36
- package/tui/lib/ws.mjs +2 -1
- package/.output/public/_nuxt/9X0fVBD7.js +0 -57
- package/.output/public/_nuxt/B6M2WJWm2.js +0 -1
- package/.output/public/_nuxt/B9Yie5HN.js +0 -2
- package/.output/public/_nuxt/BF9XPEuw.js +0 -1
- package/.output/public/_nuxt/BbQornXa.js +0 -1
- package/.output/public/_nuxt/Bxgaym6j.js +0 -1
- package/.output/public/_nuxt/Bxh5nf8j.js +0 -1
- package/.output/public/_nuxt/CXLI50Sq.js +0 -1
- package/.output/public/_nuxt/CdfAP_IH2.js +0 -1
- package/.output/public/_nuxt/Ce1zyNYu.js +0 -1
- package/.output/public/_nuxt/D-Ne4C9a.js +0 -1
- package/.output/public/_nuxt/DEHNjlTq2.js +0 -1
- package/.output/public/_nuxt/DFnGFEav.js +0 -18
- package/.output/public/_nuxt/DYWgGHNo.js +0 -7
- package/.output/public/_nuxt/OmpTerminalPanel.Bc9GjNU5.css +0 -1
- package/.output/public/_nuxt/W1E3-5ML.js +0 -14
- package/.output/public/_nuxt/XVB9_zwP.js +0 -1
- package/.output/public/_nuxt/Xaf_HIR3.js +0 -1
- package/.output/public/_nuxt/_id_.BeqSLvl_.css +0 -1
- package/.output/public/_nuxt/_id_.DN3U6EF9.css +0 -1
- package/.output/public/_nuxt/agents.BJwgTLFZ.css +0 -1
- package/.output/public/_nuxt/builds/meta/55c10854-ee0e-4be4-924b-a1b0e100fe11.json +0 -1
- package/.output/public/_nuxt/daq.BXsnevQv.css +0 -1
- package/.output/public/_nuxt/yIKY_V1P.js +0 -1
- package/.output/server/chunks/build/OmpTerminalPanel-B_SYhmgb.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-CWLFGZjV.mjs.map +0 -1
- package/.output/server/chunks/build/agents-D4l1icvn.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-DqjtC-R0.mjs.map +0 -1
- package/.output/server/chunks/build/interval-WlxgjTzO.mjs +0 -9
- package/.output/server/chunks/build/interval-WlxgjTzO.mjs.map +0 -1
- package/.output/server/chunks/build/teams-QR3BBF7A.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkshopApi-Bh5VUHWu.mjs.map +0 -1
|
@@ -1,1957 +1,1178 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OmpRpcAgentImpl — omp harness 的完整 AgentInterface 实现。
|
|
3
|
-
*
|
|
4
|
-
* 通过 OmpRpcClient 驱动真实 omp 子进程(--mode rpc),将 omp 的原生 agent 能力
|
|
5
|
-
* (LLM 推理 + 内置工具 read/write/edit/bash/grep/glob 等)接入 Workshop 的
|
|
6
|
-
* Channel 编排体系。
|
|
7
|
-
*
|
|
8
|
-
* 核心设计:
|
|
9
|
-
* - 每个 Agent 实例持有一个 omp 子进程(lazy spawn,跨消息复用)
|
|
10
|
-
* - AgentWorkspace
|
|
11
|
-
* complete_task / dispatch_task / send_message 等,无需文本解析
|
|
12
|
-
* - omp AgentSessionEvent → AgentEvent 五变体映射
|
|
13
|
-
* - worker run():接收 assign → prompt omp → agent 用原生工具作业 + host tools 管理任务生命周期
|
|
14
|
-
* - lead supervise():格式化快照 → prompt omp → agent 直接用 host tools 执行调度决策 → 返回 []
|
|
15
|
-
* - dispose():杀子进程
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
import type {
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
type
|
|
36
|
-
type
|
|
37
|
-
type HostToolCallRequest,
|
|
38
|
-
} from './adapters/omp-rpc-client'
|
|
39
|
-
import {
|
|
40
|
-
registerHarnessProcess,
|
|
41
|
-
bindHarnessProcess,
|
|
42
|
-
markHarnessProcessExit,
|
|
43
|
-
killHarnessProcess,
|
|
44
|
-
} from './harness-process'
|
|
45
|
-
import {
|
|
46
|
-
attachTerminalTap,
|
|
47
|
-
markTerminalSessionExit,
|
|
48
|
-
} from './harness-terminal'
|
|
49
|
-
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
'
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
this.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
this.
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
//
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
//
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
if (result?.summary) {
|
|
1180
|
-
void this.harvestCompaction(
|
|
1181
|
-
result.summary,
|
|
1182
|
-
result.tokensBefore,
|
|
1183
|
-
result.tokensAfter ?? result.estimatedTokensAfter,
|
|
1184
|
-
(event as { reason?: string }).reason,
|
|
1185
|
-
)
|
|
1186
|
-
}
|
|
1187
|
-
return []
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// 回合完全落定(无自动重试/压缩重试/排队续跑):后续压缩检查由 AgentRuntime 驱动
|
|
1191
|
-
case 'agent_settled':
|
|
1192
|
-
return []
|
|
1193
|
-
|
|
1194
|
-
case '__error__':
|
|
1195
|
-
return [{
|
|
1196
|
-
kind: 'error',
|
|
1197
|
-
error: { code: 'OMP_ERROR', message: (event as { error?: string }).error ?? 'omp 未知错误' },
|
|
1198
|
-
}]
|
|
1199
|
-
|
|
1200
|
-
default:
|
|
1201
|
-
return []
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
// ===== 内部:prompt 构建 =====
|
|
1206
|
-
|
|
1207
|
-
private async buildWorkerPrompt(taskId: string, taskText: string, memory?: string): Promise<string> {
|
|
1208
|
-
const parts: string[] = []
|
|
1209
|
-
|
|
1210
|
-
const ctx = this.contextPrefix(await this.teamRoster())
|
|
1211
|
-
if (ctx) parts.push(ctx)
|
|
1212
|
-
if (memory) parts.push(memory)
|
|
1213
|
-
parts.push(this.systemManual())
|
|
1214
|
-
parts.push(renderPrompt('worker-workflow', {
|
|
1215
|
-
agentName: this.agentName,
|
|
1216
|
-
channelId: this.channelId,
|
|
1217
|
-
taskId,
|
|
1218
|
-
taskText,
|
|
1219
|
-
}))
|
|
1220
|
-
|
|
1221
|
-
return parts.join('\n')
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
/**
|
|
1225
|
-
* 成员能力画像(koda 运营信号借鉴):按任务历史计算
|
|
1226
|
-
* total/completed/failed/successRate/avgDurationMs(仅统计已终态任务)。
|
|
1227
|
-
* 数据源即 supervise 快照内的任务列表——零额外查询,证据随历史自然累积。
|
|
1228
|
-
*/
|
|
1229
|
-
private capabilityProfiles(snapshot: SupervisionSnapshot): Map<string, { total: number, completed: number, failed: number, successRate: number, avgDurationMs: number }> {
|
|
1230
|
-
const byAgent = new Map<string, { total: number, completed: number, failed: number, durationSum: number }>()
|
|
1231
|
-
for (const t of snapshot.tasks) {
|
|
1232
|
-
if (!['COMPLETED', 'FAILED'].includes(t.state)) continue
|
|
1233
|
-
const agg = byAgent.get(t.assigneeId) ?? { total: 0, completed: 0, failed: 0, durationSum: 0 }
|
|
1234
|
-
agg.total += 1
|
|
1235
|
-
if (t.state === 'COMPLETED') {
|
|
1236
|
-
agg.completed += 1
|
|
1237
|
-
agg.durationSum += Math.max(0, new Date(t.updatedAt).getTime() - new Date(t.createdAt).getTime())
|
|
1238
|
-
}
|
|
1239
|
-
else {
|
|
1240
|
-
agg.failed += 1
|
|
1241
|
-
}
|
|
1242
|
-
byAgent.set(t.assigneeId, agg)
|
|
1243
|
-
}
|
|
1244
|
-
const out = new Map<string, { total: number, completed: number, failed: number, successRate: number, avgDurationMs: number }>()
|
|
1245
|
-
for (const [agentId, agg] of byAgent) {
|
|
1246
|
-
out.set(agentId, {
|
|
1247
|
-
total: agg.total,
|
|
1248
|
-
completed: agg.completed,
|
|
1249
|
-
failed: agg.failed,
|
|
1250
|
-
successRate: agg.total > 0 ? agg.completed / agg.total : 0,
|
|
1251
|
-
avgDurationMs: agg.completed > 0 ? agg.durationSum / agg.completed : 0,
|
|
1252
|
-
})
|
|
1253
|
-
}
|
|
1254
|
-
return out
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
private async buildSupervisePrompt(snapshot: SupervisionSnapshot, memory?: string): Promise<string> {
|
|
1258
|
-
const parts: string[] = []
|
|
1259
|
-
|
|
1260
|
-
const ctx = this.contextPrefix(await this.teamRoster())
|
|
1261
|
-
if (ctx) parts.push(ctx)
|
|
1262
|
-
if (memory) parts.push(memory)
|
|
1263
|
-
parts.push(this.systemManual())
|
|
1264
|
-
// 格式化成员(含队列上下文 + 实时进度 + 停滞标记 + 能力画像:koda 运营信号借鉴 —
|
|
1265
|
-
// 按任务历史算成功率/平均耗时/失败数,lead 按证据而非直觉选人)。
|
|
1266
|
-
// 进度透出(heads-up):lead 看到每个 worker 正在执行的进度 %;stalled 标记
|
|
1267
|
-
// 直接提示"忙碌但进展停滞 → 应介入",从根上杜绝"worker 在跑但 lead 不知道"。
|
|
1268
|
-
const capability = this.capabilityProfiles(snapshot)
|
|
1269
|
-
const members = snapshot.members.map((m) => {
|
|
1270
|
-
const cap = capability.get(m.agentId)
|
|
1271
|
-
const capLine = cap && cap.total > 0
|
|
1272
|
-
? `, 成功率=${Math.round(cap.successRate * 100)}%, 均耗时=${Math.round(cap.avgDurationMs / 1000)}s, 失败=${cap.failed}`
|
|
1273
|
-
: ', 暂无历史'
|
|
1274
|
-
const prog = m.currentTaskProgress != null
|
|
1275
|
-
? `, progress=${m.currentTaskProgress}%${m.stalled ? ' [STALLED 停滞,请介入:notify/reassign/cancel]' : ''}`
|
|
1276
|
-
: ''
|
|
1277
|
-
const execTitle = m.currentTaskTitle ? `「${m.currentTaskTitle}」` : ''
|
|
1278
|
-
return ` - ${m.agentId} (${m.name}, role=${m.role}, state=${m.state}, executing=${m.currentTaskId ?? '-'}${execTitle}, queued=${m.queued ?? 0}, completed=${m.completedCount ?? 0}${prog}${capLine})`
|
|
1279
|
-
}).join('\n')
|
|
1280
|
-
|
|
1281
|
-
// 格式化任务(createdAt ASC = FIFO 顺序);COMPLETED 附交付预览(lead 直接看到 worker 成果)
|
|
1282
|
-
const tasks = snapshot.tasks.map((t) => {
|
|
1283
|
-
const deliverable = t.state === 'COMPLETED' && t.artifacts.length > 0
|
|
1284
|
-
? ` — 交付:${t.artifacts.map(a => a.parts.map(p => 'text' in p ? p.text : '').join(' ').replace(/\s+/g, ' ').trim().slice(0, 200)).filter(Boolean).join(' / ').slice(0, 400) || '(空)'}`
|
|
1285
|
-
: ''
|
|
1286
|
-
const artifacts = t.artifacts.length > 0 ? `, artifacts=${t.artifacts.length}` : ''
|
|
1287
|
-
return ` - ${t.id} [${t.state}] "${t.title}" (assignee=${t.assigneeId}, progress=${t.progress}%${artifacts})${deliverable}`
|
|
1288
|
-
}).join('\n')
|
|
1289
|
-
|
|
1290
|
-
// 未完成子任务数
|
|
1291
|
-
const pending = Object.entries(snapshot.pendingChildren)
|
|
1292
|
-
.map(([parentId, count]) => ` ${parentId}: ${count} pending`)
|
|
1293
|
-
.join('\n')
|
|
1294
|
-
|
|
1295
|
-
// 最近邮件(最新在前):worker 间点对点通信/回执 —— 判断"结果是否已被产出"的依据
|
|
1296
|
-
const mail = (snapshot.mail ?? []).map((m) => {
|
|
1297
|
-
const from = m.fromAgentId ?? '(system)'
|
|
1298
|
-
const to = m.toAgentId ?? '(broadcast)'
|
|
1299
|
-
const body = m.parts.map(p => 'text' in p ? p.text : JSON.stringify('data' in p ? p.data : p)).join(' ').trim().slice(0, 140)
|
|
1300
|
-
const label = m.metadata?.['x-aw-task-kind'] === 'assign'
|
|
1301
|
-
? 'task-assign'
|
|
1302
|
-
: m.metadata?.['x-aw-msg-priority'] === 'immediate' ? 'immediate' : 'peer'
|
|
1303
|
-
return ` - [${m.createdAt.slice(11, 19)}] ${from} → ${to} (${label}): ${body || '(empty)'}`
|
|
1304
|
-
}).join('\n')
|
|
1305
|
-
|
|
1306
|
-
// 检测执行模式(从 lead 自己的任务 description 前缀)
|
|
1307
|
-
const leadTask = snapshot.tasks.find(t =>
|
|
1308
|
-
t.assigneeId === snapshot.members.find(m => m.role === 'lead')?.agentId
|
|
1309
|
-
&& (t.state === 'SUBMITTED' || t.state === 'WORKING' || t.state === 'WAITING'),
|
|
1310
|
-
)
|
|
1311
|
-
const modeInfo = leadTask ? this.detectMode(leadTask.description ?? '') : null
|
|
1312
|
-
|
|
1313
|
-
parts.push(
|
|
1314
|
-
`You are "${this.agentName}", the lead coordinator of a multi-agent team (Channel: ${this.channelId}).`,
|
|
1315
|
-
`Tick #${snapshot.tick}`,
|
|
1316
|
-
``,
|
|
1317
|
-
`## Team Members (state + task queues)`,
|
|
1318
|
-
members || ' (none)',
|
|
1319
|
-
``,
|
|
1320
|
-
`## All Tasks (FIFO order)`,
|
|
1321
|
-
tasks || ' (none)',
|
|
1322
|
-
``,
|
|
1323
|
-
`## Pending Children Count`,
|
|
1324
|
-
pending || ' (none)',
|
|
1325
|
-
``,
|
|
1326
|
-
`## Recent Team Mail (newest first)`,
|
|
1327
|
-
mail || ' (none)',
|
|
1328
|
-
)
|
|
1329
|
-
|
|
1330
|
-
// 模式特定指令(外置 mode-*.md)/ 默认协调指令(外置 lead-supervise.md)
|
|
1331
|
-
if (modeInfo) {
|
|
1332
|
-
parts.push('', ...this.buildModeInstructions(modeInfo))
|
|
1333
|
-
}
|
|
1334
|
-
else {
|
|
1335
|
-
parts.push('', renderPrompt('lead-supervise'))
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
return parts.join('\n')
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
/** 从 description 前缀检测执行模式 */
|
|
1342
|
-
private detectMode(desc: string): { mode: string, criteria?: string, stages?: string[], interval?: number } | null {
|
|
1343
|
-
const match = desc.match(/^\[mode:(goal|loop|pipeline)\]/)
|
|
1344
|
-
if (!match) return null
|
|
1345
|
-
const mode = match[1]!
|
|
1346
|
-
const result: { mode: string, criteria?: string, stages?: string[], interval?: number } = { mode }
|
|
1347
|
-
if (mode === 'goal') {
|
|
1348
|
-
const crit = desc.match(/\[criteria:([^\]]+)\]/)
|
|
1349
|
-
if (crit) result.criteria = crit[1]
|
|
1350
|
-
}
|
|
1351
|
-
if (mode === 'loop') {
|
|
1352
|
-
const intv = desc.match(/\[interval:(\d+)\]/)
|
|
1353
|
-
if (intv) result.interval = parseInt(intv[1]!, 10)
|
|
1354
|
-
}
|
|
1355
|
-
if (mode === 'pipeline') {
|
|
1356
|
-
const stg = desc.match(/\[stages:([^\]]+)\]/)
|
|
1357
|
-
if (stg) result.stages = stg[1]!.split('->')
|
|
1358
|
-
}
|
|
1359
|
-
return result
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
/** 构建模式特定指令(外置 mode-goal/loop/pipeline.md) */
|
|
1363
|
-
private buildModeInstructions(modeInfo: { mode: string, criteria?: string, stages?: string[], interval?: number }): string[] {
|
|
1364
|
-
if (modeInfo.mode === 'goal') {
|
|
1365
|
-
return [renderPrompt('mode-goal', { criteria: modeInfo.criteria ?? '任务描述中的需求已全部完成' })]
|
|
1366
|
-
}
|
|
1367
|
-
if (modeInfo.mode === 'loop') {
|
|
1368
|
-
return [renderPrompt('mode-loop', { interval: (modeInfo.interval ?? 60000) / 1000 })]
|
|
1369
|
-
}
|
|
1370
|
-
if (modeInfo.mode === 'pipeline') {
|
|
1371
|
-
const stageList = modeInfo.stages?.length
|
|
1372
|
-
? modeInfo.stages.map((s, i) => ` Stage ${i + 1}: ${s}`).join('\n')
|
|
1373
|
-
: ' Decompose the task into sequential stages yourself.'
|
|
1374
|
-
return [renderPrompt('mode-pipeline', { stageList })]
|
|
1375
|
-
}
|
|
1376
|
-
return []
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
// ===== 内部:omp 客户端管理 =====
|
|
1380
|
-
|
|
1381
|
-
private async ensureClient(ctx: AgentRunContext): Promise<void> {
|
|
1382
|
-
if (!this.workspace) {
|
|
1383
|
-
this.workspace = ctx.workspace
|
|
1384
|
-
}
|
|
1385
|
-
if (!this.agentInfo) {
|
|
1386
|
-
// init() 可能未调用;从 ctx 推断
|
|
1387
|
-
this.channelId = ctx.channelId
|
|
1388
|
-
this.agentRole = ctx.role
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
if (!this.client || !this.client.alive) {
|
|
1392
|
-
// 旧客户端已退出(exit 事件/OS 存活校准 reconcile 触发)——必须丢弃并重生,
|
|
1393
|
-
// 否则后续回合会一直对着死 stdio 报 PROMPT_FAILED 烧重试配额
|
|
1394
|
-
this.client = null
|
|
1395
|
-
this.hostToolsRegistered = false
|
|
1396
|
-
const command = this.config.command ?? 'omp'
|
|
1397
|
-
const client = new OmpRpcClient({
|
|
1398
|
-
command,
|
|
1399
|
-
mode: this.rpcMode,
|
|
1400
|
-
args: this.config.args,
|
|
1401
|
-
cwd: this.config.cwd ?? process.cwd(),
|
|
1402
|
-
// 进程退出 → 注册表标记(供运行时资源监控);pid=-1 表示无法取得,忽略
|
|
1403
|
-
onExit: (pid, code) => {
|
|
1404
|
-
if (pid > 0) {
|
|
1405
|
-
markHarnessProcessExit(pid, code)
|
|
1406
|
-
markTerminalSessionExit(pid, code)
|
|
1407
|
-
}
|
|
1408
|
-
},
|
|
1409
|
-
})
|
|
1410
|
-
await client.start()
|
|
1411
|
-
|
|
1412
|
-
// 登记 + 绑定 agent 身份(harness 进程监控的事实源)
|
|
1413
|
-
const pid = client.pid
|
|
1414
|
-
if (pid) {
|
|
1415
|
-
registerHarnessProcess(pid, {
|
|
1416
|
-
harness: 'omp',
|
|
1417
|
-
command,
|
|
1418
|
-
args: ['--mode', this.rpcMode, ...(this.config.args ?? [])],
|
|
1419
|
-
})
|
|
1420
|
-
bindHarnessProcess(pid, {
|
|
1421
|
-
agentId: this.selfAgentId,
|
|
1422
|
-
channelId: this.channelId,
|
|
1423
|
-
name: this.agentName,
|
|
1424
|
-
role: this.agentRole,
|
|
1425
|
-
})
|
|
1426
|
-
// 终端镜像 tap:全部 RPC 帧 → /monitor 终端(实时 TUI 渲染 + HITL)
|
|
1427
|
-
attachTerminalTap(client, {
|
|
1428
|
-
pid,
|
|
1429
|
-
harness: 'omp',
|
|
1430
|
-
agentId: this.selfAgentId,
|
|
1431
|
-
channelId: this.channelId,
|
|
1432
|
-
name: this.agentName,
|
|
1433
|
-
role: this.agentRole,
|
|
1434
|
-
})
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
// 设置模型(如果配置了 provider/model 任一;omp 以当前已设值为缺省补全,
|
|
1438
|
-
// 只配其一时也显式发送,避免 respawn 后静默回落默认模型)
|
|
1439
|
-
if (this.config.provider || this.config.model) {
|
|
1440
|
-
try {
|
|
1441
|
-
// 未配置一侧保持 undefined(JSON 序列化丢键)→ omp 以当前已设值补全
|
|
1442
|
-
await client.send({
|
|
1443
|
-
type: 'set_model',
|
|
1444
|
-
provider: this.config.provider as string,
|
|
1445
|
-
modelId: this.config.model as string,
|
|
1446
|
-
})
|
|
1447
|
-
}
|
|
1448
|
-
catch {
|
|
1449
|
-
// 模型设置失败不致命(用 omp 默认模型)
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
// 注册 host tools(按角色差异化:lead 全量,worker 剔除调度/团队管理专属工具)
|
|
1454
|
-
client.onHostToolCall(req => this.handleHostTool(req))
|
|
1455
|
-
await client.send({ type: 'set_host_tools', tools: hostToolsForRole(this.agentRole) })
|
|
1456
|
-
|
|
1457
|
-
// 上下文治理探测(feature-detect 一次;失败退化被动 usage 跟踪)+ 原生压缩兜底保持开启
|
|
1458
|
-
void this.probeContext(client).catch(() => {})
|
|
1459
|
-
void client.send({ type: 'set_auto_compaction', enabled: true }).catch(() => {})
|
|
1460
|
-
|
|
1461
|
-
this.client = client
|
|
1462
|
-
this.hostToolsRegistered = true
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
/** 上下文状态探测:get_state 取 contextWindow/sessionId(一次;失败仅损失 percent 精度) */
|
|
1467
|
-
private async probeContext(client: OmpRpcClient): Promise<void> {
|
|
1468
|
-
try {
|
|
1469
|
-
const resp = await client.send({ type: 'get_state' })
|
|
1470
|
-
const data = (resp.data ?? {}) as Record<string, unknown>
|
|
1471
|
-
const model = data.model as { contextWindow?: number } | undefined
|
|
1472
|
-
if (typeof model?.contextWindow === 'number' && model.contextWindow > 0) {
|
|
1473
|
-
this.contextWindow = model.contextWindow
|
|
1474
|
-
client.setContextWindow(model.contextWindow)
|
|
1475
|
-
}
|
|
1476
|
-
if (typeof data.sessionId === 'string' && data.sessionId) this.sessionId = data.sessionId
|
|
1477
|
-
}
|
|
1478
|
-
catch { /* get_state 不可用:仅被动 usage */ }
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
// ===== 内部:host tool handler(workspace 桥接) =====
|
|
1482
|
-
|
|
1483
|
-
private async handleHostTool(req: HostToolCallRequest): Promise<{ text: string, isError?: boolean }> {
|
|
1484
|
-
// 插件工具分发(ctx.omp.registerTool;不依赖 workspace,优先于内置面;异常由外层 catch 收口)
|
|
1485
|
-
const pluginTool = listPluginTools().get(req.toolName)
|
|
1486
|
-
if (pluginTool) {
|
|
1487
|
-
return await pluginTool.handler(req.arguments ?? {}, {
|
|
1488
|
-
agentId: this.selfAgentId,
|
|
1489
|
-
channelId: this.channelId,
|
|
1490
|
-
role: this.agentRole,
|
|
1491
|
-
name: this.agentName,
|
|
1492
|
-
})
|
|
1493
|
-
}
|
|
1494
|
-
const ws = this.workspace
|
|
1495
|
-
if (!ws) {
|
|
1496
|
-
return { text: 'workspace 未就绪', isError: true }
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
try {
|
|
1500
|
-
switch (req.toolName) {
|
|
1501
|
-
case 'report_progress': {
|
|
1502
|
-
const progress = req.arguments.progress as number
|
|
1503
|
-
const message = req.arguments.message as string | undefined
|
|
1504
|
-
const taskId = this.currentTaskId
|
|
1505
|
-
if (!taskId) return { text: '无当前任务上下文', isError: true }
|
|
1506
|
-
await ws.reportTask({ taskId, progress, message })
|
|
1507
|
-
return { text: `进度已上报: ${progress}%${message ? ` (${message})` : ''}` }
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
case 'complete_task': {
|
|
1511
|
-
const summary = req.arguments.summary as string
|
|
1512
|
-
const deliverable = req.arguments.deliverable as string | undefined
|
|
1513
|
-
const taskId = (req.arguments.task_id as string | undefined) ?? this.currentTaskId
|
|
1514
|
-
if (!taskId) return { text: '无任务 ID', isError: true }
|
|
1515
|
-
// 父任务保护:有未完成子任务时拒绝完成(lead 须等 worker 交付)
|
|
1516
|
-
const allTasks = await ws.listTasks()
|
|
1517
|
-
const incompleteChildren = allTasks.filter(t => t.parentId === taskId && t.state !== 'COMPLETED' && t.state !== 'CANCELED')
|
|
1518
|
-
if (incompleteChildren.length > 0) {
|
|
1519
|
-
return {
|
|
1520
|
-
text: `任务 ${taskId} 有 ${incompleteChildren.length} 个未完成子任务,不能完成父任务。请等待子任务完成。`,
|
|
1521
|
-
isError: true,
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
// 终态幂等:任务已被平台收口(看门狗取消/调度器完成)时不撞状态机 ——
|
|
1525
|
-
// 给 Agent 明确的"无需再完成,继续下一项"信号,杜绝重复重试烧 token
|
|
1526
|
-
const current = await ws.getTask(taskId)
|
|
1527
|
-
if (current && current.state !== 'SUBMITTED' && current.state !== 'ASSIGNED' && current.state !== 'WORKING' && current.state !== 'WAITING') {
|
|
1528
|
-
if (current.state === 'COMPLETED') {
|
|
1529
|
-
return { text: `任务 ${taskId} 已是完成状态(可能已被平台收口),无需重复完成。` }
|
|
1530
|
-
}
|
|
1531
|
-
try {
|
|
1532
|
-
const q = await ws.myQueue()
|
|
1533
|
-
const next = q.queued[0]
|
|
1534
|
-
return {
|
|
1535
|
-
text: `任务 ${taskId} 已被平台${current.state === 'CANCELED' ? '取消(如停滞回收/上级作废)' : '判定失败'},不能再标记完成 —— 这不是你的错误,也无需重试。${next ? `队列还有 ${q.queued.length} 项,下一项「${next.title}」即将开始,请继续处理。` : '队列为空,保持待命。'}`,
|
|
1536
|
-
isError: false,
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
catch {
|
|
1540
|
-
return { text: `任务 ${taskId} 已被平台${current.state === 'CANCELED' ? '取消' : '判定失败'},无需再完成,请继续处理队列下一项。` }
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
const artifacts: A2AArtifact[] = []
|
|
1544
|
-
if (deliverable || summary) {
|
|
1545
|
-
artifacts.push({
|
|
1546
|
-
artifactId: randomUUID(),
|
|
1547
|
-
name: 'deliverable',
|
|
1548
|
-
parts: [{ text: deliverable ?? summary }],
|
|
1549
|
-
})
|
|
1550
|
-
}
|
|
1551
|
-
await ws.completeTask(taskId, artifacts)
|
|
1552
|
-
this.currentTaskId = null
|
|
1553
|
-
// 完成即衔接:报告队列余量与下一项(状态同步 + 驱动继续处理;
|
|
1554
|
-
// 消费循环随后会以新消息自动起回合,这里给 LLM 明确的继续信号)
|
|
1555
|
-
try {
|
|
1556
|
-
const q = await ws.myQueue()
|
|
1557
|
-
const next = q.queued[0]
|
|
1558
|
-
if (next) {
|
|
1559
|
-
return {
|
|
1560
|
-
text: `任务 ${taskId} 已完成(状态已同步为 COMPLETED)。你的队列还有 ${q.queued.length} 项待处理,下一项:「${next.title}」(即将自动开始;收到任务指派消息后按工作流执行,完成后同样调用 complete_task)。`,
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
return { text: `任务 ${taskId} 已完成(状态已同步为 COMPLETED),队列为空。保持待命:新任务/实时消息会自动到达你的信箱。` }
|
|
1564
|
-
}
|
|
1565
|
-
catch {
|
|
1566
|
-
return { text: `任务 ${taskId} 已完成(状态已同步为 COMPLETED)。` }
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
case 'dispatch_task': {
|
|
1571
|
-
const assigneeId = req.arguments.assignee_id as string
|
|
1572
|
-
const title = req.arguments.title as string
|
|
1573
|
-
const description = req.arguments.description as string | undefined
|
|
1574
|
-
const parentTaskId = req.arguments.parent_task_id as string | undefined
|
|
1575
|
-
const routeReason = req.arguments.route_reason as string | undefined
|
|
1576
|
-
const task = await ws.dispatchTask({ assigneeId, title, description, parentTaskId, routeReason })
|
|
1577
|
-
return { text: `子任务 ${task.id} 已创建并指派 → ${assigneeId}(父任务 ${parentTaskId ?? '无'},标题: ${title}${routeReason ? `,路由理由: ${routeReason}` : ''})` }
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
case 'send_message_to_agent': {
|
|
1581
|
-
const toAgentId = req.arguments.to_agent_id as string
|
|
1582
|
-
const message = req.arguments.message as string
|
|
1583
|
-
// 回执自动实时:回复(in_reply_to)默认提升为 immediate——
|
|
1584
|
-
// 接收方正等待该结果,realtime 路由会把回复直接注入其运行中的会话
|
|
1585
|
-
let priority = (req.arguments.priority as string | undefined) ?? 'task'
|
|
1586
|
-
const metadata: Record<string, unknown> = {}
|
|
1587
|
-
// 触发器:要求对方回复 / 标记本消息是对某消息的回复(回执关联)
|
|
1588
|
-
if (req.arguments.require_reply === true) metadata['x-aw-require-reply'] = 'true'
|
|
1589
|
-
let inReplyTo = req.arguments.in_reply_to as string | undefined
|
|
1590
|
-
// 自动关联兜底:LLM 省略 in_reply_to 时,按待回执上下文盖章
|
|
1591
|
-
// (触发消息要求回复 → 本次发送即回执;发给原发送者且无显式 in_reply_to)
|
|
1592
|
-
const replyCtx = this.takeReplyContext()
|
|
1593
|
-
if (!inReplyTo && replyCtx && replyCtx.fromId === toAgentId) {
|
|
1594
|
-
inReplyTo = replyCtx.messageId
|
|
1595
|
-
}
|
|
1596
|
-
if (inReplyTo) {
|
|
1597
|
-
metadata['x-aw-in-reply-to'] = inReplyTo
|
|
1598
|
-
if (priority === 'task') priority = 'immediate'
|
|
1599
|
-
}
|
|
1600
|
-
metadata['x-aw-msg-priority'] = priority
|
|
1601
|
-
const sent = await ws.sendMessage({ toAgentId, parts: [{ text: message }], metadata })
|
|
1602
|
-
const triggerNote = inReplyTo
|
|
1603
|
-
? `(回复 ${inReplyTo.slice(0, 8)}…,已实时推送给对方)`
|
|
1604
|
-
: metadata['x-aw-require-reply'] === 'true' ? '(已要求对方回复)' : ''
|
|
1605
|
-
return { text: `消息 ${sent.messageId.slice(0, 8)}… 已发送给 ${toAgentId}(priority=${priority})${triggerNote}` }
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
case 'send_cross_channel_message': {
|
|
1609
|
-
// 跨 Channel 通信(仅 lead):直投目标 channel 的 lead mailbox
|
|
1610
|
-
const toChannelId = req.arguments.to_channel_id as string
|
|
1611
|
-
const message = req.arguments.message as string
|
|
1612
|
-
if (!toChannelId || !message) return { text: '缺少 to_channel_id 或 message', isError: true }
|
|
1613
|
-
try {
|
|
1614
|
-
const r = await ws.sendCrossChannelMessage({
|
|
1615
|
-
toChannelId,
|
|
1616
|
-
parts: [{ text: message }],
|
|
1617
|
-
requireReply: req.arguments.require_reply === true,
|
|
1618
|
-
inReplyTo: req.arguments.in_reply_to as string | undefined,
|
|
1619
|
-
})
|
|
1620
|
-
const note = req.arguments.require_reply === true ? '(已要求对方 Leader 回复)' : ''
|
|
1621
|
-
return { text: `跨 Channel 消息 ${r.messageId.slice(0, 8)}… 已送达 channel「${r.toChannelName}」的 Leader(${r.toLeadAgentId.slice(0, 8)}…)${note}。对方将按你的信息需求处理;其回复会经 mailbox 到达你这里。` }
|
|
1622
|
-
}
|
|
1623
|
-
catch (err) {
|
|
1624
|
-
return { text: `跨 Channel 发送失败: ${err instanceof Error ? err.message : String(err)}(仅 Leader 可跨 Channel 通信)`, isError: true }
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
case 'list_other_teams': {
|
|
1629
|
-
// 跨团队观察面(仅 lead):其他团队的场景任务概览
|
|
1630
|
-
const teams = await ws.listOtherTeams()
|
|
1631
|
-
if (teams.length === 0) return { text: '当前没有其他团队(或均未启用)。' }
|
|
1632
|
-
const text = teams.map((t) => {
|
|
1633
|
-
const active = t.activeTasks.length > 0
|
|
1634
|
-
? t.activeTasks.map(x => `「${x.title}」(${x.state})`).join('、')
|
|
1635
|
-
: '无进行中任务'
|
|
1636
|
-
const done = t.recentCompleted.length > 0
|
|
1637
|
-
? t.recentCompleted.map(x => `「${x.title}」`).join('、')
|
|
1638
|
-
: '无'
|
|
1639
|
-
const mem = t.sharedMemories > 0 ? `共享记忆 ${t.sharedMemories} 条(可检索)` : '暂无共享记忆'
|
|
1640
|
-
return `- ${t.name}${t.description ? `(${t.description})` : ''} · lead=${t.leadName ?? '?'} · ${mem}\n 进行中: ${active}\n 近期完成: ${done}\n channel_id: ${t.channelId}`
|
|
1641
|
-
}).join('\n')
|
|
1642
|
-
return { text: `其他团队概览:\n${text}\n(需要协作时用 send_cross_channel_message 向对应团队 Leader 发信;查具体知识用 search_other_teams_memory)` }
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
case 'search_other_teams_memory': {
|
|
1646
|
-
// 跨团队共享记忆(全员只读):别的团队沉淀的结论/经验,先查再决定是否发信
|
|
1647
|
-
const query = (req.arguments.query as string | undefined) ?? ''
|
|
1648
|
-
if (!query) return { text: '缺少 query', isError: true }
|
|
1649
|
-
const rows = await ws.searchOtherTeamsMemory({ query, limit: Number(req.arguments.limit ?? 5) })
|
|
1650
|
-
if (rows.length === 0) return { text: `其他团队的共享记忆中没有命中「${query}」的内容。` }
|
|
1651
|
-
const text = rows.map(r =>
|
|
1652
|
-
`- [${r.channelName}] 「${r.title}」(${r.createdAt.slice(0, 10)}): ${r.content}`).join('\n')
|
|
1653
|
-
return { text: `其他团队共享记忆命中 ${rows.length} 条:\n${text}` }
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
case 'refuse_task': {
|
|
1657
|
-
const taskId = req.arguments.task_id as string
|
|
1658
|
-
const reason = req.arguments.reason as string
|
|
1659
|
-
if (!taskId || !reason) return { text: '缺少 task_id 或 reason', isError: true }
|
|
1660
|
-
const r = await ws.refuseTask(taskId, reason)
|
|
1661
|
-
const notified = r.notifiedTo ? `拒绝回执已送达 ${r.notifiedTo.slice(0, 8)}` : '无回执对象(创建者已不在 channel)'
|
|
1662
|
-
return {
|
|
1663
|
-
text: `任务 ${taskId.slice(0, 8)}("${r.task.title}") 已拒绝(state=${r.task.state});${notified}。调度器将改派他人,请勿再处理该任务。`,
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
case 'poll_messages': {
|
|
1668
|
-
const limit = (req.arguments.limit as number | undefined) ?? 10
|
|
1669
|
-
// 阻塞等待(真即时):Mailbox 到信回调毫秒级唤醒 + 250ms 兜底重查;
|
|
1670
|
-
// 对方消息一到立即返回 —— 取代旧每秒盲查(阻塞窗口内到信可能被
|
|
1671
|
-
// 实时注入层抢先标记消费,盲查扑空即"轮询查空"丢失)
|
|
1672
|
-
const waitSec = Math.min(180, Math.max(0, Number(req.arguments.wait_seconds ?? 0) || 0))
|
|
1673
|
-
const msgs = await ws.waitMailbox(limit, waitSec * 1000)
|
|
1674
|
-
if (msgs.length === 0) {
|
|
1675
|
-
return {
|
|
1676
|
-
text: waitSec > 0
|
|
1677
|
-
? `等待 ${waitSec}s 后收件箱仍为空。若此前已有"[实时消息 from X]"注入你的会话,那就是回复本身(无需再轮询);否则可继续处理其他工作,对方回复会以新回合送达。`
|
|
1678
|
-
: '收件箱为空(无未消费消息)。等待回复请用 wait_seconds 参数阻塞等待,不要反复空轮询。',
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
// 读即取:协作消息(非任务投递)取出即确认——防止消费循环稍后把同一消息再跑一遍回合;
|
|
1682
|
-
// 任务指派(assign)不确认,仍由执行循环处理
|
|
1683
|
-
const ackIds = msgs
|
|
1684
|
-
.filter(m => !m.metadata?.['x-aw-task-kind'])
|
|
1685
|
-
.map(m => m.messageId)
|
|
1686
|
-
if (ackIds.length > 0) await ws.ackMailbox(ackIds)
|
|
1687
|
-
// 触发上下文(轮询路径的回执自动关联):取到的 require-reply 协作消息 →
|
|
1688
|
-
// 后续 send_message_to_agent 回给发送者时自动盖 in_reply_to(与 peer 回合同源)
|
|
1689
|
-
const trigger = msgs.find(m =>
|
|
1690
|
-
m.metadata?.['x-aw-require-reply'] === 'true'
|
|
1691
|
-
&& typeof m.metadata?.['x-aw-from-agent'] === 'string')
|
|
1692
|
-
if (trigger) {
|
|
1693
|
-
this.replyContext = {
|
|
1694
|
-
fromId: String(trigger.metadata!['x-aw-from-agent']),
|
|
1695
|
-
messageId: trigger.messageId,
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
const text = msgs.map((m, i) => {
|
|
1699
|
-
const from = m.metadata?.['x-aw-from-agent'] ?? '?'
|
|
1700
|
-
const reply = m.metadata?.['x-aw-in-reply-to']
|
|
1701
|
-
? ` (回复 ${String(m.metadata['x-aw-in-reply-to']).slice(0, 8)}…)`
|
|
1702
|
-
: ''
|
|
1703
|
-
// 需回复的消息附带回执指令:对方在等你的结果,须回信并关联原消息
|
|
1704
|
-
const needReply = m.metadata?.['x-aw-require-reply'] === 'true'
|
|
1705
|
-
? ` [需回复:用 send_message_to_agent 回 ${from},in_reply_to=${m.messageId}]`
|
|
1706
|
-
: ''
|
|
1707
|
-
const body = m.parts.map(p => 'text' in p ? p.text : '').join(' ')
|
|
1708
|
-
// 不截到 100 字符:谜面/任务书等长消息截半句会让收件人误判内容不完整
|
|
1709
|
-
return ` [${i + 1}/${msgs.length}] [from ${from}]${needReply}${reply} ${body.slice(0, 2000)}`
|
|
1710
|
-
}).join('\n')
|
|
1711
|
-
return {
|
|
1712
|
-
text:
|
|
1713
|
-
`未消费消息(${msgs.length},已读即取):\n${text}`
|
|
1714
|
-
+ (msgs.length > 1
|
|
1715
|
-
? '\n(收到多条:请按编号逐条处理并逐条回复,不要只回应最后一条或合并敷衍)'
|
|
1716
|
-
: ''),
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
case 'read_channel_mail': {
|
|
1721
|
-
const limit = (req.arguments.limit as number | undefined) ?? 50
|
|
1722
|
-
const agentId = req.arguments.agent_id as string | undefined
|
|
1723
|
-
const mails = await ws.listMail({ limit, agentId })
|
|
1724
|
-
if (mails.length === 0) return { text: 'Channel 无邮件记录(或该成员无往来)' }
|
|
1725
|
-
const text = mails.map((m) => {
|
|
1726
|
-
const from = m.fromAgentId ?? '(系统)'
|
|
1727
|
-
const to = m.toAgentId ?? '(广播)'
|
|
1728
|
-
const body = partsToText(m.parts).trim().slice(0, 2000)
|
|
1729
|
-
const reply = m.metadata?.['x-aw-in-reply-to']
|
|
1730
|
-
? ` [回复 ${String(m.metadata['x-aw-in-reply-to']).slice(0, 8)}…]`
|
|
1731
|
-
: ''
|
|
1732
|
-
const label = m.metadata?.['x-aw-task-kind'] === 'assign'
|
|
1733
|
-
? '[任务指派]'
|
|
1734
|
-
: m.metadata?.['x-aw-msg-priority'] === 'immediate' ? '[实时]' : '[协作]'
|
|
1735
|
-
const state = m.state === 'pending' ? '未读' : m.state === 'consuming' ? '处理中' : '已读'
|
|
1736
|
-
return ` ${m.createdAt.slice(11, 19)} ${label} ${from} → ${to} (${state})${reply}: ${body || '(空)'}`
|
|
1737
|
-
}).join('\n')
|
|
1738
|
-
return { text: `Channel 邮件(${mails.length},倒序;可传 agent_id 查看指定成员信箱):\n${text}` }
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
case 'broadcast_message': {
|
|
1742
|
-
const message = req.arguments.message as string
|
|
1743
|
-
const priority = (req.arguments.priority as string | undefined) ?? 'task'
|
|
1744
|
-
const agents = await ws.listAgents()
|
|
1745
|
-
const others = agents.filter(a => a.id !== this.selfAgentId)
|
|
1746
|
-
for (const agent of others) {
|
|
1747
|
-
await ws.sendMessage({
|
|
1748
|
-
toAgentId: agent.id,
|
|
1749
|
-
parts: [{ text: message }],
|
|
1750
|
-
metadata: { 'x-aw-msg-priority': priority },
|
|
1751
|
-
})
|
|
1752
|
-
}
|
|
1753
|
-
return { text: `已广播给 ${others.length} 个 agent(priority=${priority})` }
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
case 'list_channel_tasks': {
|
|
1757
|
-
const tasks = await ws.listTasks()
|
|
1758
|
-
const text = tasks.map(t =>
|
|
1759
|
-
` ${t.id} [${t.state}] "${t.title}" assignee=${t.assigneeId} progress=${t.progress}%`,
|
|
1760
|
-
).join('\n')
|
|
1761
|
-
return { text: `Channel 任务(${tasks.length}):\n${text || '(空)'}` }
|
|
1762
|
-
}
|
|
1763
|
-
|
|
1764
|
-
case 'get_my_task_queue': {
|
|
1765
|
-
const queue = await ws.myQueue()
|
|
1766
|
-
const fmt = (t: WorkspaceTask): string =>
|
|
1767
|
-
` ${t.id} [${t.state}] "${t.title}" progress=${t.progress}%`
|
|
1768
|
-
return {
|
|
1769
|
-
text: [
|
|
1770
|
-
`我的任务队列(${this.agentRole}):`,
|
|
1771
|
-
`执行中: ${queue.current ? `${queue.current.id} "${queue.current.title}" (${queue.current.progress}%)` : '(无)'}`,
|
|
1772
|
-
`待执行(${queue.queued.length},FIFO):`,
|
|
1773
|
-
...queue.queued.map(fmt),
|
|
1774
|
-
`已完成(${queue.completed.length}):`,
|
|
1775
|
-
...queue.completed.map(fmt),
|
|
1776
|
-
].join('\n'),
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
case 'get_queue_overview': {
|
|
1781
|
-
const overview = await ws.queueOverview()
|
|
1782
|
-
const lines = overview.map(s =>
|
|
1783
|
-
` ${s.agentId} (${s.name}, role=${s.role}, state=${s.state}, current=${s.currentTaskId ?? '-'}${s.currentTaskTitle ? `「${s.currentTaskTitle}」` : ''}, progress=${s.currentTaskProgress ?? '-'}%, queued=${s.queuedCount}, completed=${s.completedCount})`,
|
|
1784
|
-
)
|
|
1785
|
-
return { text: `团队队列总览(${overview.length}):\n${lines.join('\n') || '(空)'}` }
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
case 'reassign_task': {
|
|
1789
|
-
const taskId = req.arguments.task_id as string
|
|
1790
|
-
const toAgentId = req.arguments.to_agent_id as string
|
|
1791
|
-
const task = await ws.reassignTask(taskId, toAgentId)
|
|
1792
|
-
return { text: `任务 ${taskId}("${task.title}")已调配 → ${toAgentId}(state=${task.state})` }
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
case 'update_task': {
|
|
1796
|
-
const taskId = req.arguments.task_id as string
|
|
1797
|
-
const title = req.arguments.title as string | undefined
|
|
1798
|
-
const description = req.arguments.description as string | undefined
|
|
1799
|
-
const task = await ws.updateTask(taskId, { title, description })
|
|
1800
|
-
return { text: `任务 ${taskId} 已更新: "${task.title}"` }
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
case 'cancel_task': {
|
|
1804
|
-
const taskId = req.arguments.task_id as string
|
|
1805
|
-
// 守卫:goal/loop/pipeline 的 mode 父任务是用户的作业主任务,Agent 不得经工具取消
|
|
1806
|
-
// (模型偶发误判会毁掉整个目标;终止主任务只能由用户在界面/REST 操作)。
|
|
1807
|
-
// 目标未达成时的正确动作:dispatch_task 补派子任务;确认无法达成:complete_task 附结论说明。
|
|
1808
|
-
const target = await ws.getTask(taskId).catch(() => null)
|
|
1809
|
-
if (target && extractTaskMode(target)) {
|
|
1810
|
-
return {
|
|
1811
|
-
text: `拒绝:任务 ${taskId} 是 mode 父任务(${target.title}),不能用 cancel_task 取消。若目标未达成 → dispatch_task 派发子任务补齐差距;若确认无法达成 → complete_task 并在交付中说明未达成原因。终止整个作业请由用户操作。`,
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
await ws.cancelTask(taskId)
|
|
1815
|
-
return { text: `任务 ${taskId} 已取消并移出 assignee 队列` }
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
case 'list_team_agents': {
|
|
1819
|
-
const agents = await ws.listAgents()
|
|
1820
|
-
const text = agents.map(a =>
|
|
1821
|
-
` ${a.id} (${a.name}, role=${a.role}, harness=${a.harness})`,
|
|
1822
|
-
).join('\n')
|
|
1823
|
-
return { text: `团队成员(${agents.length}):\n${text || '(空)'}` }
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
case 'get_task_details': {
|
|
1827
|
-
const taskId = req.arguments.task_id as string
|
|
1828
|
-
const task: WorkspaceTask = await ws.getTask(taskId)
|
|
1829
|
-
const artifactText = task.artifacts.map(a =>
|
|
1830
|
-
` artifact ${a.artifactId}: ${a.parts.map(p => 'text' in p ? p.text.slice(0, 100) : '').join('; ')}`,
|
|
1831
|
-
).join('\n')
|
|
1832
|
-
return {
|
|
1833
|
-
text: `任务 ${task.id}\n 状态: ${task.state}\n 标题: ${task.title}\n 描述: ${task.description ?? '-'}\n 指派: ${task.assigneeId}\n 进度: ${task.progress}%\n 成果:\n${artifactText || ' (无)'}`,
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
case 'search_memory': {
|
|
1838
|
-
const query = req.arguments.query as string
|
|
1839
|
-
const scope = (req.arguments.scope as 'auto' | 'private' | 'shared' | undefined) ?? 'auto'
|
|
1840
|
-
const snippets = await ws.recallMemory({ query, scope, limit: req.arguments.limit as number | undefined })
|
|
1841
|
-
if (snippets.length === 0) {
|
|
1842
|
-
return { text: `记忆检索无命中(query="${query}", scope=${scope})。可尝试更换关键词或放宽 scope。` }
|
|
1843
|
-
}
|
|
1844
|
-
const lines = snippets.map(s =>
|
|
1845
|
-
` [${s.source}·${s.kind}·score=${s.score}] ${s.title}\n ${s.content}`,
|
|
1846
|
-
)
|
|
1847
|
-
return { text: `记忆检索结果(${snippets.length} 条, scope=${scope}):\n${lines.join('\n')}` }
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
case 'save_memory': {
|
|
1851
|
-
const title = req.arguments.title as string
|
|
1852
|
-
const content = req.arguments.content as string
|
|
1853
|
-
const scope = req.arguments.scope as 'private' | 'shared'
|
|
1854
|
-
const saved = await ws.saveMemory({
|
|
1855
|
-
title,
|
|
1856
|
-
content,
|
|
1857
|
-
importance: req.arguments.importance as number | undefined,
|
|
1858
|
-
scope,
|
|
1859
|
-
dedupKey: req.arguments.dedup_key as string | undefined,
|
|
1860
|
-
})
|
|
1861
|
-
const where = scope === 'shared' ? 'Channel 公共记忆(全员可检索)' : '本人私有记忆'
|
|
1862
|
-
return { text: `已沉淀到${where}: "${title}"(dedupKey=${saved.dedupKey})` }
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
case 'create_team_agent': {
|
|
1866
|
-
const name = req.arguments.name as string
|
|
1867
|
-
const harness = req.arguments.harness as string | undefined
|
|
1868
|
-
const systemPrompt = req.arguments.system_prompt as string | undefined
|
|
1869
|
-
const reason = req.arguments.reason as string | undefined
|
|
1870
|
-
const agent = await ws.createTeamMember({
|
|
1871
|
-
name,
|
|
1872
|
-
harness,
|
|
1873
|
-
config: systemPrompt ? { systemPromptPrefix: systemPrompt } : undefined,
|
|
1874
|
-
reason,
|
|
1875
|
-
})
|
|
1876
|
-
return {
|
|
1877
|
-
text: [
|
|
1878
|
-
`团队成员已创建并加入 channel:`,
|
|
1879
|
-
` id: ${agent.id}`,
|
|
1880
|
-
` name: ${agent.name}(role=worker, harness=${agent.harness})`,
|
|
1881
|
-
`新成员当前空闲,可立即 dispatch_task 指派任务;list_team_agents 可随时查看团队名册。`,
|
|
1882
|
-
].join('\n'),
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
|
|
1886
|
-
case 'update_team_agent': {
|
|
1887
|
-
const agentId = req.arguments.agent_id as string
|
|
1888
|
-
const name = req.arguments.name as string | undefined
|
|
1889
|
-
const systemPrompt = req.arguments.system_prompt as string | undefined
|
|
1890
|
-
const enabled = req.arguments.enabled as boolean | undefined
|
|
1891
|
-
const reason = req.arguments.reason as string | undefined
|
|
1892
|
-
const agent = await ws.updateTeamMember(agentId, {
|
|
1893
|
-
name,
|
|
1894
|
-
config: systemPrompt !== undefined ? { systemPromptPrefix: systemPrompt } : undefined,
|
|
1895
|
-
enabled: enabled === undefined ? undefined : (enabled ? 1 : 0),
|
|
1896
|
-
reason,
|
|
1897
|
-
})
|
|
1898
|
-
return {
|
|
1899
|
-
text: `团队成员 ${agentId} 已更新:name="${agent.name}"${enabled !== undefined ? `, enabled=${enabled ? 1 : 0}` : ''};运行时将按新配置重载(下次任务生效)。`,
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
case 'remove_team_agent': {
|
|
1904
|
-
const agentId = req.arguments.agent_id as string
|
|
1905
|
-
const reason = req.arguments.reason as string | undefined
|
|
1906
|
-
const result = await ws.removeTeamMember(agentId, reason)
|
|
1907
|
-
const recycleNote = result.recycledTasks.length > 0
|
|
1908
|
-
? `其 ${result.recycledTasks.length} 个在途任务已回收(排队任务重派给剩余最短队列成员;执行中任务转 FAILED 待调度重试)。`
|
|
1909
|
-
: `该成员无在途任务。`
|
|
1910
|
-
return { text: `团队成员 ${agentId} 已移除。${recycleNote}` }
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
case 'my_industrial_nodes': {
|
|
1914
|
-
return toolMyIndustrialNodes(this.selfAgentId)
|
|
1915
|
-
}
|
|
1916
|
-
case 'dcw_control': {
|
|
1917
|
-
return toolDcwControl(this.selfAgentId, req.arguments as { node_id?: string, value?: number | string, hypothesis?: string, task_id?: string })
|
|
1918
|
-
}
|
|
1919
|
-
case 'dcw_read': {
|
|
1920
|
-
return toolDcwRead(this.selfAgentId, req.arguments as { node_id?: string })
|
|
1921
|
-
}
|
|
1922
|
-
case 'daq_query': {
|
|
1923
|
-
return toolDaqQuery(this.selfAgentId, req.arguments as Parameters<typeof toolDaqQuery>[1])
|
|
1924
|
-
}
|
|
1925
|
-
case 'daq_frames': {
|
|
1926
|
-
return toolDaqFrames(this.selfAgentId, req.arguments as Parameters<typeof toolDaqFrames>[1])
|
|
1927
|
-
}
|
|
1928
|
-
case 'dcw_judge': {
|
|
1929
|
-
return toolDcwJudge(this.selfAgentId, req.arguments as { record_id?: string, verdict?: string, reason?: string })
|
|
1930
|
-
}
|
|
1931
|
-
case 'dcw_rollback': {
|
|
1932
|
-
return toolDcwRollback(this.selfAgentId, req.arguments as { record_id?: string, node_id?: string, to?: string })
|
|
1933
|
-
}
|
|
1934
|
-
case 'dcw_journal': {
|
|
1935
|
-
return toolDcwJournal(this.selfAgentId, req.arguments as { node_id?: string, recipe_id?: string, limit?: number | string })
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
// 插件工具分发(ctx.omp.registerTool 注册的自定义工具)
|
|
1939
|
-
const pluginTool = listPluginTools().get(req.toolName)
|
|
1940
|
-
if (pluginTool) {
|
|
1941
|
-
return await pluginTool.handler(req.arguments ?? {}, {
|
|
1942
|
-
agentId: this.selfAgentId,
|
|
1943
|
-
channelId: this.channelId,
|
|
1944
|
-
role: this.agentRole,
|
|
1945
|
-
name: this.agentName,
|
|
1946
|
-
})
|
|
1947
|
-
}
|
|
1948
|
-
return { text: `未知工具: ${req.toolName}`, isError: true }
|
|
1949
|
-
}
|
|
1950
|
-
catch (err) {
|
|
1951
|
-
return {
|
|
1952
|
-
text: `工具执行异常(${req.toolName}): ${err instanceof Error ? err.message : String(err)}`,
|
|
1953
|
-
isError: true,
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* OmpRpcAgentImpl — omp harness 的完整 AgentInterface 实现。
|
|
3
|
+
*
|
|
4
|
+
* 通过 OmpRpcClient 驱动真实 omp 子进程(--mode rpc),将 omp 的原生 agent 能力
|
|
5
|
+
* (LLM 推理 + 内置工具 read/write/edit/bash/grep/glob 等)接入 Workshop 的
|
|
6
|
+
* Channel 编排体系。
|
|
7
|
+
*
|
|
8
|
+
* 核心设计:
|
|
9
|
+
* - 每个 Agent 实例持有一个 omp 子进程(lazy spawn,跨消息复用)
|
|
10
|
+
* - AgentWorkspace 方法经 host-tool-bridge 注册为 omp host tools,agent 原生调用
|
|
11
|
+
* report_progress / complete_task / dispatch_task / send_message 等,无需文本解析
|
|
12
|
+
* - omp AgentSessionEvent → AgentEvent 五变体映射
|
|
13
|
+
* - worker run():接收 assign → prompt omp → agent 用原生工具作业 + host tools 管理任务生命周期
|
|
14
|
+
* - lead supervise():格式化快照 → prompt omp → agent 直接用 host tools 执行调度决策 → 返回 []
|
|
15
|
+
* - dispose():杀子进程
|
|
16
|
+
*
|
|
17
|
+
* prompt 组装与工具分发已抽至共享层(prompt-builder / host-tool-bridge),
|
|
18
|
+
* 与 codex/dsh/opencode impl 全引擎一致。协议权威:omp://rpc.md。
|
|
19
|
+
*/
|
|
20
|
+
import { createLogger } from '../logger'
|
|
21
|
+
import { randomUUID } from 'node:crypto'
|
|
22
|
+
import type {
|
|
23
|
+
AgentEvent,
|
|
24
|
+
AgentInterface,
|
|
25
|
+
AgentInfo,
|
|
26
|
+
AgentRunContext,
|
|
27
|
+
AgentRunRequest,
|
|
28
|
+
SupervisionDecision,
|
|
29
|
+
SupervisionSnapshot,
|
|
30
|
+
} from './agent-interface'
|
|
31
|
+
import type { A2AMessage } from '../types/a2a'
|
|
32
|
+
import type { AgentContextStats } from '../types/task'
|
|
33
|
+
import {
|
|
34
|
+
OmpRpcClient,
|
|
35
|
+
type AgentSessionEvent,
|
|
36
|
+
type CompactionResult,
|
|
37
|
+
type HostToolCallRequest,
|
|
38
|
+
} from './adapters/omp-rpc-client'
|
|
39
|
+
import {
|
|
40
|
+
registerHarnessProcess,
|
|
41
|
+
bindHarnessProcess,
|
|
42
|
+
markHarnessProcessExit,
|
|
43
|
+
killHarnessProcess,
|
|
44
|
+
} from './harness-process'
|
|
45
|
+
import {
|
|
46
|
+
attachTerminalTap,
|
|
47
|
+
markTerminalSessionExit,
|
|
48
|
+
} from './harness-terminal'
|
|
49
|
+
import {
|
|
50
|
+
createSessionState,
|
|
51
|
+
dispatchHostTool,
|
|
52
|
+
hostToolsForRole,
|
|
53
|
+
} from './host-tool-bridge'
|
|
54
|
+
import {
|
|
55
|
+
contextPrefix as buildContextPrefix,
|
|
56
|
+
createRosterCache,
|
|
57
|
+
extractJsonArray,
|
|
58
|
+
parseSteerBanner,
|
|
59
|
+
peerPrompt,
|
|
60
|
+
supervisePrompt,
|
|
61
|
+
systemManual,
|
|
62
|
+
toolArgsPreview,
|
|
63
|
+
workerPrompt,
|
|
64
|
+
} from './prompt-builder'
|
|
65
|
+
import { renderPrompt } from '../prompts/loader'
|
|
66
|
+
import { attachOmpPluginBridge, onPluginToolsChange } from './plugin-tools'
|
|
67
|
+
import { ompSettings } from '../settings'
|
|
68
|
+
|
|
69
|
+
const log = createLogger('workshop.omp')
|
|
70
|
+
|
|
71
|
+
// 兼容再导出(脚本/测试历史导入面;实现在 host-tool-bridge)
|
|
72
|
+
export { HOST_TOOLS, hostToolsForRole } from './host-tool-bridge'
|
|
73
|
+
|
|
74
|
+
// ===== 配置 =====
|
|
75
|
+
|
|
76
|
+
export interface OmpAgentConfig {
|
|
77
|
+
/** omp 可执行文件路径(默认 'omp',从 PATH 查找) */
|
|
78
|
+
command?: string
|
|
79
|
+
/** 额外 CLI 参数 */
|
|
80
|
+
args?: string[]
|
|
81
|
+
/** omp 工作目录(默认 process.cwd()) */
|
|
82
|
+
cwd?: string
|
|
83
|
+
/** 模型 provider(如 'anthropic'/'openai'/'zhipu');省略则用 omp 默认 */
|
|
84
|
+
provider?: string
|
|
85
|
+
/** 模型 ID;省略则用 omp 默认 */
|
|
86
|
+
model?: string
|
|
87
|
+
/** thinking level: off/minimal/low/medium/high/xhigh/max */
|
|
88
|
+
thinkingLevel?: string
|
|
89
|
+
/** 自定义系统 prompt 前缀(拼接到 agent 系统指令之前) */
|
|
90
|
+
systemPromptPrefix?: string
|
|
91
|
+
/** 限制 omp 仅使用指定内置工具(null = 全部) */
|
|
92
|
+
toolNames?: string[]
|
|
93
|
+
/** 每轮停滞超时(ms,默认 300000):整轮无任何 omp 事件才中止;工具内阻塞(poll_messages ≤180s)有 tool 事件刷新计时 */
|
|
94
|
+
promptTimeoutMs?: number
|
|
95
|
+
/** supervise 轮超时(ms,默认 150000) */
|
|
96
|
+
superviseTimeoutMs?: number
|
|
97
|
+
/**
|
|
98
|
+
* omp 输出模式:'rpc-ui'(默认)= RPC 协议 + UI 上下文 —— ask 工具与
|
|
99
|
+
* extension_ui_request 对话框可用(监控终端 HITL 通道,强制 noPty);
|
|
100
|
+
* 'rpc' = 纯协议(无 UI,ask 不可用)。
|
|
101
|
+
*/
|
|
102
|
+
rpcMode?: 'rpc' | 'rpc-ui'
|
|
103
|
+
/** channel 级作业场景 prompt(manager 装配时注入;用户场景规范,全员共享) */
|
|
104
|
+
scenarioPrompt?: string
|
|
105
|
+
/** agent 身份(由 factory 从 AgentInfo 注入) */
|
|
106
|
+
agentId?: string
|
|
107
|
+
name?: string
|
|
108
|
+
role?: 'lead' | 'worker'
|
|
109
|
+
channelId?: string
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 插件桥接管(host.mjs ctx.omp 排队注册的工具在此回放;幂等)
|
|
113
|
+
attachOmpPluginBridge()
|
|
114
|
+
|
|
115
|
+
/** 在跑 agent 实例表(工具注册表变更 → 热重发 set_host_tools,运行时注入无需重spawn) */
|
|
116
|
+
const gLive = globalThis as typeof globalThis & { __ompLiveAgents?: Set<OmpRpcAgentImpl> }
|
|
117
|
+
function liveAgents(): Set<OmpRpcAgentImpl> {
|
|
118
|
+
return gLive.__ompLiveAgents ??= new Set()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 工具注册表变更 → 全部在跑会话热更新工具面
|
|
122
|
+
onPluginToolsChange(() => {
|
|
123
|
+
for (const impl of liveAgents()) {
|
|
124
|
+
try {
|
|
125
|
+
impl.refreshPluginTools()
|
|
126
|
+
}
|
|
127
|
+
catch { /* 单实例失败不影响其他 */ }
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// ===== OmpRpcAgentImpl =====
|
|
132
|
+
|
|
133
|
+
export class OmpRpcAgentImpl implements AgentInterface {
|
|
134
|
+
private readonly config: OmpAgentConfig
|
|
135
|
+
private client: OmpRpcClient | null = null
|
|
136
|
+
private workspace: AgentRunContext['workspace'] | null = null
|
|
137
|
+
private agentInfo: AgentInfo | null = null
|
|
138
|
+
private hostToolsRegistered = false
|
|
139
|
+
/** host tool 会话态(当前任务 + 待回执上下文;与共享桥共用) */
|
|
140
|
+
private readonly toolState = createSessionState()
|
|
141
|
+
/** 工具桥上下文(分发唯一入口) */
|
|
142
|
+
private readonly bridgeCtx = {
|
|
143
|
+
identity: { agentId: '', channelId: '', role: 'worker' as 'lead' | 'worker', name: 'agent' },
|
|
144
|
+
state: this.toolState,
|
|
145
|
+
getWorkspace: () => this.workspace,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 会话回合状态(steer 可靠注入的依据):
|
|
150
|
+
* omp 的 steer 仅在回合 streaming 中生效——prompt 已入列但尚未开始输出时,
|
|
151
|
+
* steer 会"成功"返回但被静默丢弃。因此注入方轮询直到回合输出开始(streamingStarted)
|
|
152
|
+
* 才发送;若回合在等待期间结束,消息保持 pending 由消费循环处理。
|
|
153
|
+
*/
|
|
154
|
+
private streaming = false
|
|
155
|
+
private turnActive = false
|
|
156
|
+
/** 当前回合产生的 assistant 文本(供诊断) */
|
|
157
|
+
private turnText = ''
|
|
158
|
+
/** agent 身份信息(factory 注入;无需等待 init()) */
|
|
159
|
+
private selfAgentId = ''
|
|
160
|
+
private agentName = 'agent'
|
|
161
|
+
private agentRole: 'lead' | 'worker' = 'worker'
|
|
162
|
+
private channelId = ''
|
|
163
|
+
/** 团队名册缓存(共享 prompt-builder) */
|
|
164
|
+
private roster = createRosterCache({
|
|
165
|
+
selfAgentId: '',
|
|
166
|
+
listAgents: () => this.workspace!.listAgents(),
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// ===== 上下文治理(70% 无中断压缩环)=====
|
|
170
|
+
/** 压缩进行中(平台 gate 与 omp 原生压缩共用互斥位) */
|
|
171
|
+
private compacting = false
|
|
172
|
+
private lastCompactAt = 0
|
|
173
|
+
/** omp 不支持 compact 命令(旧版)→ 永久停用平台压缩;原生 auto-compaction 兜底,harvest 照常 */
|
|
174
|
+
private compactLegacy = false
|
|
175
|
+
/** 模型上下文窗口(get_state/get_session_stats 探测;null = 未知,percent 不可算) */
|
|
176
|
+
private contextWindow: number | null = null
|
|
177
|
+
private sessionId: string | null = null
|
|
178
|
+
/** harvest 双路去重(compact 响应与 compaction_end 事件可能双达) */
|
|
179
|
+
private lastHarvestKey = ''
|
|
180
|
+
private lastHarvestAt = 0
|
|
181
|
+
|
|
182
|
+
/** 总开关(omp.compact_enabled;env AW_OMP_COMPACT_ENABLED=0 显式关闭兼容) */
|
|
183
|
+
private compactEnabled(): boolean {
|
|
184
|
+
return ompSettings().compact_enabled
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private compactThreshold(): number {
|
|
188
|
+
return ompSettings().compact_threshold
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private compactMinIntervalMs(): number {
|
|
192
|
+
return ompSettings().compact_min_interval_ms
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private compactWaitMs(): number {
|
|
196
|
+
return ompSettings().compact_wait_ms
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** 被动上下文用量快照(无探测 RPC;getStatus 透出用) */
|
|
200
|
+
getContextStats(): AgentContextStats | null {
|
|
201
|
+
const usage = this.client?.getContextUsage() ?? null
|
|
202
|
+
const window = this.contextWindow ?? usage?.contextWindow ?? null
|
|
203
|
+
if (!usage && window === null) return null
|
|
204
|
+
const usedTokens = usage?.tokens ?? 0
|
|
205
|
+
return {
|
|
206
|
+
usedTokens,
|
|
207
|
+
contextWindow: window,
|
|
208
|
+
percent: window && window > 0 ? Math.min(1, usedTokens / window) : null,
|
|
209
|
+
compacting: this.compacting,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 上下文用量探测:get_session_stats 权威值;不可用退化为被动 usage + 已探测窗口。
|
|
215
|
+
* percent 归一化到 0-1(omp 可能返回 0-100)。
|
|
216
|
+
*/
|
|
217
|
+
private async probeUsage(client: OmpRpcClient): Promise<{ tokens: number, contextWindow: number | null, percent: number | null } | null> {
|
|
218
|
+
try {
|
|
219
|
+
const resp = await client.send({ type: 'get_session_stats' })
|
|
220
|
+
const cu = ((resp.data ?? {}) as Record<string, unknown>).contextUsage as
|
|
221
|
+
| { tokens?: number, contextWindow?: number, percent?: number }
|
|
222
|
+
| undefined
|
|
223
|
+
if (cu && typeof cu.tokens === 'number' && cu.tokens > 0) {
|
|
224
|
+
if (typeof cu.contextWindow === 'number' && cu.contextWindow > 0) {
|
|
225
|
+
this.contextWindow = cu.contextWindow
|
|
226
|
+
client.setContextWindow(cu.contextWindow)
|
|
227
|
+
}
|
|
228
|
+
const window = this.contextWindow ?? cu.contextWindow ?? null
|
|
229
|
+
const rawPercent = typeof cu.percent === 'number' ? (cu.percent > 1 ? cu.percent / 100 : cu.percent) : null
|
|
230
|
+
const percent = rawPercent ?? (window && window > 0 ? Math.min(1, cu.tokens / window) : null)
|
|
231
|
+
return { tokens: cu.tokens, contextWindow: window, percent }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch { /* get_session_stats 不可用 → 被动跟踪 */ }
|
|
235
|
+
return client.getContextUsage()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 上下文治理门控:三条 prompt 路径(worker/peer/supervise)在回合间隙统一调用;
|
|
240
|
+
* post-settle 经 onTurnSettled 复用同一守卫。任何失败路径都放行——压缩晚一轮
|
|
241
|
+
* 永远好过中断作业。三重防线:仅回合间隙发起 → get_state 复查 isStreaming/
|
|
242
|
+
* isCompacting → compacting 互斥位 + 最小间隔防振荡。
|
|
243
|
+
*/
|
|
244
|
+
private async contextGate(reason: string): Promise<void> {
|
|
245
|
+
if (!this.compactEnabled() || this.compactLegacy || this.compacting) return
|
|
246
|
+
const client = this.client
|
|
247
|
+
if (!client || !client.alive) return
|
|
248
|
+
try {
|
|
249
|
+
const usage = await this.probeUsage(client)
|
|
250
|
+
if (!usage || usage.percent === null || usage.percent < this.compactThreshold()) return
|
|
251
|
+
if (Date.now() - this.lastCompactAt < this.compactMinIntervalMs()) return
|
|
252
|
+
// 回合间隙硬校验:流式中/压缩中绝不发起(不中断执行的硬约束)
|
|
253
|
+
const st = await client.send({ type: 'get_state' })
|
|
254
|
+
const s = (st.data ?? {}) as Record<string, unknown>
|
|
255
|
+
if (s.isStreaming === true || s.isCompacting === true) return
|
|
256
|
+
if (typeof s.sessionId === 'string' && s.sessionId) this.sessionId = s.sessionId
|
|
257
|
+
this.compacting = true
|
|
258
|
+
this.lastCompactAt = Date.now()
|
|
259
|
+
try {
|
|
260
|
+
const result = await this.runCompaction(client)
|
|
261
|
+
if (result?.summary) {
|
|
262
|
+
await this.harvestCompaction(
|
|
263
|
+
result.summary,
|
|
264
|
+
result.tokensBefore,
|
|
265
|
+
result.tokensAfter ?? result.estimatedTokensAfter,
|
|
266
|
+
reason,
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
this.compacting = false
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
276
|
+
if (/compact/i.test(msg) && /unknown|unsupported|invalid|无效|未知|不支持/i.test(msg)) this.compactLegacy = true
|
|
277
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] contextGate(${reason}) 失败,放行: ${msg}`)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 发起 compact 并等待 compaction_end(响应内含 result 则立即收口;
|
|
283
|
+
* 超时放行——压缩在 harness 内后台继续,isCompacting 防后续双发)。
|
|
284
|
+
*/
|
|
285
|
+
private runCompaction(client: OmpRpcClient): Promise<CompactionResult | null> {
|
|
286
|
+
return new Promise<CompactionResult | null>((resolve) => {
|
|
287
|
+
let settled = false
|
|
288
|
+
const finish = (r: CompactionResult | null): void => {
|
|
289
|
+
if (settled) return
|
|
290
|
+
settled = true
|
|
291
|
+
off()
|
|
292
|
+
clearTimeout(timer)
|
|
293
|
+
resolve(r)
|
|
294
|
+
}
|
|
295
|
+
const off = client.onEvent((event) => {
|
|
296
|
+
if (event.type === 'compaction_end') {
|
|
297
|
+
finish((event as { result?: CompactionResult }).result ?? null)
|
|
298
|
+
}
|
|
299
|
+
else if (event.type === '__process_exit__') {
|
|
300
|
+
finish(null)
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
const timer = setTimeout(() => {
|
|
304
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] compact 等待超时(${this.compactWaitMs()}ms)→ 放行(压缩后台继续)`)
|
|
305
|
+
finish(null)
|
|
306
|
+
}, this.compactWaitMs())
|
|
307
|
+
void client.send({ type: 'compact', customInstructions: this.compactionHints() })
|
|
308
|
+
.then((resp) => {
|
|
309
|
+
const result = (resp.data as { result?: CompactionResult } | undefined)?.result
|
|
310
|
+
if (result?.summary) finish(result)
|
|
311
|
+
// 响应无 result:等 compaction_end 事件(协议权威语义)
|
|
312
|
+
})
|
|
313
|
+
.catch((err) => {
|
|
314
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
315
|
+
if (/compact/i.test(msg)) this.compactLegacy = true
|
|
316
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] compact 发起失败: ${msg}`)
|
|
317
|
+
finish(null)
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** 压缩摘要指令(外置 prompts;缺失时省略,omp 用默认策略) */
|
|
323
|
+
private compactionHints(): string | undefined {
|
|
324
|
+
try {
|
|
325
|
+
return renderPrompt('compaction-hints')
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return undefined
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 压缩摘要 harvest(三路统一:平台主动 compact / omp 阈值自动 / overflow):
|
|
334
|
+
* 经 workspace.recordSessionMemory 落库为本人 episodic-session 记忆。
|
|
335
|
+
* 双路去重:同摘要 60s 内只入库一次。
|
|
336
|
+
*/
|
|
337
|
+
private async harvestCompaction(summary: string, tokensBefore?: number, tokensAfter?: number, reason?: string): Promise<void> {
|
|
338
|
+
const key = summary.slice(0, 120)
|
|
339
|
+
if (key === this.lastHarvestKey && Date.now() - this.lastHarvestAt < 60_000) return
|
|
340
|
+
this.lastHarvestKey = key
|
|
341
|
+
this.lastHarvestAt = Date.now()
|
|
342
|
+
try {
|
|
343
|
+
await this.workspace?.recordSessionMemory?.({ summary, tokensBefore, tokensAfter, reason })
|
|
344
|
+
log.info(`[OmpRpcAgent:${this.selfAgentId}] 压缩摘要已入记忆(${summary.length} 字,reason=${reason ?? 'auto'})`)
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] 压缩摘要入库失败(不影响会话):`, err instanceof Error ? err.message : err)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** 回合落定钩子(AgentRuntime 在信箱空时调用):后台压缩检查,自守卫不抛错 */
|
|
352
|
+
async onTurnSettled(): Promise<void> {
|
|
353
|
+
await this.contextGate('post-settle')
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
constructor(config: Record<string, unknown> = {}) {
|
|
357
|
+
this.config = config as OmpAgentConfig
|
|
358
|
+
// effort 统一入口(channel 默认 LLM 注入 config.effort)→ omp thinking 级别
|
|
359
|
+
if (!this.config.thinkingLevel && typeof config.effort === 'string' && config.effort) {
|
|
360
|
+
this.config.thinkingLevel = config.effort
|
|
361
|
+
}
|
|
362
|
+
// factory 注入的 agent 身份(无需等待 init())
|
|
363
|
+
this.selfAgentId = this.config.agentId ?? ''
|
|
364
|
+
this.agentName = this.config.name ?? 'agent'
|
|
365
|
+
this.agentRole = this.config.role ?? 'worker'
|
|
366
|
+
this.channelId = this.config.channelId ?? ''
|
|
367
|
+
this.bridgeCtx.identity = { agentId: this.selfAgentId, channelId: this.channelId, role: this.agentRole, name: this.agentName }
|
|
368
|
+
this.roster = createRosterCache({
|
|
369
|
+
selfAgentId: this.selfAgentId,
|
|
370
|
+
listAgents: () => this.workspace!.listAgents(),
|
|
371
|
+
})
|
|
372
|
+
liveAgents().add(this) // 在跑实例表:插件工具热注入的目标集
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ===== 生命周期 =====
|
|
376
|
+
|
|
377
|
+
async init(input: { agent: AgentInfo, channelId: string }): Promise<void> {
|
|
378
|
+
this.agentInfo = input.agent
|
|
379
|
+
this.channelId = input.channelId
|
|
380
|
+
this.agentName = input.agent.name
|
|
381
|
+
this.agentRole = input.agent.role
|
|
382
|
+
this.selfAgentId = input.agent.id
|
|
383
|
+
this.bridgeCtx.identity = { agentId: this.selfAgentId, channelId: this.channelId, role: this.agentRole, name: this.agentName }
|
|
384
|
+
this.roster = createRosterCache({
|
|
385
|
+
selfAgentId: this.selfAgentId,
|
|
386
|
+
listAgents: () => this.workspace!.listAgents(),
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async dispose(): Promise<void> {
|
|
391
|
+
liveAgents().delete(this)
|
|
392
|
+
if (this.client) {
|
|
393
|
+
const pid = this.client.pid
|
|
394
|
+
await this.client.dispose()
|
|
395
|
+
this.client = null
|
|
396
|
+
if (pid) {
|
|
397
|
+
markHarnessProcessExit(pid, null)
|
|
398
|
+
markTerminalSessionExit(pid, null)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
this.hostToolsRegistered = false
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** 工具注册表变更 → 热重发 set_host_tools(在跑会话立即获得插件新工具,无需重spawn) */
|
|
405
|
+
refreshPluginTools(): void {
|
|
406
|
+
const client = this.client
|
|
407
|
+
if (!client || !client.alive) return
|
|
408
|
+
void client.send({ type: 'set_host_tools', tools: hostToolsForRole(this.agentRole) }).catch(() => {})
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** harness 进程资源信息(运行时资源监控;进程未 spawn/已回收 → null) */
|
|
412
|
+
getProcessInfo(): { pid: number, alive: boolean, command: string } | null {
|
|
413
|
+
const client = this.client
|
|
414
|
+
const pid = client?.pid
|
|
415
|
+
if (!pid || !client) return null
|
|
416
|
+
return { pid, alive: client.alive, command: `omp --mode ${this.rpcMode}` }
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** 强制终止 harness 进程(进程树;终止后由调用方停止对应 AgentRuntime) */
|
|
420
|
+
killProcess(): void {
|
|
421
|
+
const pid = this.client?.pid
|
|
422
|
+
if (pid) {
|
|
423
|
+
markTerminalSessionExit(pid, null)
|
|
424
|
+
killHarnessProcess(pid)
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
this.client?.kill()
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* harness 进程存活校准(manager sweeper 周期性调用;休眠/强杀后 exit 事件
|
|
433
|
+
* 可能不达父进程):按 PID 探 OS 实际存在性,进程已死 → 客户端收敛为已退出,
|
|
434
|
+
* 在途回合经 __process_exit__ 归位,下一回合 ensureClient 重生子进程。
|
|
435
|
+
*/
|
|
436
|
+
reconcileProcess(): void {
|
|
437
|
+
this.client?.reconcile()
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** omp 输出模式(rpc-ui = 默认,启用 HITL 对话框) */
|
|
441
|
+
private get rpcMode(): 'rpc' | 'rpc-ui' {
|
|
442
|
+
return this.config.rpcMode === 'rpc' ? 'rpc' : 'rpc-ui'
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// ===== 工具桥(共享分发)=====
|
|
446
|
+
|
|
447
|
+
/** 引擎无关工具面:REST/MCP 桥直调与 omp 内部 host_tool_call 共用同一实现 */
|
|
448
|
+
dispatchHostTool(toolName: string, args: Record<string, unknown>): Promise<{ text: string, isError?: boolean }> {
|
|
449
|
+
return dispatchHostTool(this.bridgeCtx, { toolName, arguments: args })
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private async handleHostTool(req: HostToolCallRequest): Promise<{ text: string, isError?: boolean }> {
|
|
453
|
+
return this.dispatchHostTool(req.toolName, req.arguments ?? {})
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ===== prompt 组合 =====
|
|
457
|
+
|
|
458
|
+
/** 前置上下文(场景 × 身份 × 工业简报 × 名册;共享 prompt-builder) */
|
|
459
|
+
private async contextPrefix(): Promise<string> {
|
|
460
|
+
return buildContextPrefix({
|
|
461
|
+
scenarioPrompt: this.config.scenarioPrompt,
|
|
462
|
+
systemPromptPrefix: this.config.systemPromptPrefix,
|
|
463
|
+
agentId: this.selfAgentId,
|
|
464
|
+
roster: await this.roster.roster(),
|
|
465
|
+
})
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private systemManual(): string {
|
|
469
|
+
return systemManual()
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* 实时消息注入(送达模式协议):
|
|
474
|
+
* - 回合 streaming 中 → 立即 steer,返回 'steer'(同轮可见,唯一可标记消费的路径)
|
|
475
|
+
* - 回合活跃但尚未 streaming(prompt 排队窗口,上限 20s)→ 等 streaming 开始后 steer
|
|
476
|
+
* - 回合被 host 工具阻塞(如 poll_messages 等待)/空闲/发送失败 → 返回 'deferred':
|
|
477
|
+
* 消息保持 pending,poll_messages 的 Mailbox 到信回调即时取走(毫秒级),或本回合
|
|
478
|
+
* 结束后由消费循环按 FIFO 起回合处理。
|
|
479
|
+
*/
|
|
480
|
+
async steer(text: string): Promise<'steer' | 'deferred'> {
|
|
481
|
+
// 从确定性触发横幅提取回执上下文(AgentRuntime.injectSteer 生成,格式固定):
|
|
482
|
+
// "[实时消息 from <id>]: ..." + "[系统触发器] 本消息要求回复(reply_to=<messageId>)。"
|
|
483
|
+
const banner = parseSteerBanner(text)
|
|
484
|
+
if (banner) {
|
|
485
|
+
this.toolState.replyContext = banner
|
|
486
|
+
}
|
|
487
|
+
const client = this.client
|
|
488
|
+
if (!client) return 'deferred'
|
|
489
|
+
try {
|
|
490
|
+
if (this.streaming) {
|
|
491
|
+
await client.send({ type: 'steer', message: text })
|
|
492
|
+
return 'steer'
|
|
493
|
+
}
|
|
494
|
+
if (this.turnActive) {
|
|
495
|
+
// prompt 排队窗口:等 streaming 开始(上限 20s);工具阻塞的回合等不到 → deferred
|
|
496
|
+
const deadline = Date.now() + 20_000
|
|
497
|
+
while (Date.now() < deadline && this.turnActive && !this.streaming) {
|
|
498
|
+
const { promise, resolve } = Promise.withResolvers()
|
|
499
|
+
setTimeout(resolve, 150)
|
|
500
|
+
await promise
|
|
501
|
+
}
|
|
502
|
+
if (this.streaming && this.turnActive) {
|
|
503
|
+
await client.send({ type: 'steer', message: text })
|
|
504
|
+
return 'steer'
|
|
505
|
+
}
|
|
506
|
+
return 'deferred'
|
|
507
|
+
}
|
|
508
|
+
// 空闲/回合已结束:deferred(消息 pending,消费循环 dequeue 即起回合)
|
|
509
|
+
return 'deferred'
|
|
510
|
+
}
|
|
511
|
+
catch (err) {
|
|
512
|
+
log.error(`[OmpRpcAgent:${this.selfAgentId}] steer 注入失败(消息保持 pending):`, err instanceof Error ? err.message : err)
|
|
513
|
+
return 'deferred'
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async* run(request: AgentRunRequest, ctx: AgentRunContext): AsyncIterable<AgentEvent> {
|
|
518
|
+
const kind = request.message.metadata?.['x-aw-task-kind']
|
|
519
|
+
|
|
520
|
+
// worker: assign 消息 → 执行任务
|
|
521
|
+
if (kind === 'assign' && ctx.role === 'worker') {
|
|
522
|
+
yield* this.workerRun(request, ctx)
|
|
523
|
+
return
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// worker/lead: 同事点对点消息(含实时通信触发器)→ 按触发器语义处理并回复;
|
|
527
|
+
// 人类经 REST 注入的消息(from 空 + x-aw-from-label)同样进入应答流
|
|
528
|
+
if (!kind && (request.fromAgentId || request.message.metadata?.['x-aw-from-label'])) {
|
|
529
|
+
yield* this.peerMessageRun(request, ctx)
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// 其余消息(lead 的 assign/child-completed 等):no-op(调度由 supervise() 处理)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// ===== supervise() =====
|
|
537
|
+
|
|
538
|
+
/** supervise 单飞守卫:同一 client 不并发 LLM 回合(残留回合与下一 prompt 混流的根因) */
|
|
539
|
+
private supervising = false
|
|
540
|
+
|
|
541
|
+
async supervise(snapshot: SupervisionSnapshot, ctx: AgentRunContext, opts?: { signal?: AbortSignal }): Promise<SupervisionDecision[]> {
|
|
542
|
+
await this.ensureClient(ctx)
|
|
543
|
+
if (!this.client) return []
|
|
544
|
+
if (this.supervising) return [] // 上一轮 supervise 未收口:跳过本拍(节流即正确)
|
|
545
|
+
|
|
546
|
+
// 上下文门控(≥70% 先压缩再调度;回合间隙发起,失败放行)
|
|
547
|
+
await this.contextGate('supervise')
|
|
548
|
+
|
|
549
|
+
const prompt = supervisePrompt({
|
|
550
|
+
snapshot,
|
|
551
|
+
agentName: this.agentName,
|
|
552
|
+
channelId: this.channelId,
|
|
553
|
+
ctxPrefix: await this.contextPrefix(),
|
|
554
|
+
manual: this.systemManual(),
|
|
555
|
+
memory: ctx.memory,
|
|
556
|
+
})
|
|
557
|
+
// 150s 上界:supervise 持 lead.execLock 期间信箱消费停顿(更久会拖垮 worker 回执处理);
|
|
558
|
+
// supervise 是一次真实 LLM 回合:omp 冷启动(插件/MCP 加载 30~90s)+ 慢 provider
|
|
559
|
+
// 单步可能 >60s,过紧会把正常回合掐成 "Interrupted by user"。默认 150s,
|
|
560
|
+
// 仅拦真僵死(更紧的预算由调用方 config.superviseTimeoutMs 显式传入);真 abort 已实现。
|
|
561
|
+
const timeoutMs = this.config.superviseTimeoutMs ?? 150_000
|
|
562
|
+
|
|
563
|
+
return new Promise<SupervisionDecision[]>((resolve) => {
|
|
564
|
+
let assistantText = ''
|
|
565
|
+
let resolved = false
|
|
566
|
+
|
|
567
|
+
const finish = (decisions: SupervisionDecision[]) => {
|
|
568
|
+
if (resolved) return
|
|
569
|
+
resolved = true
|
|
570
|
+
this.supervising = false
|
|
571
|
+
unsub()
|
|
572
|
+
clearTimeout(timer)
|
|
573
|
+
signalUnsub?.()
|
|
574
|
+
resolve(decisions)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const abortTurn = (): void => {
|
|
578
|
+
// 超时/外部取消:真正中止 omp 当前回合 —— 只 resolve 不 abort 会让残留回合
|
|
579
|
+
// 与下一个 prompt 在同一 client 混流(决策错位 + token 空烧)
|
|
580
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] supervise 超时(${timeoutMs}ms)→ abort 当前调度回合`)
|
|
581
|
+
void this.client?.send({ type: 'abort' }).catch(() => {})
|
|
582
|
+
finish([])
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const unsub = this.client!.onEvent((event) => {
|
|
586
|
+
if (event.type === 'message_update' && event.assistantMessageEvent?.type === 'text_delta') {
|
|
587
|
+
assistantText += event.assistantMessageEvent.delta ?? ''
|
|
588
|
+
}
|
|
589
|
+
if (event.type === 'agent_end' && event.isTerminal !== false) {
|
|
590
|
+
// 尝试从文本解析 JSON 决策(备用:如果 agent 没用 host tools 而是输出 JSON)
|
|
591
|
+
const parsed = extractJsonArray(assistantText)
|
|
592
|
+
if (parsed && parsed.length > 0) {
|
|
593
|
+
finish(parsed as SupervisionDecision[])
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// agent 可能已通过 host tools 直接执行了调度,返回空(已执行)
|
|
597
|
+
finish([])
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (event.type === '__process_exit__' || event.type === '__error__') {
|
|
601
|
+
finish([])
|
|
602
|
+
}
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
const timer = setTimeout(abortTurn, timeoutMs)
|
|
606
|
+
|
|
607
|
+
// 外部取消(调度器 cancel 路径)传导:abort 当前 LLM 回合并立即收口
|
|
608
|
+
let signalUnsub: (() => void) | undefined
|
|
609
|
+
if (opts?.signal) {
|
|
610
|
+
if (opts.signal.aborted) {
|
|
611
|
+
unsub()
|
|
612
|
+
resolve([])
|
|
613
|
+
return
|
|
614
|
+
}
|
|
615
|
+
const onAbort = (): void => abortTurn()
|
|
616
|
+
opts.signal.addEventListener('abort', onAbort, { once: true })
|
|
617
|
+
signalUnsub = () => opts.signal?.removeEventListener('abort', onAbort)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
this.supervising = true
|
|
621
|
+
this.client!.send({ type: 'prompt', message: prompt }).catch(() => finish([]))
|
|
622
|
+
})
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// ===== 内部:worker 执行 =====
|
|
626
|
+
|
|
627
|
+
private async* workerRun(request: AgentRunRequest, ctx: AgentRunContext): AsyncGenerator<AgentEvent, void, unknown> {
|
|
628
|
+
const taskId = request.taskId
|
|
629
|
+
?? (request.message.metadata?.['x-aw-task-id'] as string | undefined)
|
|
630
|
+
if (!taskId) return
|
|
631
|
+
|
|
632
|
+
this.toolState.currentTaskId = taskId
|
|
633
|
+
|
|
634
|
+
try {
|
|
635
|
+
await this.ensureClient(ctx)
|
|
636
|
+
}
|
|
637
|
+
catch (err) {
|
|
638
|
+
yield {
|
|
639
|
+
kind: 'error',
|
|
640
|
+
error: {
|
|
641
|
+
code: 'OMP_SPAWN_FAILED',
|
|
642
|
+
message: `omp 子进程启动失败: ${err instanceof Error ? err.message : String(err)}`,
|
|
643
|
+
},
|
|
644
|
+
}
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (!this.client) {
|
|
649
|
+
yield { kind: 'error', error: { code: 'OMP_NOT_READY', message: 'omp 客户端未就绪' } }
|
|
650
|
+
return
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// 构建 prompt
|
|
654
|
+
const taskText = request.message.parts.map((p) => {
|
|
655
|
+
if ('text' in p) return p.text
|
|
656
|
+
if ('data' in p) return JSON.stringify(p.data)
|
|
657
|
+
if ('url' in p) return p.url
|
|
658
|
+
if ('raw' in p) return p.raw
|
|
659
|
+
return ''
|
|
660
|
+
}).join('\n')
|
|
661
|
+
const prompt = workerPrompt({
|
|
662
|
+
agentName: this.agentName,
|
|
663
|
+
channelId: this.channelId,
|
|
664
|
+
taskId,
|
|
665
|
+
taskText,
|
|
666
|
+
memory: request.memory,
|
|
667
|
+
ctxPrefix: await this.contextPrefix(),
|
|
668
|
+
manual: this.systemManual(),
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
// 流式执行 + 事件映射
|
|
672
|
+
yield* this.promptAndStream(prompt, taskId, ctx.signal)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* 点对点消息处理(实时通信驱动;lead 与 worker 通用)。
|
|
677
|
+
* 触发器语义:metadata['x-aw-require-reply']='true' → 必须经 send_message_to_agent
|
|
678
|
+
* 回给发送者:执行结果 + 对方所需内容,in_reply_to 关联原消息,并声明是否需再响应。
|
|
679
|
+
*/
|
|
680
|
+
private async* peerMessageRun(request: AgentRunRequest, ctx: AgentRunContext): AsyncGenerator<AgentEvent, void, unknown> {
|
|
681
|
+
try {
|
|
682
|
+
await this.ensureClient(ctx)
|
|
683
|
+
}
|
|
684
|
+
catch (err) {
|
|
685
|
+
// spawn 失败必须显式报错:静默 return 会让 sawRunError=false → 消息被
|
|
686
|
+
// 直接 markConsumed,实时消息一次性丢失且无任何重试;产出 error 事件
|
|
687
|
+
// 走消息 requeue(≤2 次),与 workerRun 同口径
|
|
688
|
+
yield {
|
|
689
|
+
kind: 'error',
|
|
690
|
+
error: {
|
|
691
|
+
code: 'OMP_SPAWN_FAILED',
|
|
692
|
+
message: `omp 子进程启动失败(peer 消息未消费,将重试): ${err instanceof Error ? err.message : String(err)}`,
|
|
693
|
+
},
|
|
694
|
+
}
|
|
695
|
+
return
|
|
696
|
+
}
|
|
697
|
+
if (!this.client) {
|
|
698
|
+
yield { kind: 'error', error: { code: 'OMP_NOT_READY', message: 'omp 客户端未就绪(peer 消息未消费,将重试)' } }
|
|
699
|
+
return
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const msg = request.message
|
|
703
|
+
// 显示名:agent 同事用 id(名册可解析);人类发送者用 fromLabel;兜底 unknown
|
|
704
|
+
const fromId = request.fromAgentId
|
|
705
|
+
?? (typeof msg.metadata?.['x-aw-from-label'] === 'string' ? msg.metadata['x-aw-from-label'] : undefined)
|
|
706
|
+
?? 'unknown'
|
|
707
|
+
const msgText = msg.parts.map((p) => {
|
|
708
|
+
if ('text' in p) return p.text
|
|
709
|
+
if ('data' in p) return JSON.stringify(p.data)
|
|
710
|
+
if ('url' in p) return p.url
|
|
711
|
+
if ('raw' in p) return p.raw
|
|
712
|
+
return ''
|
|
713
|
+
}).join('\n')
|
|
714
|
+
const requireReply = msg.metadata?.['x-aw-require-reply'] === 'true'
|
|
715
|
+
const isReply = typeof msg.metadata?.['x-aw-in-reply-to'] === 'string'
|
|
716
|
+
// 跨 Channel 来信:发送方是其他 channel 的 lead,回执必须走 send_cross_channel_message
|
|
717
|
+
const crossChannel = msg.metadata?.['x-aw-cross-channel'] === 'true'
|
|
718
|
+
const fromChannel = typeof msg.metadata?.['x-aw-from-channel'] === 'string'
|
|
719
|
+
? String(msg.metadata['x-aw-from-channel'])
|
|
720
|
+
: ''
|
|
721
|
+
// 回执自动关联仅对真实 agent 发送者生效(人类无 agentId 可回投;
|
|
722
|
+
// 对人类的回复经事件流/时间线可见)
|
|
723
|
+
this.toolState.replyContext = requireReply && request.fromAgentId && !crossChannel
|
|
724
|
+
? { fromId: request.fromAgentId, messageId: msg.messageId }
|
|
725
|
+
: null
|
|
726
|
+
|
|
727
|
+
const prompt = peerPrompt({
|
|
728
|
+
agentName: this.agentName,
|
|
729
|
+
role: this.agentRole,
|
|
730
|
+
channelId: this.channelId,
|
|
731
|
+
ctxPrefix: await this.contextPrefix(),
|
|
732
|
+
manual: this.systemManual(),
|
|
733
|
+
memory: request.memory,
|
|
734
|
+
fromId,
|
|
735
|
+
messageId: msg.messageId,
|
|
736
|
+
requireReply,
|
|
737
|
+
isReply,
|
|
738
|
+
crossChannel,
|
|
739
|
+
fromChannel,
|
|
740
|
+
msgText,
|
|
741
|
+
})
|
|
742
|
+
|
|
743
|
+
yield* this.promptAndStream(prompt, undefined, ctx.signal)
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// ===== 内部:prompt 发送 + 事件流桥接 =====
|
|
747
|
+
|
|
748
|
+
private async* promptAndStream(
|
|
749
|
+
prompt: string,
|
|
750
|
+
taskId: string | undefined,
|
|
751
|
+
signal: AbortSignal,
|
|
752
|
+
): AsyncGenerator<AgentEvent, void, unknown> {
|
|
753
|
+
const client = this.client
|
|
754
|
+
if (!client) {
|
|
755
|
+
throw new Error('omp 客户端未就绪')
|
|
756
|
+
}
|
|
757
|
+
// 上下文门控(≥70% 时在回合间隙先压缩再发 prompt;等待有界,失败放行)
|
|
758
|
+
await this.contextGate('pre-prompt')
|
|
759
|
+
const queue: AgentEvent[] = []
|
|
760
|
+
let resolveWait: (() => void) | null = null
|
|
761
|
+
let isDone = false
|
|
762
|
+
// 回合生命周期:prompt 已发出 → turnActive;首条 message_update → streaming;message_end/turn_end → 结束
|
|
763
|
+
this.turnActive = true
|
|
764
|
+
this.streaming = false
|
|
765
|
+
this.turnText = ''
|
|
766
|
+
// 正文流式差额账本:contentIndex → 已流出长度。部分 provider(reasoning 模型/
|
|
767
|
+
// openai-completions)的 text 块不走 text_delta(只有换行),全文在 text_end.content
|
|
768
|
+
// 落定 —— 差额兜底把未流出部分补发为 delta,保证前端流式不缺正文。
|
|
769
|
+
const textSent = new Map<number, number>()
|
|
770
|
+
const unsubState = client.onEvent((event) => {
|
|
771
|
+
if (event.type === 'message_update') {
|
|
772
|
+
const ev = event.assistantMessageEvent as
|
|
773
|
+
| { type?: string, delta?: string, content?: string, contentIndex?: number }
|
|
774
|
+
| undefined
|
|
775
|
+
if (ev?.type === 'text_delta') {
|
|
776
|
+
this.streaming = true
|
|
777
|
+
const text = ev.delta ?? ''
|
|
778
|
+
const ci = typeof ev.contentIndex === 'number' ? ev.contentIndex : 0
|
|
779
|
+
textSent.set(ci, (textSent.get(ci) ?? 0) + text.length)
|
|
780
|
+
this.turnText += text
|
|
781
|
+
// LLM 流式增量透出(AEP agent.delta 事件源):仅 worker/peer 转本走生成器
|
|
782
|
+
if (text) enqueueDelta(text)
|
|
783
|
+
}
|
|
784
|
+
else if (ev?.type === 'text_end' && typeof ev.content === 'string') {
|
|
785
|
+
const ci = typeof ev.contentIndex === 'number' ? ev.contentIndex : 0
|
|
786
|
+
const sent = textSent.get(ci) ?? 0
|
|
787
|
+
if (ev.content.length > sent) {
|
|
788
|
+
const chunk = ev.content.slice(sent)
|
|
789
|
+
textSent.set(ci, ev.content.length)
|
|
790
|
+
this.streaming = true
|
|
791
|
+
this.turnText += chunk
|
|
792
|
+
enqueueDelta(chunk)
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (event.type === 'message_end' || event.type === 'turn_end') {
|
|
797
|
+
this.streaming = false
|
|
798
|
+
flushDelta()
|
|
799
|
+
}
|
|
800
|
+
if (event.type === 'agent_end' && event.isTerminal !== false) {
|
|
801
|
+
this.turnActive = false
|
|
802
|
+
this.streaming = false
|
|
803
|
+
flushDelta()
|
|
804
|
+
}
|
|
805
|
+
if (event.type === '__process_exit__' || event.type === '__error__') {
|
|
806
|
+
this.turnActive = false
|
|
807
|
+
this.streaming = false
|
|
808
|
+
}
|
|
809
|
+
})
|
|
810
|
+
|
|
811
|
+
const enqueue = (event: AgentEvent): void => {
|
|
812
|
+
queue.push(event)
|
|
813
|
+
if (event.kind === 'done' || event.kind === 'error') isDone = true
|
|
814
|
+
lastActivity = Date.now()
|
|
815
|
+
resolveWait?.()
|
|
816
|
+
resolveWait = null
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// 停滞看门狗:整轮无任何 omp 事件(挂死的 LLM 调用/子进程僵死)时中止回合,
|
|
820
|
+
// 否则消息永久卡 consuming、队友消息无限堆积。host 工具内阻塞
|
|
821
|
+
// (poll_messages 最长 180s)与慢 provider(实测单步可 >5min)都有
|
|
822
|
+
// tool/状态事件刷新计时;默认 600s 覆盖慢回合,只拦真僵死。
|
|
823
|
+
const idleTimeoutMs = this.config.promptTimeoutMs ?? 600_000
|
|
824
|
+
let lastActivity = Date.now()
|
|
825
|
+
let stallTimer: ReturnType<typeof setTimeout> | null = null
|
|
826
|
+
|
|
827
|
+
// LLM 流式增量缓冲:50ms 批量合并为一帧 delta(防高频 text_delta 洪泛 WS)
|
|
828
|
+
let deltaBuf = ''
|
|
829
|
+
let deltaTimer: ReturnType<typeof setTimeout> | null = null
|
|
830
|
+
const flushDelta = (): void => {
|
|
831
|
+
if (deltaTimer) {
|
|
832
|
+
clearTimeout(deltaTimer)
|
|
833
|
+
deltaTimer = null
|
|
834
|
+
}
|
|
835
|
+
if (!deltaBuf) return
|
|
836
|
+
const text = deltaBuf
|
|
837
|
+
deltaBuf = ''
|
|
838
|
+
enqueue({ kind: 'delta', delta: { text } })
|
|
839
|
+
}
|
|
840
|
+
const enqueueDelta = (text: string): void => {
|
|
841
|
+
deltaBuf += text
|
|
842
|
+
deltaTimer ??= setTimeout(() => {
|
|
843
|
+
deltaTimer = null
|
|
844
|
+
flushDelta()
|
|
845
|
+
}, 50)
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// 订阅 omp 事件流
|
|
849
|
+
const unsub = client.onEvent((event) => {
|
|
850
|
+
for (const mapped of this.mapOmpEvent(event, taskId)) {
|
|
851
|
+
enqueue(mapped)
|
|
852
|
+
}
|
|
853
|
+
})
|
|
854
|
+
|
|
855
|
+
// abort 传导
|
|
856
|
+
const onAbort = (): void => {
|
|
857
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] run 被 abort(signal)→ 中止 omp 回合,taskId=${taskId ?? '-'}`)
|
|
858
|
+
client.send({ type: 'abort' }).catch(() => {})
|
|
859
|
+
if (!isDone) {
|
|
860
|
+
enqueue({ kind: 'done', final: taskId ? { taskId } : undefined })
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (signal.aborted) {
|
|
864
|
+
onAbort()
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
signal.addEventListener('abort', onAbort, { once: true })
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// 发送 prompt
|
|
871
|
+
try {
|
|
872
|
+
await client.send({ type: 'prompt', message: prompt })
|
|
873
|
+
}
|
|
874
|
+
catch (err) {
|
|
875
|
+
unsub()
|
|
876
|
+
signal.removeEventListener('abort', onAbort)
|
|
877
|
+
// 僵死进程回收:prompt send 失败 = stdio 断裂或 60s 命令确认超时 —— 进程
|
|
878
|
+
// "活着但不干活"(alive-but-wedged)。若仅报错不清理,消息重试(≤2 次)会
|
|
879
|
+
// 复用同一僵死 stdio 每次空转 60s;必须杀掉并置空,下回合 ensureClient
|
|
880
|
+
// 全新重生(host tools/模型/终端 tap 随重建)。
|
|
881
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] prompt 失败 → 回收可疑僵死进程 pid=${client.pid}`)
|
|
882
|
+
this.killProcess()
|
|
883
|
+
this.client = null
|
|
884
|
+
this.hostToolsRegistered = false
|
|
885
|
+
yield {
|
|
886
|
+
kind: 'error',
|
|
887
|
+
error: {
|
|
888
|
+
code: 'PROMPT_FAILED',
|
|
889
|
+
message: `omp prompt 失败: ${err instanceof Error ? err.message : String(err)}`,
|
|
890
|
+
},
|
|
891
|
+
}
|
|
892
|
+
return
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// 产出事件直到 done/error
|
|
896
|
+
try {
|
|
897
|
+
while (!isDone || queue.length > 0) {
|
|
898
|
+
if (queue.length === 0 && !isDone) {
|
|
899
|
+
const remaining = idleTimeoutMs - (Date.now() - lastActivity)
|
|
900
|
+
if (remaining <= 0) {
|
|
901
|
+
client.send({ type: 'abort' }).catch(() => {})
|
|
902
|
+
// 子进程大概率已僵死:击杀并清引用,下回合 ensureClient 全新重生
|
|
903
|
+
this.killProcess()
|
|
904
|
+
this.client = null
|
|
905
|
+
this.hostToolsRegistered = false
|
|
906
|
+
enqueue({
|
|
907
|
+
kind: 'error',
|
|
908
|
+
error: {
|
|
909
|
+
code: 'TURN_STALLED',
|
|
910
|
+
message: `回合停滞 ${Math.round(idleTimeoutMs / 1000)}s 无 omp 事件,已中止重置(消息按已处理落账,后续消息继续)`,
|
|
911
|
+
},
|
|
912
|
+
})
|
|
913
|
+
continue
|
|
914
|
+
}
|
|
915
|
+
await new Promise<void>((r) => {
|
|
916
|
+
resolveWait = r
|
|
917
|
+
stallTimer = setTimeout(() => {
|
|
918
|
+
resolveWait = null
|
|
919
|
+
r()
|
|
920
|
+
}, remaining + 100)
|
|
921
|
+
})
|
|
922
|
+
if (stallTimer) {
|
|
923
|
+
clearTimeout(stallTimer)
|
|
924
|
+
stallTimer = null
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
while (queue.length > 0) {
|
|
928
|
+
yield queue.shift()!
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
finally {
|
|
933
|
+
if (stallTimer) clearTimeout(stallTimer)
|
|
934
|
+
// 事件监听必须全部解除:本 client 跨回合长期复用,残留的 mapOmpEvent
|
|
935
|
+
// 闭包会在后续每一帧继续执行并把事件 push 进孤儿队列(内存/CPU 随
|
|
936
|
+
// 回合数线性劣化,直接违背进程常驻的稳定性目标)
|
|
937
|
+
unsubState()
|
|
938
|
+
unsub()
|
|
939
|
+
signal.removeEventListener('abort', onAbort)
|
|
940
|
+
this.turnActive = false
|
|
941
|
+
this.streaming = false
|
|
942
|
+
this.toolState.currentTaskId = null
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// ===== 内部:omp 事件 → AgentEvent 映射 =====
|
|
947
|
+
|
|
948
|
+
private mapOmpEvent(event: AgentSessionEvent, taskId: string | undefined): AgentEvent[] {
|
|
949
|
+
switch (event.type) {
|
|
950
|
+
case 'agent_start':
|
|
951
|
+
return [{
|
|
952
|
+
kind: 'status',
|
|
953
|
+
status: { state: 'WORKING', timestamp: new Date().toISOString() },
|
|
954
|
+
}]
|
|
955
|
+
case 'message_end': {
|
|
956
|
+
// 消息完成:如果有 message 内容,产出为 status 事件(任务历史追踪)
|
|
957
|
+
const msg = event.message
|
|
958
|
+
if (msg && Array.isArray(msg.content)) {
|
|
959
|
+
const text = (msg.content as Array<{ type?: string, text?: string }>)
|
|
960
|
+
.filter(c => c.type === 'text')
|
|
961
|
+
.map(c => c.text ?? '')
|
|
962
|
+
.join('')
|
|
963
|
+
if (text) {
|
|
964
|
+
const a2aMsg: A2AMessage = {
|
|
965
|
+
messageId: randomUUID(),
|
|
966
|
+
contextId: this.channelId,
|
|
967
|
+
role: 'ROLE_AGENT',
|
|
968
|
+
parts: [{ text }],
|
|
969
|
+
}
|
|
970
|
+
return [{
|
|
971
|
+
kind: 'status',
|
|
972
|
+
status: { state: 'WORKING', message: a2aMsg, timestamp: new Date().toISOString() },
|
|
973
|
+
}]
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return []
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
case 'tool_execution_start': {
|
|
980
|
+
const toolName = event.toolName ?? 'tool'
|
|
981
|
+
return [{
|
|
982
|
+
kind: 'status',
|
|
983
|
+
status: {
|
|
984
|
+
state: 'WORKING',
|
|
985
|
+
message: {
|
|
986
|
+
messageId: randomUUID(),
|
|
987
|
+
contextId: this.channelId,
|
|
988
|
+
role: 'ROLE_AGENT',
|
|
989
|
+
parts: [{ text: `🔧 ${toolName}${toolArgsPreview(event.args)}` }],
|
|
990
|
+
},
|
|
991
|
+
timestamp: new Date().toISOString(),
|
|
992
|
+
},
|
|
993
|
+
}]
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
case 'agent_end': {
|
|
997
|
+
if (event.isTerminal === false) return []
|
|
998
|
+
const events: AgentEvent[] = []
|
|
999
|
+
// 提取最终 assistant 文本作为 artifact
|
|
1000
|
+
const messages = event.messages ?? []
|
|
1001
|
+
const assistantText = messages
|
|
1002
|
+
.filter(m => m.role === 'assistant')
|
|
1003
|
+
.flatMap(m => m.content.filter(c => c.type === 'text').map(c => c.text ?? ''))
|
|
1004
|
+
.join('')
|
|
1005
|
+
// 回合内的 provider/API 失败(限额 429、鉴权、上游 5xx 等):omp 把错误挂在最后一条
|
|
1006
|
+
// assistant 消息上(stopReason='error' + errorStatus/errorMessage)而非 __error__ 帧。
|
|
1007
|
+
// 不映射出来,上层只能看到「回合结束无产出」,真实原因彻底丢失(且照常重试到耗尽)。
|
|
1008
|
+
const failure = [...messages].reverse().find((m) => {
|
|
1009
|
+
const mm = m as { role?: string, stopReason?: string, errorMessage?: string }
|
|
1010
|
+
return mm.role === 'assistant' && (mm.stopReason === 'error' || typeof mm.errorMessage === 'string')
|
|
1011
|
+
}) as { errorStatus?: number, errorMessage?: string } | undefined
|
|
1012
|
+
if (failure) {
|
|
1013
|
+
const code = failure.errorStatus != null ? `OMP_LLM_${failure.errorStatus}` : 'OMP_LLM_ERROR'
|
|
1014
|
+
const detail = failure.errorMessage ?? 'harness 回合以错误结束(无错误详情)'
|
|
1015
|
+
log.error(`[OmpRpcAgent:${this.selfAgentId}] harness 回合失败 ${code}: ${detail}`)
|
|
1016
|
+
// error 事件即回合终点(队列侧据此收口),不再追加 done
|
|
1017
|
+
return [{ kind: 'error', error: { code, message: detail } }]
|
|
1018
|
+
}
|
|
1019
|
+
if (assistantText) {
|
|
1020
|
+
events.push({
|
|
1021
|
+
kind: 'artifact',
|
|
1022
|
+
artifact: {
|
|
1023
|
+
artifactId: randomUUID(),
|
|
1024
|
+
name: 'output',
|
|
1025
|
+
parts: [{ text: assistantText }],
|
|
1026
|
+
},
|
|
1027
|
+
lastChunk: true,
|
|
1028
|
+
totalChunks: 1,
|
|
1029
|
+
})
|
|
1030
|
+
}
|
|
1031
|
+
events.push({ kind: 'done', final: taskId ? { taskId } : undefined })
|
|
1032
|
+
return events
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
case '__process_exit__':
|
|
1036
|
+
return [{
|
|
1037
|
+
kind: 'error',
|
|
1038
|
+
error: { code: 'OMP_PROCESS_EXIT', message: 'omp 子进程意外退出' },
|
|
1039
|
+
}]
|
|
1040
|
+
|
|
1041
|
+
// ===== 上下文治理事件(回合中到达的原生阈值/overflow 压缩;不污染事件流)=====
|
|
1042
|
+
case 'compaction_start':
|
|
1043
|
+
this.compacting = true
|
|
1044
|
+
return []
|
|
1045
|
+
|
|
1046
|
+
case 'compaction_end': {
|
|
1047
|
+
this.compacting = false
|
|
1048
|
+
const result = (event as { result?: CompactionResult }).result
|
|
1049
|
+
if (result?.summary) {
|
|
1050
|
+
void this.harvestCompaction(
|
|
1051
|
+
result.summary,
|
|
1052
|
+
result.tokensBefore,
|
|
1053
|
+
result.tokensAfter ?? result.estimatedTokensAfter,
|
|
1054
|
+
(event as { reason?: string }).reason,
|
|
1055
|
+
)
|
|
1056
|
+
}
|
|
1057
|
+
return []
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// 回合完全落定(无自动重试/压缩重试/排队续跑):后续压缩检查由 AgentRuntime 驱动
|
|
1061
|
+
case 'agent_settled':
|
|
1062
|
+
return []
|
|
1063
|
+
|
|
1064
|
+
case '__error__':
|
|
1065
|
+
return [{
|
|
1066
|
+
kind: 'error',
|
|
1067
|
+
error: { code: 'OMP_ERROR', message: (event as { error?: string }).error ?? 'omp 未知错误' },
|
|
1068
|
+
}]
|
|
1069
|
+
|
|
1070
|
+
default:
|
|
1071
|
+
return []
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// ===== 内部:omp 客户端管理 =====
|
|
1076
|
+
|
|
1077
|
+
private async ensureClient(ctx: AgentRunContext): Promise<void> {
|
|
1078
|
+
if (!this.workspace) {
|
|
1079
|
+
this.workspace = ctx.workspace
|
|
1080
|
+
}
|
|
1081
|
+
if (!this.agentInfo) {
|
|
1082
|
+
// init() 可能未调用;从 ctx 推断
|
|
1083
|
+
this.channelId = ctx.channelId
|
|
1084
|
+
this.agentRole = ctx.role
|
|
1085
|
+
this.bridgeCtx.identity = { agentId: this.selfAgentId, channelId: this.channelId, role: this.agentRole, name: this.agentName }
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if (!this.client || !this.client.alive) {
|
|
1089
|
+
// 旧客户端已退出(exit 事件/OS 存活校准 reconcile 触发)——必须丢弃并重生,
|
|
1090
|
+
// 否则后续回合会一直对着死 stdio 报 PROMPT_FAILED 烧重试配额
|
|
1091
|
+
this.client = null
|
|
1092
|
+
this.hostToolsRegistered = false
|
|
1093
|
+
const command = this.config.command ?? 'omp'
|
|
1094
|
+
const client = new OmpRpcClient({
|
|
1095
|
+
command,
|
|
1096
|
+
mode: this.rpcMode,
|
|
1097
|
+
args: [...(this.config.thinkingLevel ? ['--thinking', this.config.thinkingLevel] : [])],
|
|
1098
|
+
args: this.config.args,
|
|
1099
|
+
cwd: this.config.cwd ?? process.cwd(),
|
|
1100
|
+
// 进程退出 → 注册表标记(供运行时资源监控);pid=-1 表示无法取得,忽略
|
|
1101
|
+
onExit: (pid, code) => {
|
|
1102
|
+
if (pid > 0) {
|
|
1103
|
+
markHarnessProcessExit(pid, code)
|
|
1104
|
+
markTerminalSessionExit(pid, code)
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
})
|
|
1108
|
+
await client.start()
|
|
1109
|
+
|
|
1110
|
+
// 登记 + 绑定 agent 身份(harness 进程监控的事实源)
|
|
1111
|
+
const pid = client.pid
|
|
1112
|
+
if (pid) {
|
|
1113
|
+
registerHarnessProcess(pid, {
|
|
1114
|
+
harness: 'omp',
|
|
1115
|
+
command,
|
|
1116
|
+
args: ['--mode', this.rpcMode, ...(this.config.args ?? [])],
|
|
1117
|
+
})
|
|
1118
|
+
bindHarnessProcess(pid, {
|
|
1119
|
+
agentId: this.selfAgentId,
|
|
1120
|
+
channelId: this.channelId,
|
|
1121
|
+
name: this.agentName,
|
|
1122
|
+
role: this.agentRole,
|
|
1123
|
+
})
|
|
1124
|
+
// 终端镜像 tap:全部 RPC 帧 → /monitor 终端(实时 TUI 渲染 + HITL)
|
|
1125
|
+
attachTerminalTap(client, {
|
|
1126
|
+
pid,
|
|
1127
|
+
harness: 'omp',
|
|
1128
|
+
agentId: this.selfAgentId,
|
|
1129
|
+
channelId: this.channelId,
|
|
1130
|
+
name: this.agentName,
|
|
1131
|
+
role: this.agentRole,
|
|
1132
|
+
})
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// 设置模型(如果配置了 provider/model 任一;omp 以当前已设值为缺省补全,
|
|
1136
|
+
// 只配其一时也显式发送,避免 respawn 后静默回落默认模型)
|
|
1137
|
+
if (this.config.provider || this.config.model) {
|
|
1138
|
+
try {
|
|
1139
|
+
// 未配置一侧保持 undefined(JSON 序列化丢键)→ omp 以当前已设值补全
|
|
1140
|
+
await client.send({
|
|
1141
|
+
type: 'set_model',
|
|
1142
|
+
provider: this.config.provider as string,
|
|
1143
|
+
modelId: this.config.model as string,
|
|
1144
|
+
})
|
|
1145
|
+
}
|
|
1146
|
+
catch {
|
|
1147
|
+
// 模型设置失败不致命(用 omp 默认模型)
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// 注册 host tools(按角色差异化:lead 全量,worker 剔除调度/团队管理专属工具)
|
|
1152
|
+
client.onHostToolCall(req => this.handleHostTool(req))
|
|
1153
|
+
await client.send({ type: 'set_host_tools', tools: hostToolsForRole(this.agentRole) })
|
|
1154
|
+
|
|
1155
|
+
// 上下文治理探测(feature-detect 一次;失败退化被动 usage 跟踪)+ 原生压缩兜底保持开启
|
|
1156
|
+
void this.probeContext(client).catch(() => {})
|
|
1157
|
+
void client.send({ type: 'set_auto_compaction', enabled: true }).catch(() => {})
|
|
1158
|
+
|
|
1159
|
+
this.client = client
|
|
1160
|
+
this.hostToolsRegistered = true
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/** 上下文状态探测:get_state 取 contextWindow/sessionId(一次;失败仅损失 percent 精度) */
|
|
1165
|
+
private async probeContext(client: OmpRpcClient): Promise<void> {
|
|
1166
|
+
try {
|
|
1167
|
+
const resp = await client.send({ type: 'get_state' })
|
|
1168
|
+
const data = (resp.data ?? {}) as Record<string, unknown>
|
|
1169
|
+
const model = data.model as { contextWindow?: number } | undefined
|
|
1170
|
+
if (typeof model?.contextWindow === 'number' && model.contextWindow > 0) {
|
|
1171
|
+
this.contextWindow = model.contextWindow
|
|
1172
|
+
client.setContextWindow(model.contextWindow)
|
|
1173
|
+
}
|
|
1174
|
+
if (typeof data.sessionId === 'string' && data.sessionId) this.sessionId = data.sessionId
|
|
1175
|
+
}
|
|
1176
|
+
catch { /* get_state 不可用:仅被动 usage */ }
|
|
1177
|
+
}
|
|
1178
|
+
}
|