agentworkshop 0.2.1
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 +631 -0
- package/.AgentWorkShop/prompts/lead-supervise.md +42 -0
- package/.AgentWorkShop/prompts/mode-goal.md +26 -0
- package/.AgentWorkShop/prompts/mode-loop.md +10 -0
- package/.AgentWorkShop/prompts/mode-pipeline.md +16 -0
- package/.AgentWorkShop/prompts/peer-lead-roster.md +3 -0
- package/.AgentWorkShop/prompts/peer-message.md +14 -0
- package/.AgentWorkShop/prompts/peer-reply-optional.md +2 -0
- package/.AgentWorkShop/prompts/peer-reply-required.md +8 -0
- package/.AgentWorkShop/prompts/scenario-default.md +11 -0
- package/.AgentWorkShop/prompts/system-manual.md +18 -0
- package/.AgentWorkShop/prompts/team-roster.md +9 -0
- package/.AgentWorkShop/prompts/worker-workflow.md +19 -0
- package/.env.example +34 -0
- package/README-zh.md +424 -0
- package/README.md +424 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.json +1 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/1459e6269990a5d229b9228c8b2f62ffaa7d350068380ae8b875fee0db45cca6.source +180 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/c2991e909c70c5c12b90a407c7adcf63384c51668a2b334e6e047fa675c268fe.source +305 -0
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/e09129039d1e27e3fac24becc9bdafd2d1a7c8049bff24173df007be865f0165.source +264 -0
- package/app/.mimosa/hook-status/sess_05189059-e42d-4188-8911-4f7baabe009b-28ef7c75ea.json +14 -0
- package/app/app.vue +139 -0
- package/app/assets/css/main.css +1257 -0
- package/app/components/AppHeader.vue +675 -0
- package/app/components/AppSidebar.vue +286 -0
- package/app/components/AwChart.vue +44 -0
- package/app/components/AwLogo.vue +75 -0
- package/app/components/workshop/A2aRpcDebugger.vue +237 -0
- package/app/components/workshop/AgentInspectorDrawer.vue +424 -0
- package/app/components/workshop/AgentLanesView.vue +947 -0
- package/app/components/workshop/ArtifactCard.vue +120 -0
- package/app/components/workshop/AssetLibrary.vue +450 -0
- package/app/components/workshop/ChannelSessionList.vue +593 -0
- package/app/components/workshop/CommandPalette.vue +270 -0
- package/app/components/workshop/Composer.vue +731 -0
- package/app/components/workshop/DeviceTwinPanel.vue +607 -0
- package/app/components/workshop/EventBlock.vue +602 -0
- package/app/components/workshop/FileSelectorModal.vue +188 -0
- package/app/components/workshop/InspectorPanel.vue +265 -0
- package/app/components/workshop/LiveAgentBar.vue +362 -0
- package/app/components/workshop/MemoryPanel.vue +364 -0
- package/app/components/workshop/MentionHoverCard.vue +155 -0
- package/app/components/workshop/MultiChannelView.vue +189 -0
- package/app/components/workshop/PaneSplitter.vue +142 -0
- package/app/components/workshop/StatsPanel.vue +168 -0
- package/app/components/workshop/TaskBoardView.vue +636 -0
- package/app/components/workshop/TaskInspectorDrawer.vue +390 -0
- package/app/components/workshop/TranscriptTimeline.vue +545 -0
- package/app/components/workshop/blocks/ClusterArtifact.vue +77 -0
- package/app/components/workshop/blocks/ClusterError.vue +85 -0
- package/app/components/workshop/blocks/ClusterLife.vue +87 -0
- package/app/components/workshop/blocks/ClusterMember.vue +93 -0
- package/app/components/workshop/blocks/ClusterMemory.vue +84 -0
- package/app/components/workshop/blocks/ClusterOther.vue +67 -0
- package/app/components/workshop/blocks/ClusterRoute.vue +359 -0
- package/app/components/workshop/blocks/ClusterStatus.vue +147 -0
- package/app/components/workshop/blocks/ClusterStream.vue +325 -0
- package/app/components/workshop/blocks/ClusterTask.vue +151 -0
- package/app/components/workshop/blocks/ClusterTool.vue +158 -0
- package/app/components/workshop/lanes/LaneBlocks.vue +149 -0
- package/app/components/workshop/terminal/OmpTerminalPanel.vue +1010 -0
- package/app/components/workshop/town/ModelPreview3D.vue +276 -0
- package/app/components/workshop/town/TownScene.ts +1355 -0
- package/app/components/workshop/town/TownScene3D.ts +4533 -0
- package/app/components/workshop/town/TownView.vue +7071 -0
- package/app/composables/useCodeCopy.ts +30 -0
- package/app/composables/useHttp.ts +8 -0
- package/app/composables/useRouteMeta.ts +50 -0
- package/app/composables/useSiteConfig.ts +21 -0
- package/app/composables/workshop/apiClient.ts +95 -0
- package/app/composables/workshop/useCharacterAssets.ts +292 -0
- package/app/composables/workshop/useClusteredBlocks.ts +150 -0
- package/app/composables/workshop/useComposerBus.ts +16 -0
- package/app/composables/workshop/useDaqStream.ts +379 -0
- package/app/composables/workshop/useDcwStream.ts +300 -0
- package/app/composables/workshop/useDeviceTwins.ts +147 -0
- package/app/composables/workshop/useEventBlocks.ts +662 -0
- package/app/composables/workshop/useLocalTime.ts +33 -0
- package/app/composables/workshop/useOpsLog.ts +164 -0
- package/app/composables/workshop/useSceneLayouts.ts +166 -0
- package/app/composables/workshop/useTownBus.ts +47 -0
- package/app/composables/workshop/useVisibleInterval.ts +42 -0
- package/app/composables/workshop/useWorkshopApi.ts +224 -0
- package/app/composables/workshop/useWorkshopWs.ts +113 -0
- package/app/config/index.ts +66 -0
- package/app/config/schema.ts +68 -0
- package/app/error.vue +76 -0
- package/app/layouts/default.vue +100 -0
- package/app/layouts/town.vue +16 -0
- package/app/pages/daq/[id].vue +796 -0
- package/app/pages/daq/index.vue +2577 -0
- package/app/pages/dcw/[id].vue +2259 -0
- package/app/pages/dcw/index.vue +1079 -0
- package/app/pages/index.vue +830 -0
- package/app/pages/logs/index.vue +621 -0
- package/app/pages/monitor.vue +665 -0
- package/app/pages/settings.vue +757 -0
- package/app/pages/tokens.vue +610 -0
- package/app/pages/town.vue +145 -0
- package/app/pages/users.vue +257 -0
- package/app/pages/workshop/agents.vue +382 -0
- package/app/pages/workshop/channel-templates.vue +456 -0
- package/app/pages/workshop/index.vue +604 -0
- package/app/pages/workshop/teams.vue +448 -0
- package/app/pages/workshop/w/[wsId].vue +516 -0
- package/app/plugins/http.ts +71 -0
- package/app/plugins/locale-restore.client.ts +13 -0
- package/app/plugins/runtime-config.client.ts +65 -0
- package/app/plugins/session-restore.client.ts +24 -0
- package/app/plugins/theme.client.ts +15 -0
- package/app/stores/app.ts +32 -0
- package/app/stores/route-trail.ts +45 -0
- package/app/stores/runtime-config.ts +212 -0
- package/app/stores/workshop/connection.ts +184 -0
- package/app/stores/workshop/entities.ts +350 -0
- package/app/stores/workshop/events.ts +292 -0
- package/app/stores/workshop/user.ts +176 -0
- package/app/stores/workshop/workspaces.ts +133 -0
- package/bin/aw.mjs +13 -0
- package/cli/aw.mjs +184 -0
- package/cli/commands/build.mjs +33 -0
- package/cli/commands/config.mjs +157 -0
- package/cli/commands/dev.mjs +74 -0
- package/cli/commands/doctor.mjs +120 -0
- package/cli/commands/home.mjs +75 -0
- package/cli/commands/init.mjs +150 -0
- package/cli/commands/register.mjs +123 -0
- package/cli/commands/start.mjs +106 -0
- package/cli/commands/status.mjs +67 -0
- package/cli/commands/update.mjs +116 -0
- package/cli/commands/version.mjs +32 -0
- package/cli/core/args.mjs +95 -0
- package/cli/core/context.mjs +264 -0
- package/cli/core/errors.mjs +18 -0
- package/cli/core/help.mjs +68 -0
- package/cli/core/logger.mjs +40 -0
- package/cli/core/registry.mjs +111 -0
- package/config.yml +70 -0
- package/docker-compose.yml +34 -0
- package/docs/cli.md +220 -0
- package/i18n/dicts/_en.json +1097 -0
- package/i18n/dicts/_en2.json +300 -0
- package/i18n/dicts/_en3.json +102 -0
- package/i18n/dicts/_en4.json +115 -0
- package/i18n/dicts/_en5.json +33 -0
- package/i18n/dicts/a2aRpcDebugger.json +10 -0
- package/i18n/dicts/agentInspectorDrawer.json +25 -0
- package/i18n/dicts/agentLanesView.json +57 -0
- package/i18n/dicts/agents.json +27 -0
- package/i18n/dicts/appHeader.json +7 -0
- package/i18n/dicts/appSidebar.json +1 -0
- package/i18n/dicts/artifactCard.json +6 -0
- package/i18n/dicts/assetLibrary.json +18 -0
- package/i18n/dicts/awChart.json +1 -0
- package/i18n/dicts/awLogo.json +1 -0
- package/i18n/dicts/channelSessionList.json +45 -0
- package/i18n/dicts/clusterArtifact.json +4 -0
- package/i18n/dicts/clusterError.json +3 -0
- package/i18n/dicts/clusterLife.json +5 -0
- package/i18n/dicts/clusterMember.json +3 -0
- package/i18n/dicts/clusterMemory.json +3 -0
- package/i18n/dicts/clusterOther.json +3 -0
- package/i18n/dicts/clusterRoute.json +15 -0
- package/i18n/dicts/clusterStatus.json +3 -0
- package/i18n/dicts/clusterStream.json +3 -0
- package/i18n/dicts/clusterTask.json +10 -0
- package/i18n/dicts/clusterTool.json +3 -0
- package/i18n/dicts/commandPalette.json +16 -0
- package/i18n/dicts/composer.json +38 -0
- package/i18n/dicts/ctpl.json +32 -0
- package/i18n/dicts/daq.json +157 -0
- package/i18n/dicts/daqDetail.json +55 -0
- package/i18n/dicts/dcw.json +68 -0
- package/i18n/dicts/dcwDetail.json +199 -0
- package/i18n/dicts/deviceTwinPanel.json +12 -0
- package/i18n/dicts/eventBlock.json +8 -0
- package/i18n/dicts/fileSelectorModal.json +6 -0
- package/i18n/dicts/inspectorPanel.json +12 -0
- package/i18n/dicts/laneBlocks.json +6 -0
- package/i18n/dicts/liveAgentBar.json +8 -0
- package/i18n/dicts/logs.json +39 -0
- package/i18n/dicts/memoryPanel.json +20 -0
- package/i18n/dicts/mentionHoverCard.json +4 -0
- package/i18n/dicts/modelPreview3D.json +1 -0
- package/i18n/dicts/monitor.json +5 -0
- package/i18n/dicts/multiChannelView.json +13 -0
- package/i18n/dicts/ompTerminalPanel.json +7 -0
- package/i18n/dicts/paneSplitter.json +3 -0
- package/i18n/dicts/settings.json +1 -0
- package/i18n/dicts/statsPanel.json +6 -0
- package/i18n/dicts/taskBoardView.json +22 -0
- package/i18n/dicts/taskInspectorDrawer.json +19 -0
- package/i18n/dicts/teams.json +33 -0
- package/i18n/dicts/tokens.json +30 -0
- package/i18n/dicts/town.json +7 -0
- package/i18n/dicts/townView.json +209 -0
- package/i18n/dicts/transcriptTimeline.json +31 -0
- package/i18n/dicts/users.json +15 -0
- package/i18n/dicts/wsHome.json +33 -0
- package/i18n/dicts/wsView.json +15 -0
- package/i18n/locales/en.ts +1737 -0
- package/i18n/locales/zh-CN.ts +1736 -0
- package/nuxt.config.ts +209 -0
- package/package.json +123 -0
- package/public/assets/game/character/bot.png +0 -0
- package/public/assets/game/character/device-3d.glb +0 -0
- package/public/assets/game/character/hero-3d.glb +0 -0
- package/public/assets/game/character/hero-anime-1.glb +0 -0
- package/public/assets/game/character/hero-anime-2.glb +0 -0
- package/public/assets/game/character/hero-anime-3.glb +0 -0
- package/public/assets/game/character/hero-anime-4.glb +0 -0
- package/public/assets/game/character/knight.png +0 -0
- package/public/assets/game/character/mage.png +0 -0
- package/public/assets/game/coin.png +0 -0
- package/public/assets/game/devices/agv.glb +0 -0
- package/public/assets/game/devices/caster.glb +0 -0
- package/public/assets/game/devices/device-console.glb +0 -0
- package/public/assets/game/devices/device-robot-arm.glb +0 -0
- package/public/assets/game/devices/device-scanner.glb +0 -0
- package/public/assets/game/devices/extruder.glb +0 -0
- package/public/assets/game/devices/mdo.glb +0 -0
- package/public/assets/game/devices/power-cabinet.glb +0 -0
- package/public/assets/game/devices/pump.glb +0 -0
- package/public/assets/game/devices/tdo.glb +0 -0
- package/public/assets/game/devices/thickness-scanner.glb +0 -0
- package/public/assets/game/devices/winder.glb +0 -0
- package/public/assets/game/npc/boltnu-sheet.png +0 -0
- package/public/assets/game/npc/fluttaflap-sheet.png +0 -0
- package/public/assets/game/npc/rockitten-sheet.png +0 -0
- package/public/assets/game/npc/tux-sheet.png +0 -0
- package/public/assets/game/player/misa-back-walk.000.png +0 -0
- package/public/assets/game/player/misa-back-walk.001.png +0 -0
- package/public/assets/game/player/misa-back-walk.002.png +0 -0
- package/public/assets/game/player/misa-back-walk.003.png +0 -0
- package/public/assets/game/player/misa-back.png +0 -0
- package/public/assets/game/player/misa-front-walk.000.png +0 -0
- package/public/assets/game/player/misa-front-walk.001.png +0 -0
- package/public/assets/game/player/misa-front-walk.002.png +0 -0
- package/public/assets/game/player/misa-front-walk.003.png +0 -0
- package/public/assets/game/player/misa-front.png +0 -0
- package/public/assets/game/player/misa-left-walk.000.png +0 -0
- package/public/assets/game/player/misa-left-walk.001.png +0 -0
- package/public/assets/game/player/misa-left-walk.002.png +0 -0
- package/public/assets/game/player/misa-left-walk.003.png +0 -0
- package/public/assets/game/player/misa-left.png +0 -0
- package/public/assets/game/player/misa-right-walk.000.png +0 -0
- package/public/assets/game/player/misa-right-walk.001.png +0 -0
- package/public/assets/game/player/misa-right-walk.002.png +0 -0
- package/public/assets/game/player/misa-right-walk.003.png +0 -0
- package/public/assets/game/player/misa-right.png +0 -0
- package/public/assets/game/player-sheet.png +0 -0
- package/public/assets/game/tuxemon-town.json +1 -0
- package/public/assets/game/tuxmon-sample-32px-extruded.png +0 -0
- package/public/assets/game/wuwa/scene-map.png +0 -0
- package/public/assets/game/wuwa/world-far.png +0 -0
- package/public/assets/game/wuwa/world-middle.png +0 -0
- package/public/assets/game/wuwa/world-near.png +0 -0
- package/public/assets/game/wuwa/wu-aura.png +0 -0
- package/public/assets/game/wuwa/wu-lead.png +0 -0
- package/public/assets/game/wuwa/wu-ring.png +0 -0
- package/public/assets/game/wuwa/wu-slash.png +0 -0
- package/public/assets/game/wuwa/wu-worker-0.png +0 -0
- package/public/assets/game/wuwa/wu-worker-1.png +0 -0
- package/public/assets/game/wuwa/wu-worker-2.png +0 -0
- package/public/favicon.svg +14 -0
- package/public/scene/background/cyber-town-background.svg +110 -0
- package/scripts/_daq-realtime-probe.mjs +68 -0
- package/scripts/_daq-warn-probe.mjs +50 -0
- package/scripts/_dbg-agent-tools-audit.mjs +154 -0
- package/scripts/_dbg-agent-workflow-audit.mjs +177 -0
- package/scripts/_dbg-anim-verify.mjs +81 -0
- package/scripts/_dbg-audit-all-pages.mjs +43 -0
- package/scripts/_dbg-autoframe.mjs +103 -0
- package/scripts/_dbg-beacon-final.mjs +181 -0
- package/scripts/_dbg-bind-bidi-e2e.mjs +239 -0
- package/scripts/_dbg-callout-near.mjs +108 -0
- package/scripts/_dbg-chan-focus.mjs +49 -0
- package/scripts/_dbg-cleanup.mjs +31 -0
- package/scripts/_dbg-closedloop-ui-shot.mjs +61 -0
- package/scripts/_dbg-console-audit.mjs +61 -0
- package/scripts/_dbg-console-probe.mjs +23 -0
- package/scripts/_dbg-context-inject-audit.mjs +231 -0
- package/scripts/_dbg-crash-recovery.mjs +143 -0
- package/scripts/_dbg-crop-shot.mjs +16 -0
- package/scripts/_dbg-daq-acc-probe.mjs +24 -0
- package/scripts/_dbg-daq-attrib.mjs +34 -0
- package/scripts/_dbg-daq-bind-audit.mjs +119 -0
- package/scripts/_dbg-daq-cadence.mjs +104 -0
- package/scripts/_dbg-daq-debug.mjs +22 -0
- package/scripts/_dbg-daq-diag.mjs +33 -0
- package/scripts/_dbg-daq-e2e.mjs +171 -0
- package/scripts/_dbg-daq-film.mjs +136 -0
- package/scripts/_dbg-daq-filter-e2e.mjs +109 -0
- package/scripts/_dbg-daq-filter-e2e2.mjs +62 -0
- package/scripts/_dbg-daq-final-regression.mjs +78 -0
- package/scripts/_dbg-daq-full-check.mjs +179 -0
- package/scripts/_dbg-daq-line-note-verify.mjs +61 -0
- package/scripts/_dbg-daq-linebanner-audit.mjs +107 -0
- package/scripts/_dbg-daq-pages-shot.mjs +37 -0
- package/scripts/_dbg-daq-perf.mjs +61 -0
- package/scripts/_dbg-daq-probe.mjs +27 -0
- package/scripts/_dbg-daq-recipe-e2e.mjs +30 -0
- package/scripts/_dbg-daq-redesign-shot.mjs +88 -0
- package/scripts/_dbg-daq-rows-probe.mjs +26 -0
- package/scripts/_dbg-daq-rowstate-e2e.mjs +152 -0
- package/scripts/_dbg-daq-rowstate-shot.mjs +45 -0
- package/scripts/_dbg-daq-rowstate-shot2.mjs +38 -0
- package/scripts/_dbg-daq-tpl.mjs +144 -0
- package/scripts/_dbg-dash-console-check.mjs +49 -0
- package/scripts/_dbg-dash-deep-probe.mjs +61 -0
- package/scripts/_dbg-dash-hitpoint.mjs +44 -0
- package/scripts/_dbg-dash-live-shot.mjs +36 -0
- package/scripts/_dbg-dash-shot.mjs +21 -0
- package/scripts/_dbg-dashboard-shot.mjs +35 -0
- package/scripts/_dbg-dcw-audit.mjs +126 -0
- package/scripts/_dbg-dcw-card-e2e.mjs +108 -0
- package/scripts/_dbg-dcw-channel-ui.mjs +144 -0
- package/scripts/_dbg-dcw-examples.mjs +81 -0
- package/scripts/_dbg-dcw-light-shot.mjs +19 -0
- package/scripts/_dbg-dcw-line-audit.mjs +107 -0
- package/scripts/_dbg-dcw-pause-tpl-audit.mjs +161 -0
- package/scripts/_dbg-dcw-pause-ui.mjs +175 -0
- package/scripts/_dbg-dcw-scene.mjs +72 -0
- package/scripts/_dbg-dcw-shot2.mjs +14 -0
- package/scripts/_dbg-dcw-twin-e2e.mjs +192 -0
- package/scripts/_dbg-dt-interact.mjs +193 -0
- package/scripts/_dbg-error-probe.mjs +54 -0
- package/scripts/_dbg-filter-light-verify.mjs +54 -0
- package/scripts/_dbg-final-acceptance-2.mjs +72 -0
- package/scripts/_dbg-final-acceptance.mjs +192 -0
- package/scripts/_dbg-final-agent-e2e.mjs +151 -0
- package/scripts/_dbg-final-full-e2e.mjs +218 -0
- package/scripts/_dbg-final-line-e2e.mjs +173 -0
- package/scripts/_dbg-final-t6-hitl.mjs +38 -0
- package/scripts/_dbg-focus-range.mjs +115 -0
- package/scripts/_dbg-fresh-scene-e2e.mjs +125 -0
- package/scripts/_dbg-full-feature-e2e.mjs +219 -0
- package/scripts/_dbg-fullchain-e2e.mjs +178 -0
- package/scripts/_dbg-gate-debug.mjs +37 -0
- package/scripts/_dbg-gif-encode.mjs +111 -0
- package/scripts/_dbg-gizmo.mjs +95 -0
- package/scripts/_dbg-glass-probe.mjs +57 -0
- package/scripts/_dbg-glass-shot.mjs +50 -0
- package/scripts/_dbg-glass-sweep.mjs +25 -0
- package/scripts/_dbg-glb-probe.mjs +43 -0
- package/scripts/_dbg-hardening-probe.mjs +90 -0
- package/scripts/_dbg-hero-btn-probe.mjs +21 -0
- package/scripts/_dbg-hidden-check.mjs +21 -0
- package/scripts/_dbg-hitl-timeout-e2e.mjs +93 -0
- package/scripts/_dbg-hmi-audit.mjs +164 -0
- package/scripts/_dbg-home-repro.mjs +24 -0
- package/scripts/_dbg-home-stack.mjs +13 -0
- package/scripts/_dbg-huashu-survey.mjs +31 -0
- package/scripts/_dbg-huashu-survey2.mjs +35 -0
- package/scripts/_dbg-huashu-verify.mjs +25 -0
- package/scripts/_dbg-huashu-verify2.mjs +26 -0
- package/scripts/_dbg-hydration-detail.mjs +18 -0
- package/scripts/_dbg-i18n-debug.mjs +27 -0
- package/scripts/_dbg-i18n-debug2.mjs +26 -0
- package/scripts/_dbg-i18n-final.mjs +42 -0
- package/scripts/_dbg-i18n-scan.mjs +38 -0
- package/scripts/_dbg-infra-lifecycle.mjs +44 -0
- package/scripts/_dbg-line-aura-shots.mjs +54 -0
- package/scripts/_dbg-line-control-e2e.mjs +120 -0
- package/scripts/_dbg-line-gate-audit.mjs +108 -0
- package/scripts/_dbg-line-live-e2e.mjs +144 -0
- package/scripts/_dbg-line-scenario-e2e.mjs +256 -0
- package/scripts/_dbg-minimal-omp.mjs +77 -0
- package/scripts/_dbg-mode-nav.mjs +173 -0
- package/scripts/_dbg-mqtt-chain-e2e.mjs +90 -0
- package/scripts/_dbg-nav-rpg.mjs +84 -0
- package/scripts/_dbg-nav-shot.mjs +28 -0
- package/scripts/_dbg-nav.mjs +27 -0
- package/scripts/_dbg-opslog-e2e.mjs +121 -0
- package/scripts/_dbg-opslog-shots.mjs +62 -0
- package/scripts/_dbg-opt-team-e2e.mjs +133 -0
- package/scripts/_dbg-optimization-e2e.mjs +94 -0
- package/scripts/_dbg-orbit.mjs +58 -0
- package/scripts/_dbg-panesplitter-fix-check.mjs +62 -0
- package/scripts/_dbg-phase-e3-monitor.mjs +25 -0
- package/scripts/_dbg-pop-clamp-verify.mjs +77 -0
- package/scripts/_dbg-preset-e2e.mjs +116 -0
- package/scripts/_dbg-preview-console.mjs +28 -0
- package/scripts/_dbg-preview-minrepro.mjs +156 -0
- package/scripts/_dbg-preview-pixels.mjs +93 -0
- package/scripts/_dbg-quality-pages-e2e.mjs +84 -0
- package/scripts/_dbg-query-cancel.ts +12 -0
- package/scripts/_dbg-rail-tree-e2e.mjs +183 -0
- package/scripts/_dbg-real-driver-e2e.mjs +55 -0
- package/scripts/_dbg-redesign-shots.mjs +34 -0
- package/scripts/_dbg-restart-persist.mjs +41 -0
- package/scripts/_dbg-rollback-mgr-audit.mjs +169 -0
- package/scripts/_dbg-rpg.mjs +71 -0
- package/scripts/_dbg-sc001-dispatch.mjs +63 -0
- package/scripts/_dbg-scenario-final-shot.mjs +24 -0
- package/scripts/_dbg-scenario-phase-e2.mjs +75 -0
- package/scripts/_dbg-scenario-phase-e3.mjs +96 -0
- package/scripts/_dbg-scenario-phase1.mjs +90 -0
- package/scripts/_dbg-scenario-phase2.mjs +105 -0
- package/scripts/_dbg-scenario-phase3-monitor.mjs +48 -0
- package/scripts/_dbg-scenario-phase3.mjs +111 -0
- package/scripts/_dbg-scene-polish-shot.mjs +118 -0
- package/scripts/_dbg-selring-probe.mjs +47 -0
- package/scripts/_dbg-sideinfo.mjs +101 -0
- package/scripts/_dbg-splitter.mjs +88 -0
- package/scripts/_dbg-team-repro.mjs +76 -0
- package/scripts/_dbg-town-alarm-e2e.mjs +40 -0
- package/scripts/_dbg-town-beauty-shots.mjs +54 -0
- package/scripts/_dbg-town-daq-shot.mjs +26 -0
- package/scripts/_dbg-town-full-e2e.mjs +104 -0
- package/scripts/_dbg-town-gate.mjs +44 -0
- package/scripts/_dbg-town-ins-probe.mjs +37 -0
- package/scripts/_dbg-town-perf.mjs +50 -0
- package/scripts/_dbg-town-polish-shots.mjs +82 -0
- package/scripts/_dbg-town-realtime.mjs +42 -0
- package/scripts/_dbg-town-shot3.mjs +17 -0
- package/scripts/_dbg-town-truth.mjs +37 -0
- package/scripts/_dbg-town-truth2.mjs +27 -0
- package/scripts/_dbg-transform-audit.mjs +98 -0
- package/scripts/_dbg-trend-expand-test.mjs +38 -0
- package/scripts/_dbg-twindaq-focus.mjs +81 -0
- package/scripts/_dbg-ui-polish-shot.mjs +43 -0
- package/scripts/_dbg-ui-polish-verify1.mjs +92 -0
- package/scripts/_dbg-ui-polish-verify2.mjs +50 -0
- package/scripts/_dbg-ui-polish-verify3.mjs +51 -0
- package/scripts/_dbg-ui-polish-verify4.mjs +50 -0
- package/scripts/_dbg-ui-shot.mjs +26 -0
- package/scripts/_dbg-unify-shot.mjs +43 -0
- package/scripts/_dbg-uplink-probe.mjs +36 -0
- package/scripts/_dbg-wizard-preview-shot.mjs +58 -0
- package/scripts/_dbg-wizard-ui-e2e.mjs +93 -0
- package/scripts/_dbg-ws-probe.mjs +86 -0
- package/scripts/_dbg-ws-shot2.mjs +23 -0
- package/scripts/_dbg-wsdot-sanity.mjs +25 -0
- package/scripts/_dev-stack.mjs +46 -0
- package/scripts/_http-driver-probe.mjs +30 -0
- package/scripts/_lib-dcw-line.mjs +24 -0
- package/scripts/_mqtt-driver-probe.mjs +30 -0
- package/scripts/_newdriver-rest-probe.mjs +100 -0
- package/scripts/_node-state-probe.mjs +5 -0
- package/scripts/_opcua-driver-probe.mjs +31 -0
- package/scripts/_rtu-debug.mjs +41 -0
- package/scripts/_rtu-direct.mjs +6 -0
- package/scripts/_rtu-driver-probe.mjs +53 -0
- package/scripts/_rtu-mini-slave.mjs +63 -0
- package/scripts/_townview_style.css +1185 -0
- package/scripts/_townview_template.html +1215 -0
- package/scripts/_wizard-shots.mjs +90 -0
- package/scripts/acceptance-suite.py +207 -0
- package/scripts/api-live-e2e.mjs +467 -0
- package/scripts/build-anime-models.mjs +339 -0
- package/scripts/build-character-models.mjs +175 -0
- package/scripts/build-film-devices.mjs +227 -0
- package/scripts/build-model-3d.mjs +173 -0
- package/scripts/build-player-sheet.mjs +58 -0
- package/scripts/build-world-map.mjs +188 -0
- package/scripts/build-wuwa-assets.mjs +345 -0
- package/scripts/collab-live-test.py +177 -0
- package/scripts/debug-mcp-http.ts +64 -0
- package/scripts/demo-lead-worker.ts +256 -0
- package/scripts/demo-modes.ts +415 -0
- package/scripts/demo-omp-channel.ts +135 -0
- package/scripts/design-preflight.mjs +223 -0
- package/scripts/design-verify-templates.mjs +110 -0
- package/scripts/design-verify.mjs +170 -0
- package/scripts/dev-guard.mjs +74 -0
- package/scripts/dev-modbus-simulator.mjs +55 -0
- package/scripts/dev-opcua-simulator.mjs +57 -0
- package/scripts/dev-protocol-simulators.mjs +145 -0
- package/scripts/dev-window.cmd +6 -0
- package/scripts/device-line-restore.mjs +34 -0
- package/scripts/diag-lead-status.ts +101 -0
- package/scripts/e2e-a2a-steer-live.mjs +193 -0
- package/scripts/e2e-agent-channel-task.ts +151 -0
- package/scripts/e2e-agent-team.mjs +168 -0
- package/scripts/e2e-auth-matrix.mjs +119 -0
- package/scripts/e2e-channel-isolation.ts +325 -0
- package/scripts/e2e-dual-drive-comm.ts +259 -0
- package/scripts/e2e-lead-team-mgmt.ts +217 -0
- package/scripts/e2e-memory-persistence.ts +320 -0
- package/scripts/e2e-memory-system.ts +332 -0
- package/scripts/e2e-omp-modes.ts +274 -0
- package/scripts/e2e-omp-realtime.ts +258 -0
- package/scripts/e2e-omp-workspace.ts +180 -0
- package/scripts/e2e-parallel-execution.mjs +151 -0
- package/scripts/e2e-rest-memory.mjs +251 -0
- package/scripts/e2e-rest-robustness.mjs +177 -0
- package/scripts/e2e-resume-crash.mjs +254 -0
- package/scripts/e2e-tab-close.mjs +136 -0
- package/scripts/e2e-task-queue.ts +277 -0
- package/scripts/e2e-template-isolation.mjs +267 -0
- package/scripts/e2e-token-ui.mjs +149 -0
- package/scripts/final-soup-run.py +192 -0
- package/scripts/final-soup-watch.py +83 -0
- package/scripts/home-bootstrap.mjs +117 -0
- package/scripts/i18n-codemod.mjs +173 -0
- package/scripts/i18n-fix-en.py +28 -0
- package/scripts/i18n-fix-en2.py +31 -0
- package/scripts/i18n-merge.mjs +67 -0
- package/scripts/omp-real-test.py +328 -0
- package/scripts/smoke-optimization-verify.ts +72 -0
- package/scripts/start.mjs +60 -0
- package/scripts/test-a2a-relay.mjs +222 -0
- package/scripts/test-a2a-types.ts +296 -0
- package/scripts/test-agent-brain.ts +135 -0
- package/scripts/test-agent-multi-channel.ts +160 -0
- package/scripts/test-agent-runtime.ts +348 -0
- package/scripts/test-api-client.ts +108 -0
- package/scripts/test-api-suite.mjs +271 -0
- package/scripts/test-block-consumption.mjs +194 -0
- package/scripts/test-channel-routing.ts +301 -0
- package/scripts/test-cmd-render.mjs +218 -0
- package/scripts/test-collab-e2e.mjs +136 -0
- package/scripts/test-collab-omp.mjs +192 -0
- package/scripts/test-dedup-logic.ts +132 -0
- package/scripts/test-dual-drive.ts +227 -0
- package/scripts/test-e2e-collaboration.ts +262 -0
- package/scripts/test-e2e-monitor.ts +192 -0
- package/scripts/test-event-history.ts +112 -0
- package/scripts/test-event-persistence.mjs +169 -0
- package/scripts/test-exec-modes.ts +316 -0
- package/scripts/test-fifo-realtime.mjs +193 -0
- package/scripts/test-final-e2e.mjs +290 -0
- package/scripts/test-full-system.ts +439 -0
- package/scripts/test-game-session.ts +160 -0
- package/scripts/test-goal-summary.ts +202 -0
- package/scripts/test-harness-liveness.ts +109 -0
- package/scripts/test-harness-terminal.ts +164 -0
- package/scripts/test-header-trail.mjs +95 -0
- package/scripts/test-hitl.ts +360 -0
- package/scripts/test-koda-restyle.mjs +105 -0
- package/scripts/test-lane-history-live.mjs +103 -0
- package/scripts/test-lane-raw.ts +67 -0
- package/scripts/test-lanes-history-render.mjs +138 -0
- package/scripts/test-loop-omp.mjs +154 -0
- package/scripts/test-mcp-tools.ts +285 -0
- package/scripts/test-mdlite.ts +53 -0
- package/scripts/test-memory-dynamic.ts +99 -0
- package/scripts/test-memory-maintenance.ts +117 -0
- package/scripts/test-memory-vector.ts +154 -0
- package/scripts/test-memory.ts +431 -0
- package/scripts/test-mock-collab.mjs +226 -0
- package/scripts/test-modern-render.mjs +174 -0
- package/scripts/test-omp-terminal.mjs +187 -0
- package/scripts/test-orchestration.ts +425 -0
- package/scripts/test-persistence-lazy.ts +323 -0
- package/scripts/test-prompt-system.mjs +181 -0
- package/scripts/test-prompts-loader.ts +56 -0
- package/scripts/test-protocol.ts +156 -0
- package/scripts/test-real-scenario.mjs +290 -0
- package/scripts/test-route-reason.mjs +111 -0
- package/scripts/test-scheduler-loop.ts +469 -0
- package/scripts/test-stream-render.ts +37 -0
- package/scripts/test-task-drawer.mjs +204 -0
- package/scripts/test-task-engine.ts +433 -0
- package/scripts/test-task-invariants.mjs +169 -0
- package/scripts/test-terminal-e2e.mjs +264 -0
- package/scripts/test-town-3d.mjs +121 -0
- package/scripts/test-town-drag.mjs +218 -0
- package/scripts/test-town-layout.mjs +200 -0
- package/scripts/test-town-page.mjs +136 -0
- package/scripts/test-town-scene-math.ts +169 -0
- package/scripts/test-town-twin.mjs +121 -0
- package/scripts/test-town-upload.mjs +89 -0
- package/scripts/test-town-user-flow.mjs +619 -0
- package/scripts/test-town-world.mjs +76 -0
- package/scripts/test-town-wuwa.mjs +198 -0
- package/scripts/test-user-isolation.mjs +193 -0
- package/scripts/test-workshop-db.ts +280 -0
- package/scripts/test-workshop-entries.ts +370 -0
- package/scripts/test-workspace-api.ts +329 -0
- package/scripts/test-ws-aep.mjs +187 -0
- package/scripts/test-ws-protocol.ts +134 -0
- package/scripts/test-ws-stress.mjs +208 -0
- package/scripts/town-demo-seed.mjs +75 -0
- package/scripts/town-scene-clear.mjs +56 -0
- package/scripts/ui-screenshot.mjs +128 -0
- package/scripts/verify-a2a-live.mjs +124 -0
- package/scripts/verify-exec-modes-report.ts +248 -0
- package/scripts/verify-game.mjs +477 -0
- package/scripts/verify-lead-mail.ts +188 -0
- package/scripts/verify-page-smoke.mjs +87 -0
- package/scripts/verify-ws-events.mjs +123 -0
- package/scripts/ws-test.mjs +176 -0
- package/scripts/ws-test.py +4 -0
- package/server/api/health.get.ts +32 -0
- package/server/api/mcp/workshop.ts +78 -0
- package/server/api/metrics.ts +75 -0
- package/server/api/system/config.get.ts +12 -0
- package/server/api/system/monitor/terminal/ws.ts +177 -0
- package/server/api/system/monitor/terminate.post.ts +36 -0
- package/server/api/system/monitor.get.ts +13 -0
- package/server/api/system/settings/events.get.ts +28 -0
- package/server/api/system/settings/index.get.ts +14 -0
- package/server/api/system/settings/index.patch.ts +22 -0
- package/server/api/system/settings/reset.post.ts +12 -0
- package/server/api/users/[id].delete.ts +11 -0
- package/server/api/users/[id].get.ts +11 -0
- package/server/api/users/[id].put.ts +14 -0
- package/server/api/users/index.get.ts +13 -0
- package/server/api/users/index.post.ts +13 -0
- package/server/api/users/login.post.ts +11 -0
- package/server/api/users/logout.post.ts +9 -0
- package/server/api/users/me.get.ts +9 -0
- package/server/api/users/register.post.ts +11 -0
- package/server/api/users/tokens/[id]/token.get.ts +11 -0
- package/server/api/users/tokens/[id].delete.ts +11 -0
- package/server/api/users/tokens/[id].patch.ts +14 -0
- package/server/api/users/tokens/index.get.ts +9 -0
- package/server/api/users/tokens/index.post.ts +13 -0
- package/server/api/workshop/[...path].ts +13 -0
- package/server/api/workshop/a2a/[agentId]/card/index.get.ts +75 -0
- package/server/api/workshop/a2a/[agentId]/rpc/index.post.ts +429 -0
- package/server/api/workshop/a2a/send.post.ts +28 -0
- package/server/api/workshop/agent-tools/approvals/[id]/decide.post.ts +21 -0
- package/server/api/workshop/agent-tools/approvals/index.get.ts +18 -0
- package/server/api/workshop/agent-tools/bindings/[id].delete.ts +21 -0
- package/server/api/workshop/agent-tools/bindings/[id].patch.ts +14 -0
- package/server/api/workshop/agent-tools/bindings/index.get.ts +14 -0
- package/server/api/workshop/agent-tools/bindings/index.post.ts +20 -0
- package/server/api/workshop/agent-tools/invoke.post.ts +34 -0
- package/server/api/workshop/agents/[id].delete.ts +20 -0
- package/server/api/workshop/agents/[id].get.ts +19 -0
- package/server/api/workshop/agents/[id].patch.ts +30 -0
- package/server/api/workshop/agents/index.get.ts +13 -0
- package/server/api/workshop/agents/index.post.ts +29 -0
- package/server/api/workshop/agents/subscribe.post.ts +21 -0
- package/server/api/workshop/approvals/[id]/decide.post.ts +30 -0
- package/server/api/workshop/approvals/index.get.ts +22 -0
- package/server/api/workshop/assets/character/[id].delete.ts +33 -0
- package/server/api/workshop/assets/character/index.get.ts +17 -0
- package/server/api/workshop/assets/character/index.post.ts +87 -0
- package/server/api/workshop/assets/devices/[id].delete.ts +43 -0
- package/server/api/workshop/assets/devices/index.get.ts +103 -0
- package/server/api/workshop/assets/devices/index.post.ts +53 -0
- package/server/api/workshop/audit.get.ts +43 -0
- package/server/api/workshop/caller.ts +141 -0
- package/server/api/workshop/channel-templates/[id]/instantiate.post.ts +31 -0
- package/server/api/workshop/channel-templates/[id].delete.ts +20 -0
- package/server/api/workshop/channel-templates/[id].get.ts +19 -0
- package/server/api/workshop/channel-templates/[id].patch.ts +48 -0
- package/server/api/workshop/channel-templates/from-channel.post.ts +31 -0
- package/server/api/workshop/channel-templates/index.get.ts +13 -0
- package/server/api/workshop/channel-templates/index.post.ts +61 -0
- package/server/api/workshop/channels/[id]/activate.post.ts +19 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/[memoryId].delete.ts +30 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/index.get.ts +29 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/index.post.ts +48 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/memories/search.post.ts +43 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/model.patch.ts +39 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/position.patch.ts +33 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/range.patch.ts +54 -0
- package/server/api/workshop/channels/[id]/agents/[agentId]/stop.post.ts +25 -0
- package/server/api/workshop/channels/[id]/agents/[agentId].delete.ts +23 -0
- package/server/api/workshop/channels/[id]/agents/[agentId].patch.ts +28 -0
- package/server/api/workshop/channels/[id]/agents/index.get.ts +27 -0
- package/server/api/workshop/channels/[id]/agents/index.post.ts +61 -0
- package/server/api/workshop/channels/[id]/events.get.ts +55 -0
- package/server/api/workshop/channels/[id]/index.get.ts +18 -0
- package/server/api/workshop/channels/[id]/index.patch.ts +30 -0
- package/server/api/workshop/channels/[id]/memories/[memoryId].delete.ts +29 -0
- package/server/api/workshop/channels/[id]/memories/index.get.ts +28 -0
- package/server/api/workshop/channels/[id]/memories/index.post.ts +42 -0
- package/server/api/workshop/channels/[id]/messages/index.get.ts +56 -0
- package/server/api/workshop/channels/[id]/messages/index.post.ts +75 -0
- package/server/api/workshop/channels/[id]/queue.get.ts +25 -0
- package/server/api/workshop/channels/[id]/tasks/index.get.ts +24 -0
- package/server/api/workshop/channels/[id]/tasks/index.post.ts +79 -0
- package/server/api/workshop/channels/[id]/terminals.get.ts +21 -0
- package/server/api/workshop/channels/[id].delete.ts +18 -0
- package/server/api/workshop/channels.get.ts +11 -0
- package/server/api/workshop/channels.post.ts +48 -0
- package/server/api/workshop/daq/[id]/bind.post.ts +19 -0
- package/server/api/workshop/daq/[id]/samples.get.ts +22 -0
- package/server/api/workshop/daq/[id]/test.post.ts +17 -0
- package/server/api/workshop/daq/[id].delete.ts +30 -0
- package/server/api/workshop/daq/[id].patch.ts +31 -0
- package/server/api/workshop/daq/alarms/[id]/ack.post.ts +15 -0
- package/server/api/workshop/daq/alarms.get.ts +16 -0
- package/server/api/workshop/daq/controller.post.ts +52 -0
- package/server/api/workshop/daq/index.get.ts +40 -0
- package/server/api/workshop/daq/index.post.ts +33 -0
- package/server/api/workshop/daq/infra/reconnect.post.ts +17 -0
- package/server/api/workshop/daq/infra.get.ts +11 -0
- package/server/api/workshop/daq/templates/[key].delete.ts +17 -0
- package/server/api/workshop/daq/templates/[key].patch.ts +18 -0
- package/server/api/workshop/daq/templates/index.post.ts +17 -0
- package/server/api/workshop/daq/test-driver.post.ts +23 -0
- package/server/api/workshop/dcw/[id]/bind.post.ts +17 -0
- package/server/api/workshop/dcw/[id]/param-ledger.get.ts +15 -0
- package/server/api/workshop/dcw/[id]/test.post.ts +15 -0
- package/server/api/workshop/dcw/[id]/write.post.ts +19 -0
- package/server/api/workshop/dcw/[id].delete.ts +30 -0
- package/server/api/workshop/dcw/[id].patch.ts +18 -0
- package/server/api/workshop/dcw/controller.post.ts +43 -0
- package/server/api/workshop/dcw/index.get.ts +25 -0
- package/server/api/workshop/dcw/index.post.ts +20 -0
- package/server/api/workshop/dcw/journal/index.get.ts +21 -0
- package/server/api/workshop/dcw/journal/node/[nodeId]/rollback.post.ts +22 -0
- package/server/api/workshop/dcw/line/query.get.ts +31 -0
- package/server/api/workshop/dcw/lines/[id]/start.post.ts +35 -0
- package/server/api/workshop/dcw/lines/[id]/stop.post.ts +34 -0
- package/server/api/workshop/dcw/lines/[id].delete.ts +35 -0
- package/server/api/workshop/dcw/lines/[id].patch.ts +15 -0
- package/server/api/workshop/dcw/lines/index.get.ts +14 -0
- package/server/api/workshop/dcw/lines/index.post.ts +31 -0
- package/server/api/workshop/dcw/optimizations/[id]/judge.post.ts +24 -0
- package/server/api/workshop/dcw/optimizations/[id]/rollback.post.ts +29 -0
- package/server/api/workshop/dcw/optimizations/[id]/series.get.ts +18 -0
- package/server/api/workshop/dcw/optimizations/[id].get.ts +17 -0
- package/server/api/workshop/dcw/optimizations/index.get.ts +23 -0
- package/server/api/workshop/dcw/products/[id].delete.ts +16 -0
- package/server/api/workshop/dcw/products/[id].patch.ts +17 -0
- package/server/api/workshop/dcw/products/index.post.ts +16 -0
- package/server/api/workshop/dcw/recipes/[id]/apply.post.ts +40 -0
- package/server/api/workshop/dcw/recipes/[id]/mark-good.post.ts +21 -0
- package/server/api/workshop/dcw/recipes/[id]/rollback-good.post.ts +19 -0
- package/server/api/workshop/dcw/recipes/[id].delete.ts +14 -0
- package/server/api/workshop/dcw/recipes/[id].patch.ts +16 -0
- package/server/api/workshop/dcw/recipes/index.get.ts +12 -0
- package/server/api/workshop/dcw/recipes/index.post.ts +15 -0
- package/server/api/workshop/dcw/runs/[id]/close.post.ts +15 -0
- package/server/api/workshop/dcw/runs/[id]/data.get.ts +15 -0
- package/server/api/workshop/dcw/runs/[id]/rollback.post.ts +20 -0
- package/server/api/workshop/dcw/runs/index.get.ts +11 -0
- package/server/api/workshop/dcw/templates/[key].delete.ts +14 -0
- package/server/api/workshop/dcw/templates/[key].patch.ts +16 -0
- package/server/api/workshop/dcw/templates/index.post.ts +15 -0
- package/server/api/workshop/dcw/test-driver.post.ts +16 -0
- package/server/api/workshop/device-twins/[id]/control.post.ts +21 -0
- package/server/api/workshop/device-twins/[id]/telemetry.post.ts +21 -0
- package/server/api/workshop/device-twins/[id].delete.ts +25 -0
- package/server/api/workshop/device-twins/[id].patch.ts +29 -0
- package/server/api/workshop/device-twins/index.get.ts +16 -0
- package/server/api/workshop/device-twins/index.post.ts +32 -0
- package/server/api/workshop/fs/dirs.get.ts +40 -0
- package/server/api/workshop/mailbox/all.get.ts +19 -0
- package/server/api/workshop/mailbox.get.ts +14 -0
- package/server/api/workshop/memories/maintenance.post.ts +15 -0
- package/server/api/workshop/ops-logs/index.get.ts +30 -0
- package/server/api/workshop/ops-logs/index.post.ts +39 -0
- package/server/api/workshop/runtime.get.ts +11 -0
- package/server/api/workshop/scene/layouts/[channelId].delete.ts +23 -0
- package/server/api/workshop/scene/layouts/[channelId].put.ts +31 -0
- package/server/api/workshop/scene/layouts.get.ts +13 -0
- package/server/api/workshop/tasks/[id]/cancel/index.post.ts +34 -0
- package/server/api/workshop/tasks/[id]/complete.post.ts +32 -0
- package/server/api/workshop/tasks/[id]/dispatch.post.ts +30 -0
- package/server/api/workshop/tasks/[id]/report.post.ts +38 -0
- package/server/api/workshop/tasks/[id]/retry.post.ts +33 -0
- package/server/api/workshop/tasks/[id].get.ts +32 -0
- package/server/api/workshop/tasks.get.ts +13 -0
- package/server/api/workshop/teams/[id]/deploy.post.ts +40 -0
- package/server/api/workshop/teams/[id]/members/[agentId].delete.ts +19 -0
- package/server/api/workshop/teams/[id]/members/index.get.ts +19 -0
- package/server/api/workshop/teams/[id]/members/index.post.ts +32 -0
- package/server/api/workshop/teams/[id].delete.ts +20 -0
- package/server/api/workshop/teams/[id].get.ts +19 -0
- package/server/api/workshop/teams/[id].patch.ts +32 -0
- package/server/api/workshop/teams/index.get.ts +13 -0
- package/server/api/workshop/teams/index.post.ts +27 -0
- package/server/api/workshop/users/me.get.ts +23 -0
- package/server/api/workshop/users/register.post.ts +33 -0
- package/server/api/workshop/workspaces/[id]/channel-templates/[tplId].post.ts +31 -0
- package/server/api/workshop/workspaces/[id]/channels/[channelId].delete.ts +15 -0
- package/server/api/workshop/workspaces/[id]/channels/[channelId].post.ts +15 -0
- package/server/api/workshop/workspaces/[id]/index.delete.ts +14 -0
- package/server/api/workshop/workspaces/index.get.ts +11 -0
- package/server/api/workshop/workspaces/index.post.ts +17 -0
- package/server/api/workshop/ws.ts +694 -0
- package/server/data/agent-node-bindings.json +554 -0
- package/server/data/daq-templates.json +16 -0
- package/server/data/daqs.json +2680 -0
- package/server/data/dcw-lines.json +919 -0
- package/server/data/dcw-products.json +835 -0
- package/server/data/dcw-recipes.json +1650 -0
- package/server/data/dcw-rollback.json +9339 -0
- package/server/data/dcw-runs.json +4633 -0
- package/server/data/dcw-templates.json +26 -0
- package/server/data/dcw-writes.json +9770 -0
- package/server/data/dcws.json +1736 -0
- package/server/data/device-twins.json +133 -0
- package/server/data/line-runs.json +152 -0
- package/server/data/scene-layouts.json +200 -0
- package/server/mcp/workshop-server.ts +510 -0
- package/server/middleware/request-log.ts +15 -0
- package/server/plugins/backup.ts +85 -0
- package/server/plugins/daq.ts +65 -0
- package/server/plugins/dev-stability-guard.ts +42 -0
- package/server/plugins/security.ts +29 -0
- package/server/plugins/system-config.ts +38 -0
- package/server/plugins/workshop.ts +145 -0
- package/server/repositories/user.repository.ts +391 -0
- package/server/schemas/user.schema.ts +67 -0
- package/server/services/system-config.ts +356 -0
- package/server/services/user.service.ts +186 -0
- package/server/services/workshop/agents/adapters/omp-rpc-client.ts +539 -0
- package/server/services/workshop/agents/adapters/to-a2a.ts +40 -0
- package/server/services/workshop/agents/agent-interface.ts +219 -0
- package/server/services/workshop/agents/claude-agent.ts +47 -0
- package/server/services/workshop/agents/factory.ts +24 -0
- package/server/services/workshop/agents/harness-process.ts +158 -0
- package/server/services/workshop/agents/harness-terminal.ts +545 -0
- package/server/services/workshop/agents/industrial-context.ts +222 -0
- package/server/services/workshop/agents/industrial-tools.ts +362 -0
- package/server/services/workshop/agents/mock-agent.ts +396 -0
- package/server/services/workshop/agents/node-bindings.repo.ts +165 -0
- package/server/services/workshop/agents/omp-agent.ts +1598 -0
- package/server/services/workshop/agents/tool-approvals.ts +174 -0
- package/server/services/workshop/assets/character-asset.repo.ts +120 -0
- package/server/services/workshop/assets/device-twin.repo.ts +236 -0
- package/server/services/workshop/daq/alarm-notify.ts +100 -0
- package/server/services/workshop/daq/bus/index.ts +46 -0
- package/server/services/workshop/daq/bus/inproc.adapter.ts +62 -0
- package/server/services/workshop/daq/bus/mqtt.adapter.ts +112 -0
- package/server/services/workshop/daq/bus/queue-port.ts +36 -0
- package/server/services/workshop/daq/daq-controller.ts +778 -0
- package/server/services/workshop/daq/daq-node.repo.ts +96 -0
- package/server/services/workshop/daq/daq-node.ts +247 -0
- package/server/services/workshop/daq/daq-runtime.ts +168 -0
- package/server/services/workshop/daq/daq-templates.ts +157 -0
- package/server/services/workshop/daq/drivers.ts +771 -0
- package/server/services/workshop/daq/host-bindings.ts +71 -0
- package/server/services/workshop/daq/host-ports.ts +64 -0
- package/server/services/workshop/daq/infra.ts +202 -0
- package/server/services/workshop/daq/storage/index.ts +96 -0
- package/server/services/workshop/daq/storage/sqlite.adapter.ts +205 -0
- package/server/services/workshop/daq/storage/timescale.adapter.ts +185 -0
- package/server/services/workshop/daq/storage/tsdb-port.ts +74 -0
- package/server/services/workshop/db/agent.repo.ts +105 -0
- package/server/services/workshop/db/channel-agent.repo.ts +119 -0
- package/server/services/workshop/db/channel-event.repo.ts +154 -0
- package/server/services/workshop/db/channel-template.repo.ts +113 -0
- package/server/services/workshop/db/channel.repo.ts +101 -0
- package/server/services/workshop/db/database.ts +934 -0
- package/server/services/workshop/db/memory.repo.ts +249 -0
- package/server/services/workshop/db/message.repo.ts +149 -0
- package/server/services/workshop/db/ops.repo.ts +323 -0
- package/server/services/workshop/db/schema.sql +143 -0
- package/server/services/workshop/db/subscription.repo.ts +62 -0
- package/server/services/workshop/db/task.repo.ts +175 -0
- package/server/services/workshop/db/team-member.repo.ts +70 -0
- package/server/services/workshop/db/team.repo.ts +109 -0
- package/server/services/workshop/db/user.repo.ts +100 -0
- package/server/services/workshop/dcw/dcw-controller.ts +841 -0
- package/server/services/workshop/dcw/dcw-line.repo.ts +86 -0
- package/server/services/workshop/dcw/dcw-node.repo.ts +89 -0
- package/server/services/workshop/dcw/dcw-node.ts +200 -0
- package/server/services/workshop/dcw/dcw-product.repo.ts +95 -0
- package/server/services/workshop/dcw/dcw-recipe.repo.ts +349 -0
- package/server/services/workshop/dcw/dcw-runtime.ts +82 -0
- package/server/services/workshop/dcw/dcw-templates.ts +138 -0
- package/server/services/workshop/dcw/drivers.ts +523 -0
- package/server/services/workshop/dcw/line-run.ts +101 -0
- package/server/services/workshop/dcw/recipe-rollback-manager.ts +702 -0
- package/server/services/workshop/dcw/recipe-rollback.repo.ts +197 -0
- package/server/services/workshop/logger.ts +58 -0
- package/server/services/workshop/ops/ops.ts +78 -0
- package/server/services/workshop/prompts/loader.ts +112 -0
- package/server/services/workshop/runtime/agent-runtime.ts +698 -0
- package/server/services/workshop/runtime/channel-runtime.ts +202 -0
- package/server/services/workshop/runtime/embedding-provider.ts +79 -0
- package/server/services/workshop/runtime/execution-mode.ts +215 -0
- package/server/services/workshop/runtime/mailbox.ts +148 -0
- package/server/services/workshop/runtime/manager.ts +2543 -0
- package/server/services/workshop/runtime/memory.ts +439 -0
- package/server/services/workshop/runtime/monitor.ts +194 -0
- package/server/services/workshop/runtime/scheduler-loop.ts +773 -0
- package/server/services/workshop/runtime/task-engine.ts +574 -0
- package/server/services/workshop/scene/scene-layout.repo.ts +112 -0
- package/server/services/workshop/scene-events.ts +63 -0
- package/server/services/workshop/types/a2a.ts +61 -0
- package/server/services/workshop/types/task.ts +85 -0
- package/server/types/user.ts +53 -0
- package/server/utils/approval-gate.ts +67 -0
- package/server/utils/auth.ts +13 -0
- package/server/utils/config.ts +40 -0
- package/server/utils/errors.ts +24 -0
- package/server/utils/response.ts +49 -0
- package/server/utils/validate.ts +19 -0
- package/shared/config/engine.d.mts +41 -0
- package/shared/config/engine.mjs +291 -0
- package/shared/config/home.d.mts +48 -0
- package/shared/config/home.mjs +146 -0
- package/shared/config/schema.json +30 -0
- package/shared/daq-protocol.ts +361 -0
- package/shared/dcw-protocol.ts +576 -0
- package/shared/terminal-protocol.ts +70 -0
- package/shared/town-anim.ts +56 -0
- package/shared/town-behavior.ts +136 -0
- package/shared/town-protocol.ts +110 -0
- package/shared/town-scene-math.ts +283 -0
- package/shared/workshop-protocol.ts +199 -0
- package/tsconfig.json +10 -0
- package/uno.config.ts +37 -0
|
@@ -0,0 +1,1598 @@
|
|
|
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 方法注册为 omp host tools,agent 原生调用 report_progress /
|
|
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
|
+
* 协议权威:omp://rpc.md;AgentInterface 契约见 agent-interface.ts。
|
|
18
|
+
*/
|
|
19
|
+
import { createLogger } from '../logger'
|
|
20
|
+
import { randomUUID } from 'node:crypto'
|
|
21
|
+
import type {
|
|
22
|
+
AgentEvent,
|
|
23
|
+
AgentInterface,
|
|
24
|
+
AgentInfo,
|
|
25
|
+
AgentRunContext,
|
|
26
|
+
AgentRunRequest,
|
|
27
|
+
SupervisionDecision,
|
|
28
|
+
SupervisionSnapshot,
|
|
29
|
+
} from './agent-interface'
|
|
30
|
+
import type { A2AArtifact, A2AMessage, Part } from '../types/a2a'
|
|
31
|
+
import type { WorkspaceTask } from '../types/task'
|
|
32
|
+
import {
|
|
33
|
+
OmpRpcClient,
|
|
34
|
+
type AgentSessionEvent,
|
|
35
|
+
type RpcHostToolDefinition,
|
|
36
|
+
type HostToolCallRequest,
|
|
37
|
+
} from './adapters/omp-rpc-client'
|
|
38
|
+
import {
|
|
39
|
+
registerHarnessProcess,
|
|
40
|
+
bindHarnessProcess,
|
|
41
|
+
markHarnessProcessExit,
|
|
42
|
+
killHarnessProcess,
|
|
43
|
+
} from './harness-process'
|
|
44
|
+
import {
|
|
45
|
+
attachTerminalTap,
|
|
46
|
+
markTerminalSessionExit,
|
|
47
|
+
} from './harness-terminal'
|
|
48
|
+
import {
|
|
49
|
+
loadHostToolDefs,
|
|
50
|
+
renderPrompt,
|
|
51
|
+
} from '../prompts/loader'
|
|
52
|
+
import { toolDaqQuery, toolDcwControl, toolDcwJudge, toolDcwJournal, toolDcwRollback, toolMyIndustrialNodes } from './industrial-tools'
|
|
53
|
+
import { buildIndustrialContext, industrialLoopGuide } from './industrial-context'
|
|
54
|
+
import { extractTaskMode } from '../runtime/execution-mode'
|
|
55
|
+
|
|
56
|
+
const log = createLogger('workshop.omp')
|
|
57
|
+
|
|
58
|
+
// ===== 配置 =====
|
|
59
|
+
|
|
60
|
+
export interface OmpAgentConfig {
|
|
61
|
+
/** omp 可执行文件路径(默认 'omp',从 PATH 查找) */
|
|
62
|
+
command?: string
|
|
63
|
+
/** 额外 CLI 参数 */
|
|
64
|
+
args?: string[]
|
|
65
|
+
/** omp 工作目录(默认 process.cwd()) */
|
|
66
|
+
cwd?: string
|
|
67
|
+
/** 模型 provider(如 'anthropic'/'openai'/'zhipu');省略则用 omp 默认 */
|
|
68
|
+
provider?: string
|
|
69
|
+
/** 模型 ID;省略则用 omp 默认 */
|
|
70
|
+
model?: string
|
|
71
|
+
/** thinking level: off/minimal/low/medium/high/xhigh/max */
|
|
72
|
+
thinkingLevel?: string
|
|
73
|
+
/** 自定义系统 prompt 前缀(拼接到 agent 系统指令之前) */
|
|
74
|
+
systemPromptPrefix?: string
|
|
75
|
+
/** 限制 omp 仅使用指定内置工具(null = 全部) */
|
|
76
|
+
toolNames?: string[]
|
|
77
|
+
/** 每轮停滞超时(ms,默认 300000):整轮无任何 omp 事件才中止;工具内阻塞(poll_messages ≤180s)有 tool 事件刷新计时 */
|
|
78
|
+
promptTimeoutMs?: number
|
|
79
|
+
/** supervise 轮超时(ms,默认 150000) */
|
|
80
|
+
superviseTimeoutMs?: number
|
|
81
|
+
/**
|
|
82
|
+
* omp 输出模式:'rpc-ui'(默认)= RPC 协议 + UI 上下文 —— ask 工具与
|
|
83
|
+
* extension_ui_request 对话框可用(监控终端 HITL 通道,强制 noPty);
|
|
84
|
+
* 'rpc' = 纯协议(无 UI,ask 不可用)。
|
|
85
|
+
*/
|
|
86
|
+
rpcMode?: 'rpc' | 'rpc-ui'
|
|
87
|
+
/** channel 级作业场景 prompt(manager 装配时注入;用户场景规范,全员共享) */
|
|
88
|
+
scenarioPrompt?: string
|
|
89
|
+
/** agent 身份(由 factory 从 AgentInfo 注入) */
|
|
90
|
+
agentId?: string
|
|
91
|
+
name?: string
|
|
92
|
+
role?: 'lead' | 'worker'
|
|
93
|
+
channelId?: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ===== host tool 定义(注册到 omp,agent 原生调用) =====
|
|
97
|
+
|
|
98
|
+
// ===== host tool 定义(外置 .AgentWorkShop/prompts/host-tools.json;加载器缓存)=====
|
|
99
|
+
|
|
100
|
+
export const HOST_TOOLS: RpcHostToolDefinition[] = loadHostToolDefs()
|
|
101
|
+
|
|
102
|
+
/** 仅 lead 可见的工具名(dispatch/调度/团队管理面;worker 注册时剔除,压缩工具上下文) */
|
|
103
|
+
const LEAD_ONLY_TOOL_NAMES = new Set([
|
|
104
|
+
'dispatch_task',
|
|
105
|
+
'get_queue_overview',
|
|
106
|
+
'read_channel_mail',
|
|
107
|
+
'reassign_task',
|
|
108
|
+
'update_task',
|
|
109
|
+
'create_team_agent',
|
|
110
|
+
'update_team_agent',
|
|
111
|
+
'remove_team_agent',
|
|
112
|
+
])
|
|
113
|
+
|
|
114
|
+
/** 按角色装配 host tools:lead = 全量;worker = 剔除 lead 专属(执行面 + 通信面 + 记忆面) */
|
|
115
|
+
export function hostToolsForRole(role: 'lead' | 'worker'): RpcHostToolDefinition[] {
|
|
116
|
+
if (role === 'lead') return HOST_TOOLS
|
|
117
|
+
return HOST_TOOLS.filter(t => !LEAD_ONLY_TOOL_NAMES.has(t.name))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ===== 辅助函数 =====
|
|
121
|
+
|
|
122
|
+
/** 从消息 parts 提取纯文本 */
|
|
123
|
+
function partsToText(parts: Part[]): string {
|
|
124
|
+
return parts
|
|
125
|
+
.map((p) => {
|
|
126
|
+
if ('text' in p) return p.text
|
|
127
|
+
if ('data' in p) return JSON.stringify(p.data)
|
|
128
|
+
if ('url' in p) return p.url
|
|
129
|
+
if ('raw' in p) return p.raw
|
|
130
|
+
return ''
|
|
131
|
+
})
|
|
132
|
+
.join('\n')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 工具参数预览(Codex 式紧凑工具行):`name(path)` / `name(a=1, b=2)`。
|
|
137
|
+
* 首选 path/file/command/cmd 等单值字段,否则取前两个标量字段;截断 64 字符。
|
|
138
|
+
*/
|
|
139
|
+
function toolArgsPreview(args: unknown): string {
|
|
140
|
+
if (!args || typeof args !== 'object' || Array.isArray(args)) return ''
|
|
141
|
+
const a = args as Record<string, unknown>
|
|
142
|
+
const single = ['path', 'file', 'filename', 'command', 'cmd', 'query', 'pattern', 'url', 'taskId', 'task_id', 'id', 'name', 'toolName']
|
|
143
|
+
const pick = single.find(k => typeof a[k] === 'string' && (a[k] as string).length > 0)
|
|
144
|
+
let preview: string
|
|
145
|
+
if (pick) {
|
|
146
|
+
preview = (a[pick] as string)
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const scalars = Object.entries(a)
|
|
150
|
+
.filter(([, v]) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')
|
|
151
|
+
.slice(0, 2)
|
|
152
|
+
.map(([k, v]) => `${k}=${String(v)}`)
|
|
153
|
+
if (scalars.length === 0) return ''
|
|
154
|
+
preview = scalars.join(', ')
|
|
155
|
+
}
|
|
156
|
+
const flat = preview.replace(/\s+/g, ' ').trim()
|
|
157
|
+
if (!flat) return ''
|
|
158
|
+
return `(${flat.length > 64 ? `${flat.slice(0, 64)}…` : flat})`
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** 安全 JSON 解析(容错:提取第一个 JSON 数组/对象) */
|
|
162
|
+
function extractJsonArray(text: string): unknown[] | null {
|
|
163
|
+
const trimmed = text.trim()
|
|
164
|
+
// 直接尝试
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(trimmed)
|
|
167
|
+
if (Array.isArray(parsed)) return parsed
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// 继续
|
|
172
|
+
}
|
|
173
|
+
// 提取第一个 [ ... ] 块
|
|
174
|
+
const start = trimmed.indexOf('[')
|
|
175
|
+
const end = trimmed.lastIndexOf(']')
|
|
176
|
+
if (start >= 0 && end > start) {
|
|
177
|
+
try {
|
|
178
|
+
const parsed = JSON.parse(trimmed.slice(start, end + 1))
|
|
179
|
+
if (Array.isArray(parsed)) return parsed
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// 继续
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ===== OmpRpcAgentImpl =====
|
|
189
|
+
|
|
190
|
+
export class OmpRpcAgentImpl implements AgentInterface {
|
|
191
|
+
private readonly config: OmpAgentConfig
|
|
192
|
+
private client: OmpRpcClient | null = null
|
|
193
|
+
private workspace: AgentRunContext['workspace'] | null = null
|
|
194
|
+
private agentInfo: AgentInfo | null = null
|
|
195
|
+
private hostToolsRegistered = false
|
|
196
|
+
/** 当前 run() 的 taskId(worker 完成任务时用) */
|
|
197
|
+
private currentTaskId: string | null = null
|
|
198
|
+
/**
|
|
199
|
+
* 会话回合状态(steer 可靠注入的依据):
|
|
200
|
+
* omp 的 steer 仅在回合 streaming 中生效——prompt 已入列但尚未开始输出时,
|
|
201
|
+
* steer 会"成功"返回但被静默丢弃。因此注入方轮询直到回合输出开始(streamingStarted)
|
|
202
|
+
* 才发送;若回合在等待期间结束,改走 follow_up(prompt 通道)兜底投递。
|
|
203
|
+
*/
|
|
204
|
+
private streaming = false
|
|
205
|
+
private turnActive = false
|
|
206
|
+
/** 当前回合产生的 assistant 文本(供诊断) */
|
|
207
|
+
private turnText = ''
|
|
208
|
+
/** agent 身份信息(factory 注入;无需等待 init()) */
|
|
209
|
+
private selfAgentId = ''
|
|
210
|
+
private agentName = 'agent'
|
|
211
|
+
private agentRole: 'lead' | 'worker' = 'worker'
|
|
212
|
+
private channelId = ''
|
|
213
|
+
/**
|
|
214
|
+
* 当前待回执的触发上下文(自动关联兜底):
|
|
215
|
+
* LLM 偶发省略 in_reply_to 参数 → 平台在 send_message_to_agent 时按上下文自动盖章,
|
|
216
|
+
* 保证触发器回执关联契约不依赖模型传参纪律。
|
|
217
|
+
*/
|
|
218
|
+
private replyContext: { fromId: string, messageId: string } | null = null
|
|
219
|
+
|
|
220
|
+
constructor(config: Record<string, unknown> = {}) {
|
|
221
|
+
this.config = config as OmpAgentConfig
|
|
222
|
+
// factory 注入的 agent 身份(无需等待 init())
|
|
223
|
+
this.selfAgentId = this.config.agentId ?? ''
|
|
224
|
+
this.agentName = this.config.name ?? 'agent'
|
|
225
|
+
this.agentRole = this.config.role ?? 'worker'
|
|
226
|
+
this.channelId = this.config.channelId ?? ''
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ===== 生命周期 =====
|
|
230
|
+
|
|
231
|
+
async init(input: { agent: AgentInfo, channelId: string }): Promise<void> {
|
|
232
|
+
this.agentInfo = input.agent
|
|
233
|
+
this.channelId = input.channelId
|
|
234
|
+
this.agentName = input.agent.name
|
|
235
|
+
this.agentRole = input.agent.role
|
|
236
|
+
this.selfAgentId = input.agent.id
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async dispose(): Promise<void> {
|
|
240
|
+
if (this.client) {
|
|
241
|
+
const pid = this.client.pid
|
|
242
|
+
await this.client.dispose()
|
|
243
|
+
this.client = null
|
|
244
|
+
if (pid) {
|
|
245
|
+
markHarnessProcessExit(pid, null)
|
|
246
|
+
markTerminalSessionExit(pid, null)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
this.hostToolsRegistered = false
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** harness 进程资源信息(运行时资源监控;进程未 spawn/已回收 → null) */
|
|
253
|
+
getProcessInfo(): { pid: number, alive: boolean, command: string } | null {
|
|
254
|
+
const client = this.client
|
|
255
|
+
const pid = client?.pid
|
|
256
|
+
if (!pid || !client) return null
|
|
257
|
+
return { pid, alive: client.alive, command: `omp --mode ${this.rpcMode}` }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** 强制终止 harness 进程(进程树;终止后由调用方停止对应 AgentRuntime) */
|
|
261
|
+
killProcess(): void {
|
|
262
|
+
const pid = this.client?.pid
|
|
263
|
+
if (pid) {
|
|
264
|
+
markTerminalSessionExit(pid, null)
|
|
265
|
+
killHarnessProcess(pid)
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
this.client?.kill()
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* harness 进程存活校准(manager sweeper 周期性调用;休眠/强杀后 exit 事件
|
|
274
|
+
* 可能不达父进程):按 PID 探 OS 实际存在性,进程已死 → 客户端收敛为已退出,
|
|
275
|
+
* 在途回合经 __process_exit__ 归位,下一回合 ensureClient 重生子进程。
|
|
276
|
+
*/
|
|
277
|
+
reconcileProcess(): void {
|
|
278
|
+
this.client?.reconcile()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** omp 输出模式(rpc-ui = 默认,启用 HITL 对话框) */
|
|
282
|
+
private get rpcMode(): 'rpc' | 'rpc-ui' {
|
|
283
|
+
return this.config.rpcMode === 'rpc' ? 'rpc' : 'rpc-ui'
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ===== prompt 组合:场景 × 身份 × 记忆 × 系统设计 =====
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 前置上下文段(channel 场景 + 实例身份):场景优先级最高——用户对整个作业
|
|
290
|
+
* 场景的规范要求,全体成员共享;未设定时注入通用默认场景(prompts/scenario-default.md)。
|
|
291
|
+
* 实例 systemPromptPrefix 定义成员专长(可选,无默认)。
|
|
292
|
+
*/
|
|
293
|
+
private contextPrefix(roster?: string): string {
|
|
294
|
+
const scenario = typeof this.config.scenarioPrompt === 'string' ? this.config.scenarioPrompt.trim() : ''
|
|
295
|
+
const identity = this.config.systemPromptPrefix ?? ''
|
|
296
|
+
const parts: string[] = []
|
|
297
|
+
parts.push(
|
|
298
|
+
scenario
|
|
299
|
+
? `## Scenario Brief (channel-wide, highest priority — user-defined operating rules)\n${scenario}`
|
|
300
|
+
: renderPrompt('scenario-default'),
|
|
301
|
+
``,
|
|
302
|
+
)
|
|
303
|
+
if (identity) {
|
|
304
|
+
parts.push(
|
|
305
|
+
`## Your Profile (agent-specific)\n${identity}`,
|
|
306
|
+
``,
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
// 工业工况简报(实时):本 Agent 绑定节点所在的产线运行状态/活动配方窗口/关联设备
|
|
310
|
+
// 孪生遥测 —— 让 Agent 每次回合开头就知道自己在哪条产线、生产什么、窗口多少、
|
|
311
|
+
// 设备当前状态,从而把节点读写放进工艺上下文里决策(而非盲操作)。
|
|
312
|
+
const industrial = buildIndustrialContext(this.selfAgentId)
|
|
313
|
+
if (industrial) {
|
|
314
|
+
parts.push(industrial, ``)
|
|
315
|
+
}
|
|
316
|
+
// 工业调控作业环(方法论层):有绑定节点才注入 —— 保证 Agent 在拿到任何
|
|
317
|
+
// 工具之前就理解「观察→理解→窗口内小步幅→执行→复测」的调控纪律。
|
|
318
|
+
const loop = industrialLoopGuide(this.selfAgentId)
|
|
319
|
+
if (loop) {
|
|
320
|
+
parts.push(loop, ``)
|
|
321
|
+
}
|
|
322
|
+
if (roster) {
|
|
323
|
+
parts.push(roster, ``)
|
|
324
|
+
}
|
|
325
|
+
return parts.join('\n')
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 系统设计手册(prompts/system-manual.md):让 agent 真正理解其运行环境。
|
|
330
|
+
*/
|
|
331
|
+
private systemManual(): string {
|
|
332
|
+
return renderPrompt('system-manual')
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ===== 团队名册(roster):全成员名单 + 专长,注入每个回合 =====
|
|
336
|
+
|
|
337
|
+
/** 名册缓存(null = 待刷新);30s TTL——任何成员的增删改都会及时反映到名单 */
|
|
338
|
+
private rosterCache: string | null = null
|
|
339
|
+
private rosterAt = 0
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 构建团队名册(prompts/team-roster.md):
|
|
343
|
+
* 每位成员一行 — id(寻址键)/名字/角色/harness/专长(systemPromptPrefix 压缩);
|
|
344
|
+
* 自己的条目带 ← 你 标记。所有回合(worker/supervise/peer)统一注入,
|
|
345
|
+
* 保证任何 Agent 都知道找谁、怎么发信、怎么等回执。
|
|
346
|
+
*/
|
|
347
|
+
private async teamRoster(): Promise<string> {
|
|
348
|
+
if (this.rosterCache !== null && Date.now() - this.rosterAt < 30_000) return this.rosterCache
|
|
349
|
+
if (!this.workspace) return ''
|
|
350
|
+
try {
|
|
351
|
+
const agents = await this.workspace.listAgents()
|
|
352
|
+
const condense = (s: unknown): string =>
|
|
353
|
+
String(s ?? '').replace(/\s+/g, ' ').trim().slice(0, 110)
|
|
354
|
+
const lines = agents
|
|
355
|
+
.filter(a => a.enabled !== 0)
|
|
356
|
+
.map((a) => {
|
|
357
|
+
const specialty = condense(a.config?.systemPromptPrefix)
|
|
358
|
+
const self = a.id === this.selfAgentId ? ' ← 你' : ''
|
|
359
|
+
return `- id: ${a.id} | ${a.name}${self} | role=${a.role} | harness=${a.harness}${specialty ? ` | 擅长: ${specialty}` : ''}`
|
|
360
|
+
})
|
|
361
|
+
.join('\n')
|
|
362
|
+
this.rosterCache = renderPrompt('team-roster', { rosterLines: lines })
|
|
363
|
+
this.rosterAt = Date.now()
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// 拉取失败不再沿用旧名册(频道重建/运行时漂移会把陈旧 id 冻结进每个回合,
|
|
367
|
+
// 直接导致 A2A 寻址未命中);置空下次重试,寻址层另有名字容错兜底
|
|
368
|
+
this.rosterCache = null
|
|
369
|
+
this.rosterAt = 0
|
|
370
|
+
}
|
|
371
|
+
return this.rosterCache ?? ''
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 实时消息注入(送达模式协议):
|
|
376
|
+
* - 回合 streaming 中 → 立即 steer,返回 'steer'(同轮可见,唯一可标记消费的路径)
|
|
377
|
+
* - 回合活跃但尚未 streaming(prompt 排队窗口,上限 20s)→ 等 streaming 开始后 steer
|
|
378
|
+
* - 回合被 host 工具阻塞(如 poll_messages 等待)/空闲/发送失败 → 返回 'deferred':
|
|
379
|
+
* 不再走 follow_up 兜底 —— 目标处理中时 omp 会拒绝该请求,而旧实现拒绝后仍被
|
|
380
|
+
* 上层标记消费,消息从信箱消失(轮询查空丢失 bug 根因)。deferred 下消息保持
|
|
381
|
+
* pending:poll_messages 的 Mailbox 到信回调即时取走(毫秒级),或本回合结束后
|
|
382
|
+
* 由消费循环按 FIFO 起回合处理。
|
|
383
|
+
*/
|
|
384
|
+
async steer(text: string): Promise<'steer' | 'deferred'> {
|
|
385
|
+
// 从确定性触发横幅提取回执上下文(AgentRuntime.injectSteer 生成,格式固定):
|
|
386
|
+
// "[实时消息 from <id>]: ..." + "[系统触发器] 本消息要求回复(reply_to=<messageId>)。"
|
|
387
|
+
const banner = text.match(/\[实时消息 from ([^\]]+)]:[\s\S]*?要求回复\(reply_to=([0-9a-f-]{36})\)/)
|
|
388
|
+
if (banner) {
|
|
389
|
+
this.replyContext = { fromId: banner[1]!, messageId: banner[2]! }
|
|
390
|
+
}
|
|
391
|
+
const client = this.client
|
|
392
|
+
if (!client) return 'deferred'
|
|
393
|
+
try {
|
|
394
|
+
if (this.streaming) {
|
|
395
|
+
await client.send({ type: 'steer', message: text })
|
|
396
|
+
return 'steer'
|
|
397
|
+
}
|
|
398
|
+
if (this.turnActive) {
|
|
399
|
+
// prompt 排队窗口:等 streaming 开始(上限 20s);工具阻塞的回合等不到 → deferred
|
|
400
|
+
const deadline = Date.now() + 20_000
|
|
401
|
+
while (Date.now() < deadline && this.turnActive && !this.streaming) {
|
|
402
|
+
const { promise, resolve } = Promise.withResolvers()
|
|
403
|
+
setTimeout(resolve, 150)
|
|
404
|
+
await promise
|
|
405
|
+
}
|
|
406
|
+
if (this.streaming && this.turnActive) {
|
|
407
|
+
await client.send({ type: 'steer', message: text })
|
|
408
|
+
return 'steer'
|
|
409
|
+
}
|
|
410
|
+
return 'deferred'
|
|
411
|
+
}
|
|
412
|
+
// 空闲/回合已结束:deferred(消息 pending,消费循环 dequeue 即起回合)
|
|
413
|
+
return 'deferred'
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
log.error(`[OmpRpcAgent:${this.selfAgentId}] steer 注入失败(消息保持 pending):`, err instanceof Error ? err.message : err)
|
|
417
|
+
return 'deferred'
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async* run(request: AgentRunRequest, ctx: AgentRunContext): AsyncIterable<AgentEvent> {
|
|
422
|
+
const kind = request.message.metadata?.['x-aw-task-kind']
|
|
423
|
+
|
|
424
|
+
// worker: assign 消息 → 执行任务
|
|
425
|
+
if (kind === 'assign' && ctx.role === 'worker') {
|
|
426
|
+
yield* this.workerRun(request, ctx)
|
|
427
|
+
return
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// worker/lead: 同事点对点消息(含实时通信触发器)→ 按触发器语义处理并回复;
|
|
431
|
+
// 人类经 REST 注入的消息(from 空 + x-aw-from-label)同样进入应答流 ——
|
|
432
|
+
// 旧判定把 from 为空的人类消息落到 no-op 分支静默吞掉(消息秒标 consumed,
|
|
433
|
+
// Agent 从未读到内容),这是"给 Agent 发消息没有回复"的根因
|
|
434
|
+
if (!kind && (request.fromAgentId || request.message.metadata?.['x-aw-from-label'])) {
|
|
435
|
+
yield* this.peerMessageRun(request, ctx)
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// 其余消息(lead 的 assign/child-completed 等):no-op(调度由 supervise() 处理)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// ===== supervise() =====
|
|
443
|
+
|
|
444
|
+
/** supervise 单飞守卫:同一 client 不并发 LLM 回合(残留回合与下一 prompt 混流的根因) */
|
|
445
|
+
private supervising = false
|
|
446
|
+
|
|
447
|
+
async supervise(snapshot: SupervisionSnapshot, ctx: AgentRunContext, opts?: { signal?: AbortSignal }): Promise<SupervisionDecision[]> {
|
|
448
|
+
await this.ensureClient(ctx)
|
|
449
|
+
if (!this.client) return []
|
|
450
|
+
if (this.supervising) return [] // 上一轮 supervise 未收口:跳过本拍(节流即正确)
|
|
451
|
+
|
|
452
|
+
const prompt = await this.buildSupervisePrompt(snapshot, ctx.memory)
|
|
453
|
+
// 150s 上界:supervise 持 lead.execLock 期间信箱消费停顿(更久会拖垮 worker 回执处理);
|
|
454
|
+
// supervise 是一次真实 LLM 回合:omp 冷启动(插件/MCP 加载 30~90s)+ 慢 provider
|
|
455
|
+
// 单步可能 >60s,过紧会把正常回合掐成 "Interrupted by user"。默认 150s,
|
|
456
|
+
// 仅拦真僵死(更紧的预算由调用方 config.superviseTimeoutMs 显式传入);真 abort 已实现。
|
|
457
|
+
const timeoutMs = this.config.superviseTimeoutMs ?? 150_000
|
|
458
|
+
|
|
459
|
+
return new Promise<SupervisionDecision[]>((resolve) => {
|
|
460
|
+
let assistantText = ''
|
|
461
|
+
let resolved = false
|
|
462
|
+
|
|
463
|
+
const finish = (decisions: SupervisionDecision[]) => {
|
|
464
|
+
if (resolved) return
|
|
465
|
+
resolved = true
|
|
466
|
+
this.supervising = false
|
|
467
|
+
unsub()
|
|
468
|
+
clearTimeout(timer)
|
|
469
|
+
signalUnsub?.()
|
|
470
|
+
resolve(decisions)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const abortTurn = (): void => {
|
|
474
|
+
// 超时/外部取消:真正中止 omp 当前回合 —— 只 resolve 不 abort 会让残留回合
|
|
475
|
+
// 与下一个 prompt 在同一 client 混流(决策错位 + token 空烧)
|
|
476
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] supervise 超时(${timeoutMs}ms)→ abort 当前调度回合`)
|
|
477
|
+
void this.client?.send({ type: 'abort' }).catch(() => {})
|
|
478
|
+
finish([])
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const unsub = this.client!.onEvent((event) => {
|
|
482
|
+
if (event.type === 'message_update' && event.assistantMessageEvent?.type === 'text_delta') {
|
|
483
|
+
assistantText += event.assistantMessageEvent.delta ?? ''
|
|
484
|
+
}
|
|
485
|
+
if (event.type === 'agent_end' && event.isTerminal !== false) {
|
|
486
|
+
// 尝试从文本解析 JSON 决策(备用:如果 agent 没用 host tools 而是输出 JSON)
|
|
487
|
+
const parsed = extractJsonArray(assistantText)
|
|
488
|
+
if (parsed && parsed.length > 0) {
|
|
489
|
+
finish(parsed as SupervisionDecision[])
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
// agent 可能已通过 host tools 直接执行了调度,返回空(已执行)
|
|
493
|
+
finish([])
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (event.type === '__process_exit__' || event.type === '__error__') {
|
|
497
|
+
finish([])
|
|
498
|
+
}
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
const timer = setTimeout(abortTurn, timeoutMs)
|
|
502
|
+
|
|
503
|
+
// 外部取消(调度器 cancel 路径)传导:abort 当前 LLM 回合并立即收口
|
|
504
|
+
let signalUnsub: (() => void) | undefined
|
|
505
|
+
if (opts?.signal) {
|
|
506
|
+
if (opts.signal.aborted) {
|
|
507
|
+
unsub()
|
|
508
|
+
resolve([])
|
|
509
|
+
return
|
|
510
|
+
}
|
|
511
|
+
const onAbort = (): void => abortTurn()
|
|
512
|
+
opts.signal.addEventListener('abort', onAbort, { once: true })
|
|
513
|
+
signalUnsub = () => opts.signal?.removeEventListener('abort', onAbort)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.supervising = true
|
|
517
|
+
this.client!.send({ type: 'prompt', message: prompt }).catch(() => finish([]))
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ===== 内部:worker 执行 =====
|
|
522
|
+
|
|
523
|
+
private async* workerRun(request: AgentRunRequest, ctx: AgentRunContext): AsyncGenerator<AgentEvent, void, unknown> {
|
|
524
|
+
const taskId = request.taskId
|
|
525
|
+
?? (request.message.metadata?.['x-aw-task-id'] as string | undefined)
|
|
526
|
+
if (!taskId) return
|
|
527
|
+
|
|
528
|
+
this.currentTaskId = taskId
|
|
529
|
+
|
|
530
|
+
try {
|
|
531
|
+
await this.ensureClient(ctx)
|
|
532
|
+
}
|
|
533
|
+
catch (err) {
|
|
534
|
+
yield {
|
|
535
|
+
kind: 'error',
|
|
536
|
+
error: {
|
|
537
|
+
code: 'OMP_SPAWN_FAILED',
|
|
538
|
+
message: `omp 子进程启动失败: ${err instanceof Error ? err.message : String(err)}`,
|
|
539
|
+
},
|
|
540
|
+
}
|
|
541
|
+
return
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (!this.client) {
|
|
545
|
+
yield { kind: 'error', error: { code: 'OMP_NOT_READY', message: 'omp 客户端未就绪' } }
|
|
546
|
+
return
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// 构建 prompt
|
|
550
|
+
const taskText = partsToText(request.message.parts)
|
|
551
|
+
const prompt = await this.buildWorkerPrompt(taskId, taskText, request.memory)
|
|
552
|
+
|
|
553
|
+
// 流式执行 + 事件映射
|
|
554
|
+
yield* this.promptAndStream(prompt, taskId, ctx.signal)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* 点对点消息处理(实时通信驱动;lead 与 worker 通用)。
|
|
559
|
+
* 触发器语义:metadata['x-aw-require-reply']='true' → 必须经 send_message_to_agent
|
|
560
|
+
* 回给发送者:执行结果 + 对方所需内容,in_reply_to 关联原消息,并声明是否需再响应。
|
|
561
|
+
*/
|
|
562
|
+
private async* peerMessageRun(request: AgentRunRequest, ctx: AgentRunContext): AsyncGenerator<AgentEvent, void, unknown> {
|
|
563
|
+
try {
|
|
564
|
+
await this.ensureClient(ctx)
|
|
565
|
+
}
|
|
566
|
+
catch (err) {
|
|
567
|
+
// spawn 失败必须显式报错:静默 return 会让 sawRunError=false → 消息被
|
|
568
|
+
// 直接 markConsumed,实时消息一次性丢失且无任何重试;产出 error 事件
|
|
569
|
+
// 走消息 requeue(≤2 次),与 workerRun 同口径
|
|
570
|
+
yield {
|
|
571
|
+
kind: 'error',
|
|
572
|
+
error: {
|
|
573
|
+
code: 'OMP_SPAWN_FAILED',
|
|
574
|
+
message: `omp 子进程启动失败(peer 消息未消费,将重试): ${err instanceof Error ? err.message : String(err)}`,
|
|
575
|
+
},
|
|
576
|
+
}
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
if (!this.client) {
|
|
580
|
+
yield { kind: 'error', error: { code: 'OMP_NOT_READY', message: 'omp 客户端未就绪(peer 消息未消费,将重试)' } }
|
|
581
|
+
return
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const msg = request.message
|
|
585
|
+
// 显示名:agent 同事用 id(名册可解析);人类发送者用 fromLabel;兜底 unknown
|
|
586
|
+
const fromId = request.fromAgentId
|
|
587
|
+
?? (typeof msg.metadata?.['x-aw-from-label'] === 'string' ? msg.metadata['x-aw-from-label'] : undefined)
|
|
588
|
+
?? 'unknown'
|
|
589
|
+
const msgText = partsToText(msg.parts)
|
|
590
|
+
const requireReply = msg.metadata?.['x-aw-require-reply'] === 'true'
|
|
591
|
+
const isReply = typeof msg.metadata?.['x-aw-in-reply-to'] === 'string'
|
|
592
|
+
// 回执自动关联仅对真实 agent 发送者生效(人类无 agentId 可回投;
|
|
593
|
+
// 对人类的回复经事件流/时间线可见)
|
|
594
|
+
this.replyContext = requireReply && request.fromAgentId
|
|
595
|
+
? { fromId: request.fromAgentId, messageId: msg.messageId }
|
|
596
|
+
: null
|
|
597
|
+
|
|
598
|
+
const roleLine = this.agentRole === 'lead'
|
|
599
|
+
? `You are "${this.agentName}", the LEAD coordinator of a multi-agent team (Channel: ${this.channelId}). A team member sent you a direct message.`
|
|
600
|
+
: `You are "${this.agentName}", a worker agent in a multi-agent team (Channel: ${this.channelId}).`
|
|
601
|
+
|
|
602
|
+
// 消息应答指令:必复/可选两态 + lead 名册提示(全部外置 prompts/)
|
|
603
|
+
const respondBlock = renderPrompt(requireReply ? 'peer-reply-required' : 'peer-reply-optional', {
|
|
604
|
+
fromId,
|
|
605
|
+
messageId: msg.messageId,
|
|
606
|
+
})
|
|
607
|
+
const peerBody = renderPrompt('peer-message', {
|
|
608
|
+
fromId,
|
|
609
|
+
messageId: msg.messageId,
|
|
610
|
+
requireReply,
|
|
611
|
+
isReplyTo: isReply ? `in_reply_to: ${String(msg.metadata?.['x-aw-in-reply-to'])}` : '',
|
|
612
|
+
msgText,
|
|
613
|
+
respondBlock,
|
|
614
|
+
})
|
|
615
|
+
const lines: string[] = [
|
|
616
|
+
roleLine,
|
|
617
|
+
``, this.contextPrefix(await this.teamRoster()),
|
|
618
|
+
``, this.systemManual(),
|
|
619
|
+
``,
|
|
620
|
+
...(request.memory ? [``, request.memory] : []),
|
|
621
|
+
``,
|
|
622
|
+
peerBody,
|
|
623
|
+
]
|
|
624
|
+
|
|
625
|
+
if (this.agentRole === 'lead') {
|
|
626
|
+
lines.push(``, renderPrompt('peer-lead-roster'))
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
yield* this.promptAndStream(lines.join('\n'), undefined, ctx.signal)
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** 消费待回执上下文(自动关联兜底):取走即清,避免跨消息污染 */
|
|
633
|
+
private takeReplyContext(): { fromId: string, messageId: string } | null {
|
|
634
|
+
const ctx = this.replyContext
|
|
635
|
+
this.replyContext = null
|
|
636
|
+
return ctx
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// ===== 内部:prompt 发送 + 事件流桥接 =====
|
|
640
|
+
|
|
641
|
+
private async* promptAndStream(
|
|
642
|
+
prompt: string,
|
|
643
|
+
taskId: string | undefined,
|
|
644
|
+
signal: AbortSignal,
|
|
645
|
+
): AsyncGenerator<AgentEvent, void, unknown> {
|
|
646
|
+
const client = this.client
|
|
647
|
+
if (!client) {
|
|
648
|
+
throw new Error('omp 客户端未就绪')
|
|
649
|
+
}
|
|
650
|
+
const queue: AgentEvent[] = []
|
|
651
|
+
let resolveWait: (() => void) | null = null
|
|
652
|
+
let isDone = false
|
|
653
|
+
// 回合生命周期:prompt 已发出 → turnActive;首条 message_update → streaming;message_end/turn_end → 结束
|
|
654
|
+
this.turnActive = true
|
|
655
|
+
this.streaming = false
|
|
656
|
+
this.turnText = ''
|
|
657
|
+
// 正文流式差额账本:contentIndex → 已流出长度。部分 provider(reasoning 模型/
|
|
658
|
+
// openai-completions)的 text 块不走 text_delta(只有换行),全文在 text_end.content
|
|
659
|
+
// 落定 —— 差额兜底把未流出部分补发为 delta,保证前端流式不缺正文。
|
|
660
|
+
const textSent = new Map<number, number>()
|
|
661
|
+
const unsubState = client.onEvent((event) => {
|
|
662
|
+
if (event.type === 'message_update') {
|
|
663
|
+
const ev = event.assistantMessageEvent as
|
|
664
|
+
| { type?: string, delta?: string, content?: string, contentIndex?: number }
|
|
665
|
+
| undefined
|
|
666
|
+
if (ev?.type === 'text_delta') {
|
|
667
|
+
this.streaming = true
|
|
668
|
+
const text = ev.delta ?? ''
|
|
669
|
+
const ci = typeof ev.contentIndex === 'number' ? ev.contentIndex : 0
|
|
670
|
+
textSent.set(ci, (textSent.get(ci) ?? 0) + text.length)
|
|
671
|
+
this.turnText += text
|
|
672
|
+
// LLM 流式增量透出(AEP agent.delta 事件源;P2):仅 worker/peer 转本走生成器
|
|
673
|
+
if (text) enqueueDelta(text)
|
|
674
|
+
}
|
|
675
|
+
else if (ev?.type === 'text_end' && typeof ev.content === 'string') {
|
|
676
|
+
const ci = typeof ev.contentIndex === 'number' ? ev.contentIndex : 0
|
|
677
|
+
const sent = textSent.get(ci) ?? 0
|
|
678
|
+
if (ev.content.length > sent) {
|
|
679
|
+
const chunk = ev.content.slice(sent)
|
|
680
|
+
textSent.set(ci, ev.content.length)
|
|
681
|
+
this.streaming = true
|
|
682
|
+
this.turnText += chunk
|
|
683
|
+
enqueueDelta(chunk)
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (event.type === 'message_end' || event.type === 'turn_end') {
|
|
688
|
+
this.streaming = false
|
|
689
|
+
flushDelta()
|
|
690
|
+
}
|
|
691
|
+
if (event.type === 'agent_end' && event.isTerminal !== false) {
|
|
692
|
+
this.turnActive = false
|
|
693
|
+
this.streaming = false
|
|
694
|
+
flushDelta()
|
|
695
|
+
}
|
|
696
|
+
if (event.type === '__process_exit__' || event.type === '__error__') {
|
|
697
|
+
this.turnActive = false
|
|
698
|
+
this.streaming = false
|
|
699
|
+
}
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
const enqueue = (event: AgentEvent): void => {
|
|
703
|
+
queue.push(event)
|
|
704
|
+
if (event.kind === 'done' || event.kind === 'error') isDone = true
|
|
705
|
+
lastActivity = Date.now()
|
|
706
|
+
resolveWait?.()
|
|
707
|
+
resolveWait = null
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// 停滞看门狗:整轮无任何 omp 事件(挂死的 LLM 调用/子进程僵死)时中止回合,
|
|
711
|
+
// 否则消息永久卡 consuming、队友消息无限堆积。host 工具内阻塞
|
|
712
|
+
// (poll_messages 最长 180s)与慢 provider(实测单步可 >5min)都有
|
|
713
|
+
// tool/状态事件刷新计时;默认 600s 覆盖慢回合,只拦真僵死。
|
|
714
|
+
const idleTimeoutMs = this.config.promptTimeoutMs ?? 600_000
|
|
715
|
+
let lastActivity = Date.now()
|
|
716
|
+
let stallTimer: ReturnType<typeof setTimeout> | null = null
|
|
717
|
+
|
|
718
|
+
// LLM 流式增量缓冲:50ms 批量合并为一帧 delta(防高频 text_delta 洪泛 WS)
|
|
719
|
+
let deltaBuf = ''
|
|
720
|
+
let deltaTimer: ReturnType<typeof setTimeout> | null = null
|
|
721
|
+
const flushDelta = (): void => {
|
|
722
|
+
if (deltaTimer) {
|
|
723
|
+
clearTimeout(deltaTimer)
|
|
724
|
+
deltaTimer = null
|
|
725
|
+
}
|
|
726
|
+
if (!deltaBuf) return
|
|
727
|
+
const text = deltaBuf
|
|
728
|
+
deltaBuf = ''
|
|
729
|
+
enqueue({ kind: 'delta', delta: { text } })
|
|
730
|
+
}
|
|
731
|
+
const enqueueDelta = (text: string): void => {
|
|
732
|
+
deltaBuf += text
|
|
733
|
+
deltaTimer ??= setTimeout(() => {
|
|
734
|
+
deltaTimer = null
|
|
735
|
+
flushDelta()
|
|
736
|
+
}, 50)
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// 订阅 omp 事件流
|
|
740
|
+
const unsub = client.onEvent((event) => {
|
|
741
|
+
for (const mapped of this.mapOmpEvent(event, taskId)) {
|
|
742
|
+
enqueue(mapped)
|
|
743
|
+
}
|
|
744
|
+
})
|
|
745
|
+
|
|
746
|
+
// abort 传导
|
|
747
|
+
const onAbort = (): void => {
|
|
748
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] run 被 abort(signal)→ 中止 omp 回合,taskId=${taskId ?? '-'}`)
|
|
749
|
+
client.send({ type: 'abort' }).catch(() => {})
|
|
750
|
+
if (!isDone) {
|
|
751
|
+
enqueue({ kind: 'done', final: taskId ? { taskId } : undefined })
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (signal.aborted) {
|
|
755
|
+
onAbort()
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
signal.addEventListener('abort', onAbort, { once: true })
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// 发送 prompt
|
|
762
|
+
try {
|
|
763
|
+
await client.send({ type: 'prompt', message: prompt })
|
|
764
|
+
}
|
|
765
|
+
catch (err) {
|
|
766
|
+
unsub()
|
|
767
|
+
signal.removeEventListener('abort', onAbort)
|
|
768
|
+
// 僵死进程回收:prompt send 失败 = stdio 断裂或 60s 命令确认超时 —— 进程
|
|
769
|
+
// "活着但不干活"(alive-but-wedged)。若仅报错不清理,消息重试(≤2 次)会
|
|
770
|
+
// 复用同一僵死 stdio 每次空转 60s;必须杀掉并置空,下回合 ensureClient
|
|
771
|
+
// 全新重生(host tools/模型/终端 tap 随重建)。
|
|
772
|
+
log.warn(`[OmpRpcAgent:${this.selfAgentId}] prompt 失败 → 回收可疑僵死进程 pid=${client.pid}`)
|
|
773
|
+
this.killProcess()
|
|
774
|
+
this.client = null
|
|
775
|
+
this.hostToolsRegistered = false
|
|
776
|
+
yield {
|
|
777
|
+
kind: 'error',
|
|
778
|
+
error: {
|
|
779
|
+
code: 'PROMPT_FAILED',
|
|
780
|
+
message: `omp prompt 失败: ${err instanceof Error ? err.message : String(err)}`,
|
|
781
|
+
},
|
|
782
|
+
}
|
|
783
|
+
return
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// 产出事件直到 done/error
|
|
787
|
+
try {
|
|
788
|
+
while (!isDone || queue.length > 0) {
|
|
789
|
+
if (queue.length === 0 && !isDone) {
|
|
790
|
+
const remaining = idleTimeoutMs - (Date.now() - lastActivity)
|
|
791
|
+
if (remaining <= 0) {
|
|
792
|
+
client.send({ type: 'abort' }).catch(() => {})
|
|
793
|
+
// 子进程大概率已僵死:击杀并清引用,下回合 ensureClient 全新重生
|
|
794
|
+
this.killProcess()
|
|
795
|
+
this.client = null
|
|
796
|
+
this.hostToolsRegistered = false
|
|
797
|
+
enqueue({
|
|
798
|
+
kind: 'error',
|
|
799
|
+
error: {
|
|
800
|
+
code: 'TURN_STALLED',
|
|
801
|
+
message: `回合停滞 ${Math.round(idleTimeoutMs / 1000)}s 无 omp 事件,已中止重置(消息按已处理落账,后续消息继续)`,
|
|
802
|
+
},
|
|
803
|
+
})
|
|
804
|
+
continue
|
|
805
|
+
}
|
|
806
|
+
await new Promise<void>((r) => {
|
|
807
|
+
resolveWait = r
|
|
808
|
+
stallTimer = setTimeout(() => {
|
|
809
|
+
resolveWait = null
|
|
810
|
+
r()
|
|
811
|
+
}, remaining + 100)
|
|
812
|
+
})
|
|
813
|
+
if (stallTimer) {
|
|
814
|
+
clearTimeout(stallTimer)
|
|
815
|
+
stallTimer = null
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
while (queue.length > 0) {
|
|
819
|
+
yield queue.shift()!
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
finally {
|
|
824
|
+
if (stallTimer) clearTimeout(stallTimer)
|
|
825
|
+
// 事件监听必须全部解除:本 client 跨回合长期复用,残留的 mapOmpEvent
|
|
826
|
+
// 闭包会在后续每一帧继续执行并把事件 push 进孤儿队列(内存/CPU 随
|
|
827
|
+
// 回合数线性劣化,直接违背进程常驻的稳定性目标)
|
|
828
|
+
unsubState()
|
|
829
|
+
unsub()
|
|
830
|
+
signal.removeEventListener('abort', onAbort)
|
|
831
|
+
this.turnActive = false
|
|
832
|
+
this.streaming = false
|
|
833
|
+
this.currentTaskId = null
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// ===== 内部:omp 事件 → AgentEvent 映射 =====
|
|
838
|
+
|
|
839
|
+
private mapOmpEvent(event: AgentSessionEvent, taskId: string | undefined): AgentEvent[] {
|
|
840
|
+
switch (event.type) {
|
|
841
|
+
case 'agent_start':
|
|
842
|
+
return [{
|
|
843
|
+
kind: 'status',
|
|
844
|
+
status: { state: 'WORKING', timestamp: new Date().toISOString() },
|
|
845
|
+
}]
|
|
846
|
+
case 'message_end': {
|
|
847
|
+
// 消息完成:如果有 message 内容,产出为 status 事件(任务历史追踪)
|
|
848
|
+
const msg = event.message
|
|
849
|
+
if (msg && Array.isArray(msg.content)) {
|
|
850
|
+
const text = (msg.content as Array<{ type?: string, text?: string }>)
|
|
851
|
+
.filter(c => c.type === 'text')
|
|
852
|
+
.map(c => c.text ?? '')
|
|
853
|
+
.join('')
|
|
854
|
+
if (text) {
|
|
855
|
+
const a2aMsg: A2AMessage = {
|
|
856
|
+
messageId: randomUUID(),
|
|
857
|
+
contextId: this.channelId,
|
|
858
|
+
role: 'ROLE_AGENT',
|
|
859
|
+
parts: [{ text }],
|
|
860
|
+
}
|
|
861
|
+
return [{
|
|
862
|
+
kind: 'status',
|
|
863
|
+
status: { state: 'WORKING', message: a2aMsg, timestamp: new Date().toISOString() },
|
|
864
|
+
}]
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return []
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
case 'tool_execution_start': {
|
|
871
|
+
const toolName = event.toolName ?? 'tool'
|
|
872
|
+
return [{
|
|
873
|
+
kind: 'status',
|
|
874
|
+
status: {
|
|
875
|
+
state: 'WORKING',
|
|
876
|
+
message: {
|
|
877
|
+
messageId: randomUUID(),
|
|
878
|
+
contextId: this.channelId,
|
|
879
|
+
role: 'ROLE_AGENT',
|
|
880
|
+
parts: [{ text: `🔧 ${toolName}${toolArgsPreview(event.args)}` }],
|
|
881
|
+
},
|
|
882
|
+
timestamp: new Date().toISOString(),
|
|
883
|
+
},
|
|
884
|
+
}]
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
case 'agent_end': {
|
|
888
|
+
if (event.isTerminal === false) return []
|
|
889
|
+
const events: AgentEvent[] = []
|
|
890
|
+
// 提取最终 assistant 文本作为 artifact
|
|
891
|
+
const messages = event.messages ?? []
|
|
892
|
+
const assistantText = messages
|
|
893
|
+
.filter(m => m.role === 'assistant')
|
|
894
|
+
.flatMap(m => m.content.filter(c => c.type === 'text').map(c => c.text ?? ''))
|
|
895
|
+
.join('')
|
|
896
|
+
// 回合内的 provider/API 失败(限额 429、鉴权、上游 5xx 等):omp 把错误挂在最后一条
|
|
897
|
+
// assistant 消息上(stopReason='error' + errorStatus/errorMessage)而非 __error__ 帧。
|
|
898
|
+
// 不映射出来,上层只能看到「回合结束无产出」,真实原因彻底丢失(且照常重试到耗尽)。
|
|
899
|
+
const failure = [...messages].reverse().find((m) => {
|
|
900
|
+
const mm = m as { role?: string, stopReason?: string, errorMessage?: string }
|
|
901
|
+
return mm.role === 'assistant' && (mm.stopReason === 'error' || typeof mm.errorMessage === 'string')
|
|
902
|
+
}) as { errorStatus?: number, errorMessage?: string } | undefined
|
|
903
|
+
if (failure) {
|
|
904
|
+
const code = failure.errorStatus != null ? `OMP_LLM_${failure.errorStatus}` : 'OMP_LLM_ERROR'
|
|
905
|
+
const detail = failure.errorMessage ?? 'harness 回合以错误结束(无错误详情)'
|
|
906
|
+
log.error(`[OmpRpcAgent:${this.selfAgentId}] harness 回合失败 ${code}: ${detail}`)
|
|
907
|
+
// error 事件即回合终点(队列侧据此收口),不再追加 done
|
|
908
|
+
return [{ kind: 'error', error: { code, message: detail } }]
|
|
909
|
+
}
|
|
910
|
+
if (assistantText) {
|
|
911
|
+
events.push({
|
|
912
|
+
kind: 'artifact',
|
|
913
|
+
artifact: {
|
|
914
|
+
artifactId: randomUUID(),
|
|
915
|
+
name: 'output',
|
|
916
|
+
parts: [{ text: assistantText }],
|
|
917
|
+
},
|
|
918
|
+
lastChunk: true,
|
|
919
|
+
totalChunks: 1,
|
|
920
|
+
})
|
|
921
|
+
}
|
|
922
|
+
events.push({ kind: 'done', final: taskId ? { taskId } : undefined })
|
|
923
|
+
return events
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
case '__process_exit__':
|
|
927
|
+
return [{
|
|
928
|
+
kind: 'error',
|
|
929
|
+
error: { code: 'OMP_PROCESS_EXIT', message: 'omp 子进程意外退出' },
|
|
930
|
+
}]
|
|
931
|
+
|
|
932
|
+
case '__error__':
|
|
933
|
+
return [{
|
|
934
|
+
kind: 'error',
|
|
935
|
+
error: { code: 'OMP_ERROR', message: (event as { error?: string }).error ?? 'omp 未知错误' },
|
|
936
|
+
}]
|
|
937
|
+
|
|
938
|
+
default:
|
|
939
|
+
return []
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// ===== 内部:prompt 构建 =====
|
|
944
|
+
|
|
945
|
+
private async buildWorkerPrompt(taskId: string, taskText: string, memory?: string): Promise<string> {
|
|
946
|
+
const parts: string[] = []
|
|
947
|
+
|
|
948
|
+
const ctx = this.contextPrefix(await this.teamRoster())
|
|
949
|
+
if (ctx) parts.push(ctx)
|
|
950
|
+
if (memory) parts.push(memory)
|
|
951
|
+
parts.push(this.systemManual())
|
|
952
|
+
parts.push(renderPrompt('worker-workflow', {
|
|
953
|
+
agentName: this.agentName,
|
|
954
|
+
channelId: this.channelId,
|
|
955
|
+
taskId,
|
|
956
|
+
taskText,
|
|
957
|
+
}))
|
|
958
|
+
|
|
959
|
+
return parts.join('\n')
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* 成员能力画像(koda 运营信号借鉴):按任务历史计算
|
|
964
|
+
* total/completed/failed/successRate/avgDurationMs(仅统计已终态任务)。
|
|
965
|
+
* 数据源即 supervise 快照内的任务列表——零额外查询,证据随历史自然累积。
|
|
966
|
+
*/
|
|
967
|
+
private capabilityProfiles(snapshot: SupervisionSnapshot): Map<string, { total: number, completed: number, failed: number, successRate: number, avgDurationMs: number }> {
|
|
968
|
+
const byAgent = new Map<string, { total: number, completed: number, failed: number, durationSum: number }>()
|
|
969
|
+
for (const t of snapshot.tasks) {
|
|
970
|
+
if (!['COMPLETED', 'FAILED'].includes(t.state)) continue
|
|
971
|
+
const agg = byAgent.get(t.assigneeId) ?? { total: 0, completed: 0, failed: 0, durationSum: 0 }
|
|
972
|
+
agg.total += 1
|
|
973
|
+
if (t.state === 'COMPLETED') {
|
|
974
|
+
agg.completed += 1
|
|
975
|
+
agg.durationSum += Math.max(0, new Date(t.updatedAt).getTime() - new Date(t.createdAt).getTime())
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
agg.failed += 1
|
|
979
|
+
}
|
|
980
|
+
byAgent.set(t.assigneeId, agg)
|
|
981
|
+
}
|
|
982
|
+
const out = new Map<string, { total: number, completed: number, failed: number, successRate: number, avgDurationMs: number }>()
|
|
983
|
+
for (const [agentId, agg] of byAgent) {
|
|
984
|
+
out.set(agentId, {
|
|
985
|
+
total: agg.total,
|
|
986
|
+
completed: agg.completed,
|
|
987
|
+
failed: agg.failed,
|
|
988
|
+
successRate: agg.total > 0 ? agg.completed / agg.total : 0,
|
|
989
|
+
avgDurationMs: agg.completed > 0 ? agg.durationSum / agg.completed : 0,
|
|
990
|
+
})
|
|
991
|
+
}
|
|
992
|
+
return out
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
private async buildSupervisePrompt(snapshot: SupervisionSnapshot, memory?: string): Promise<string> {
|
|
996
|
+
const parts: string[] = []
|
|
997
|
+
|
|
998
|
+
const ctx = this.contextPrefix(await this.teamRoster())
|
|
999
|
+
if (ctx) parts.push(ctx)
|
|
1000
|
+
if (memory) parts.push(memory)
|
|
1001
|
+
parts.push(this.systemManual())
|
|
1002
|
+
// 格式化成员(含队列上下文 + 实时进度 + 停滞标记 + 能力画像:koda 运营信号借鉴 —
|
|
1003
|
+
// 按任务历史算成功率/平均耗时/失败数,lead 按证据而非直觉选人)。
|
|
1004
|
+
// 进度透出(heads-up):lead 看到每个 worker 正在执行的进度 %;stalled 标记
|
|
1005
|
+
// 直接提示"忙碌但进展停滞 → 应介入",从根上杜绝"worker 在跑但 lead 不知道"。
|
|
1006
|
+
const capability = this.capabilityProfiles(snapshot)
|
|
1007
|
+
const members = snapshot.members.map((m) => {
|
|
1008
|
+
const cap = capability.get(m.agentId)
|
|
1009
|
+
const capLine = cap && cap.total > 0
|
|
1010
|
+
? `, 成功率=${Math.round(cap.successRate * 100)}%, 均耗时=${Math.round(cap.avgDurationMs / 1000)}s, 失败=${cap.failed}`
|
|
1011
|
+
: ', 暂无历史'
|
|
1012
|
+
const prog = m.currentTaskProgress != null
|
|
1013
|
+
? `, progress=${m.currentTaskProgress}%${m.stalled ? ' [STALLED 停滞,请介入:notify/reassign/cancel]' : ''}`
|
|
1014
|
+
: ''
|
|
1015
|
+
const execTitle = m.currentTaskTitle ? `「${m.currentTaskTitle}」` : ''
|
|
1016
|
+
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})`
|
|
1017
|
+
}).join('\n')
|
|
1018
|
+
|
|
1019
|
+
// 格式化任务(createdAt ASC = FIFO 顺序);COMPLETED 附交付预览(lead 直接看到 worker 成果)
|
|
1020
|
+
const tasks = snapshot.tasks.map((t) => {
|
|
1021
|
+
const deliverable = t.state === 'COMPLETED' && t.artifacts.length > 0
|
|
1022
|
+
? ` — 交付:${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) || '(空)'}`
|
|
1023
|
+
: ''
|
|
1024
|
+
const artifacts = t.artifacts.length > 0 ? `, artifacts=${t.artifacts.length}` : ''
|
|
1025
|
+
return ` - ${t.id} [${t.state}] "${t.title}" (assignee=${t.assigneeId}, progress=${t.progress}%${artifacts})${deliverable}`
|
|
1026
|
+
}).join('\n')
|
|
1027
|
+
|
|
1028
|
+
// 未完成子任务数
|
|
1029
|
+
const pending = Object.entries(snapshot.pendingChildren)
|
|
1030
|
+
.map(([parentId, count]) => ` ${parentId}: ${count} pending`)
|
|
1031
|
+
.join('\n')
|
|
1032
|
+
|
|
1033
|
+
// 最近邮件(最新在前):worker 间点对点通信/回执 —— 判断"结果是否已被产出"的依据
|
|
1034
|
+
const mail = (snapshot.mail ?? []).map((m) => {
|
|
1035
|
+
const from = m.fromAgentId ?? '(system)'
|
|
1036
|
+
const to = m.toAgentId ?? '(broadcast)'
|
|
1037
|
+
const body = m.parts.map(p => 'text' in p ? p.text : JSON.stringify('data' in p ? p.data : p)).join(' ').trim().slice(0, 140)
|
|
1038
|
+
const label = m.metadata?.['x-aw-task-kind'] === 'assign'
|
|
1039
|
+
? 'task-assign'
|
|
1040
|
+
: m.metadata?.['x-aw-msg-priority'] === 'immediate' ? 'immediate' : 'peer'
|
|
1041
|
+
return ` - [${m.createdAt.slice(11, 19)}] ${from} → ${to} (${label}): ${body || '(empty)'}`
|
|
1042
|
+
}).join('\n')
|
|
1043
|
+
|
|
1044
|
+
// 检测执行模式(从 lead 自己的任务 description 前缀)
|
|
1045
|
+
const leadTask = snapshot.tasks.find(t =>
|
|
1046
|
+
t.assigneeId === snapshot.members.find(m => m.role === 'lead')?.agentId
|
|
1047
|
+
&& (t.state === 'SUBMITTED' || t.state === 'WORKING' || t.state === 'WAITING'),
|
|
1048
|
+
)
|
|
1049
|
+
const modeInfo = leadTask ? this.detectMode(leadTask.description ?? '') : null
|
|
1050
|
+
|
|
1051
|
+
parts.push(
|
|
1052
|
+
`You are "${this.agentName}", the lead coordinator of a multi-agent team (Channel: ${this.channelId}).`,
|
|
1053
|
+
`Tick #${snapshot.tick}`,
|
|
1054
|
+
``,
|
|
1055
|
+
`## Team Members (state + task queues)`,
|
|
1056
|
+
members || ' (none)',
|
|
1057
|
+
``,
|
|
1058
|
+
`## All Tasks (FIFO order)`,
|
|
1059
|
+
tasks || ' (none)',
|
|
1060
|
+
``,
|
|
1061
|
+
`## Pending Children Count`,
|
|
1062
|
+
pending || ' (none)',
|
|
1063
|
+
``,
|
|
1064
|
+
`## Recent Team Mail (newest first)`,
|
|
1065
|
+
mail || ' (none)',
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
// 模式特定指令(外置 mode-*.md)/ 默认协调指令(外置 lead-supervise.md)
|
|
1069
|
+
if (modeInfo) {
|
|
1070
|
+
parts.push('', ...this.buildModeInstructions(modeInfo))
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
parts.push('', renderPrompt('lead-supervise'))
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
return parts.join('\n')
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/** 从 description 前缀检测执行模式 */
|
|
1080
|
+
private detectMode(desc: string): { mode: string, criteria?: string, stages?: string[], interval?: number } | null {
|
|
1081
|
+
const match = desc.match(/^\[mode:(goal|loop|pipeline)\]/)
|
|
1082
|
+
if (!match) return null
|
|
1083
|
+
const mode = match[1]!
|
|
1084
|
+
const result: { mode: string, criteria?: string, stages?: string[], interval?: number } = { mode }
|
|
1085
|
+
if (mode === 'goal') {
|
|
1086
|
+
const crit = desc.match(/\[criteria:([^\]]+)\]/)
|
|
1087
|
+
if (crit) result.criteria = crit[1]
|
|
1088
|
+
}
|
|
1089
|
+
if (mode === 'loop') {
|
|
1090
|
+
const intv = desc.match(/\[interval:(\d+)\]/)
|
|
1091
|
+
if (intv) result.interval = parseInt(intv[1]!, 10)
|
|
1092
|
+
}
|
|
1093
|
+
if (mode === 'pipeline') {
|
|
1094
|
+
const stg = desc.match(/\[stages:([^\]]+)\]/)
|
|
1095
|
+
if (stg) result.stages = stg[1]!.split('->')
|
|
1096
|
+
}
|
|
1097
|
+
return result
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/** 构建模式特定指令(外置 mode-goal/loop/pipeline.md) */
|
|
1101
|
+
private buildModeInstructions(modeInfo: { mode: string, criteria?: string, stages?: string[], interval?: number }): string[] {
|
|
1102
|
+
if (modeInfo.mode === 'goal') {
|
|
1103
|
+
return [renderPrompt('mode-goal', { criteria: modeInfo.criteria ?? '任务描述中的需求已全部完成' })]
|
|
1104
|
+
}
|
|
1105
|
+
if (modeInfo.mode === 'loop') {
|
|
1106
|
+
return [renderPrompt('mode-loop', { interval: (modeInfo.interval ?? 60000) / 1000 })]
|
|
1107
|
+
}
|
|
1108
|
+
if (modeInfo.mode === 'pipeline') {
|
|
1109
|
+
const stageList = modeInfo.stages?.length
|
|
1110
|
+
? modeInfo.stages.map((s, i) => ` Stage ${i + 1}: ${s}`).join('\n')
|
|
1111
|
+
: ' Decompose the task into sequential stages yourself.'
|
|
1112
|
+
return [renderPrompt('mode-pipeline', { stageList })]
|
|
1113
|
+
}
|
|
1114
|
+
return []
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// ===== 内部:omp 客户端管理 =====
|
|
1118
|
+
|
|
1119
|
+
private async ensureClient(ctx: AgentRunContext): Promise<void> {
|
|
1120
|
+
if (!this.workspace) {
|
|
1121
|
+
this.workspace = ctx.workspace
|
|
1122
|
+
}
|
|
1123
|
+
if (!this.agentInfo) {
|
|
1124
|
+
// init() 可能未调用;从 ctx 推断
|
|
1125
|
+
this.channelId = ctx.channelId
|
|
1126
|
+
this.agentRole = ctx.role
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
if (!this.client || !this.client.alive) {
|
|
1130
|
+
// 旧客户端已退出(exit 事件/OS 存活校准 reconcile 触发)——必须丢弃并重生,
|
|
1131
|
+
// 否则后续回合会一直对着死 stdio 报 PROMPT_FAILED 烧重试配额
|
|
1132
|
+
this.client = null
|
|
1133
|
+
this.hostToolsRegistered = false
|
|
1134
|
+
const command = this.config.command ?? 'omp'
|
|
1135
|
+
const client = new OmpRpcClient({
|
|
1136
|
+
command,
|
|
1137
|
+
mode: this.rpcMode,
|
|
1138
|
+
args: this.config.args,
|
|
1139
|
+
cwd: this.config.cwd ?? process.cwd(),
|
|
1140
|
+
// 进程退出 → 注册表标记(供运行时资源监控);pid=-1 表示无法取得,忽略
|
|
1141
|
+
onExit: (pid, code) => {
|
|
1142
|
+
if (pid > 0) {
|
|
1143
|
+
markHarnessProcessExit(pid, code)
|
|
1144
|
+
markTerminalSessionExit(pid, code)
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1147
|
+
})
|
|
1148
|
+
await client.start()
|
|
1149
|
+
|
|
1150
|
+
// 登记 + 绑定 agent 身份(harness 进程监控的事实源)
|
|
1151
|
+
const pid = client.pid
|
|
1152
|
+
if (pid) {
|
|
1153
|
+
registerHarnessProcess(pid, {
|
|
1154
|
+
harness: 'omp',
|
|
1155
|
+
command,
|
|
1156
|
+
args: ['--mode', this.rpcMode, ...(this.config.args ?? [])],
|
|
1157
|
+
})
|
|
1158
|
+
bindHarnessProcess(pid, {
|
|
1159
|
+
agentId: this.selfAgentId,
|
|
1160
|
+
channelId: this.channelId,
|
|
1161
|
+
name: this.agentName,
|
|
1162
|
+
role: this.agentRole,
|
|
1163
|
+
})
|
|
1164
|
+
// 终端镜像 tap:全部 RPC 帧 → /monitor 终端(实时 TUI 渲染 + HITL)
|
|
1165
|
+
attachTerminalTap(client, {
|
|
1166
|
+
pid,
|
|
1167
|
+
harness: 'omp',
|
|
1168
|
+
agentId: this.selfAgentId,
|
|
1169
|
+
channelId: this.channelId,
|
|
1170
|
+
name: this.agentName,
|
|
1171
|
+
role: this.agentRole,
|
|
1172
|
+
})
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// 设置模型(如果配置了 provider/model 任一;omp 以当前已设值为缺省补全,
|
|
1176
|
+
// 只配其一时也显式发送,避免 respawn 后静默回落默认模型)
|
|
1177
|
+
if (this.config.provider || this.config.model) {
|
|
1178
|
+
try {
|
|
1179
|
+
await client.send({ type: 'set_model', provider: this.config.provider, modelId: this.config.model })
|
|
1180
|
+
}
|
|
1181
|
+
catch {
|
|
1182
|
+
// 模型设置失败不致命(用 omp 默认模型)
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// 注册 host tools(按角色差异化:lead 全量,worker 剔除调度/团队管理专属工具)
|
|
1187
|
+
client.onHostToolCall(req => this.handleHostTool(req))
|
|
1188
|
+
await client.send({ type: 'set_host_tools', tools: hostToolsForRole(this.agentRole) })
|
|
1189
|
+
|
|
1190
|
+
this.client = client
|
|
1191
|
+
this.hostToolsRegistered = true
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// ===== 内部:host tool handler(workspace 桥接) =====
|
|
1196
|
+
|
|
1197
|
+
private async handleHostTool(req: HostToolCallRequest): Promise<{ text: string, isError?: boolean }> {
|
|
1198
|
+
const ws = this.workspace
|
|
1199
|
+
if (!ws) {
|
|
1200
|
+
return { text: 'workspace 未就绪', isError: true }
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
try {
|
|
1204
|
+
switch (req.toolName) {
|
|
1205
|
+
case 'report_progress': {
|
|
1206
|
+
const progress = req.arguments.progress as number
|
|
1207
|
+
const message = req.arguments.message as string | undefined
|
|
1208
|
+
const taskId = this.currentTaskId
|
|
1209
|
+
if (!taskId) return { text: '无当前任务上下文', isError: true }
|
|
1210
|
+
await ws.reportTask({ taskId, progress, message })
|
|
1211
|
+
return { text: `进度已上报: ${progress}%${message ? ` (${message})` : ''}` }
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
case 'complete_task': {
|
|
1215
|
+
const summary = req.arguments.summary as string
|
|
1216
|
+
const deliverable = req.arguments.deliverable as string | undefined
|
|
1217
|
+
const taskId = (req.arguments.task_id as string | undefined) ?? this.currentTaskId
|
|
1218
|
+
if (!taskId) return { text: '无任务 ID', isError: true }
|
|
1219
|
+
// 父任务保护:有未完成子任务时拒绝完成(lead 须等 worker 交付)
|
|
1220
|
+
const allTasks = await ws.listTasks()
|
|
1221
|
+
const incompleteChildren = allTasks.filter(t => t.parentId === taskId && t.state !== 'COMPLETED' && t.state !== 'CANCELED')
|
|
1222
|
+
if (incompleteChildren.length > 0) {
|
|
1223
|
+
return {
|
|
1224
|
+
text: `任务 ${taskId} 有 ${incompleteChildren.length} 个未完成子任务,不能完成父任务。请等待子任务完成。`,
|
|
1225
|
+
isError: true,
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
// 终态幂等:任务已被平台收口(看门狗取消/调度器完成)时不撞状态机 ——
|
|
1229
|
+
// 给 Agent 明确的"无需再完成,继续下一项"信号,杜绝重复重试烧 token
|
|
1230
|
+
const current = await ws.getTask(taskId)
|
|
1231
|
+
if (current && current.state !== 'SUBMITTED' && current.state !== 'ASSIGNED' && current.state !== 'WORKING' && current.state !== 'WAITING') {
|
|
1232
|
+
if (current.state === 'COMPLETED') {
|
|
1233
|
+
return { text: `任务 ${taskId} 已是完成状态(可能已被平台收口),无需重复完成。` }
|
|
1234
|
+
}
|
|
1235
|
+
try {
|
|
1236
|
+
const q = await ws.myQueue()
|
|
1237
|
+
const next = q.queued[0]
|
|
1238
|
+
return {
|
|
1239
|
+
text: `任务 ${taskId} 已被平台${current.state === 'CANCELED' ? '取消(如停滞回收/上级作废)' : '判定失败'},不能再标记完成 —— 这不是你的错误,也无需重试。${next ? `队列还有 ${q.queued.length} 项,下一项「${next.title}」即将开始,请继续处理。` : '队列为空,保持待命。'}`,
|
|
1240
|
+
isError: false,
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
catch {
|
|
1244
|
+
return { text: `任务 ${taskId} 已被平台${current.state === 'CANCELED' ? '取消' : '判定失败'},无需再完成,请继续处理队列下一项。` }
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const artifacts: A2AArtifact[] = []
|
|
1248
|
+
if (deliverable || summary) {
|
|
1249
|
+
artifacts.push({
|
|
1250
|
+
artifactId: randomUUID(),
|
|
1251
|
+
name: 'deliverable',
|
|
1252
|
+
parts: [{ text: deliverable ?? summary }],
|
|
1253
|
+
})
|
|
1254
|
+
}
|
|
1255
|
+
await ws.completeTask(taskId, artifacts)
|
|
1256
|
+
this.currentTaskId = null
|
|
1257
|
+
// 完成即衔接:报告队列余量与下一项(状态同步 + 驱动继续处理;
|
|
1258
|
+
// 消费循环随后会以新消息自动起回合,这里给 LLM 明确的继续信号)
|
|
1259
|
+
try {
|
|
1260
|
+
const q = await ws.myQueue()
|
|
1261
|
+
const next = q.queued[0]
|
|
1262
|
+
if (next) {
|
|
1263
|
+
return {
|
|
1264
|
+
text: `任务 ${taskId} 已完成(状态已同步为 COMPLETED)。你的队列还有 ${q.queued.length} 项待处理,下一项:「${next.title}」(即将自动开始;收到任务指派消息后按工作流执行,完成后同样调用 complete_task)。`,
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return { text: `任务 ${taskId} 已完成(状态已同步为 COMPLETED),队列为空。保持待命:新任务/实时消息会自动到达你的信箱。` }
|
|
1268
|
+
}
|
|
1269
|
+
catch {
|
|
1270
|
+
return { text: `任务 ${taskId} 已完成(状态已同步为 COMPLETED)。` }
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
case 'dispatch_task': {
|
|
1275
|
+
const assigneeId = req.arguments.assignee_id as string
|
|
1276
|
+
const title = req.arguments.title as string
|
|
1277
|
+
const description = req.arguments.description as string | undefined
|
|
1278
|
+
const parentTaskId = req.arguments.parent_task_id as string | undefined
|
|
1279
|
+
const routeReason = req.arguments.route_reason as string | undefined
|
|
1280
|
+
const task = await ws.dispatchTask({ assigneeId, title, description, parentTaskId, routeReason })
|
|
1281
|
+
return { text: `子任务 ${task.id} 已创建并指派 → ${assigneeId}(父任务 ${parentTaskId ?? '无'},标题: ${title}${routeReason ? `,路由理由: ${routeReason}` : ''})` }
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
case 'send_message_to_agent': {
|
|
1285
|
+
const toAgentId = req.arguments.to_agent_id as string
|
|
1286
|
+
const message = req.arguments.message as string
|
|
1287
|
+
// 回执自动实时:回复(in_reply_to)默认提升为 immediate——
|
|
1288
|
+
// 接收方正等待该结果,realtime 路由会把回复直接注入其运行中的会话
|
|
1289
|
+
let priority = (req.arguments.priority as string | undefined) ?? 'task'
|
|
1290
|
+
const metadata: Record<string, unknown> = {}
|
|
1291
|
+
// 触发器:要求对方回复 / 标记本消息是对某消息的回复(回执关联)
|
|
1292
|
+
if (req.arguments.require_reply === true) metadata['x-aw-require-reply'] = 'true'
|
|
1293
|
+
let inReplyTo = req.arguments.in_reply_to as string | undefined
|
|
1294
|
+
// 自动关联兜底:LLM 省略 in_reply_to 时,按待回执上下文盖章
|
|
1295
|
+
// (触发消息要求回复 → 本次发送即回执;发给原发送者且无显式 in_reply_to)
|
|
1296
|
+
const replyCtx = this.takeReplyContext()
|
|
1297
|
+
if (!inReplyTo && replyCtx && replyCtx.fromId === toAgentId) {
|
|
1298
|
+
inReplyTo = replyCtx.messageId
|
|
1299
|
+
}
|
|
1300
|
+
if (inReplyTo) {
|
|
1301
|
+
metadata['x-aw-in-reply-to'] = inReplyTo
|
|
1302
|
+
if (priority === 'task') priority = 'immediate'
|
|
1303
|
+
}
|
|
1304
|
+
metadata['x-aw-msg-priority'] = priority
|
|
1305
|
+
const sent = await ws.sendMessage({ toAgentId, parts: [{ text: message }], metadata })
|
|
1306
|
+
const triggerNote = inReplyTo
|
|
1307
|
+
? `(回复 ${inReplyTo.slice(0, 8)}…,已实时推送给对方)`
|
|
1308
|
+
: metadata['x-aw-require-reply'] === 'true' ? '(已要求对方回复)' : ''
|
|
1309
|
+
return { text: `消息 ${sent.messageId.slice(0, 8)}… 已发送给 ${toAgentId}(priority=${priority})${triggerNote}` }
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
case 'refuse_task': {
|
|
1313
|
+
const taskId = req.arguments.task_id as string
|
|
1314
|
+
const reason = req.arguments.reason as string
|
|
1315
|
+
if (!taskId || !reason) return { text: '缺少 task_id 或 reason', isError: true }
|
|
1316
|
+
const r = await ws.refuseTask(taskId, reason)
|
|
1317
|
+
const notified = r.notifiedTo ? `拒绝回执已送达 ${r.notifiedTo.slice(0, 8)}` : '无回执对象(创建者已不在 channel)'
|
|
1318
|
+
return {
|
|
1319
|
+
text: `任务 ${taskId.slice(0, 8)}("${r.task.title}") 已拒绝(state=${r.task.state});${notified}。调度器将改派他人,请勿再处理该任务。`,
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
case 'poll_messages': {
|
|
1324
|
+
const limit = (req.arguments.limit as number | undefined) ?? 10
|
|
1325
|
+
// 阻塞等待(真即时):Mailbox 到信回调毫秒级唤醒 + 250ms 兜底重查;
|
|
1326
|
+
// 对方消息一到立即返回 —— 取代旧每秒盲查(阻塞窗口内到信可能被
|
|
1327
|
+
// 实时注入层抢先标记消费,盲查扑空即"轮询查空"丢失)
|
|
1328
|
+
const waitSec = Math.min(180, Math.max(0, Number(req.arguments.wait_seconds ?? 0) || 0))
|
|
1329
|
+
const msgs = await ws.waitMailbox(limit, waitSec * 1000)
|
|
1330
|
+
if (msgs.length === 0) {
|
|
1331
|
+
return {
|
|
1332
|
+
text: waitSec > 0
|
|
1333
|
+
? `等待 ${waitSec}s 后收件箱仍为空。若此前已有"[实时消息 from X]"注入你的会话,那就是回复本身(无需再轮询);否则可继续处理其他工作,对方回复会以新回合送达。`
|
|
1334
|
+
: '收件箱为空(无未消费消息)。等待回复请用 wait_seconds 参数阻塞等待,不要反复空轮询。',
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
// 读即取:协作消息(非任务投递)取出即确认——防止消费循环稍后把同一消息再跑一遍回合;
|
|
1338
|
+
// 任务指派(assign)不确认,仍由执行循环处理
|
|
1339
|
+
const ackIds = msgs
|
|
1340
|
+
.filter(m => !m.metadata?.['x-aw-task-kind'])
|
|
1341
|
+
.map(m => m.messageId)
|
|
1342
|
+
if (ackIds.length > 0) await ws.ackMailbox(ackIds)
|
|
1343
|
+
// 触发上下文(轮询路径的回执自动关联):取到的 require-reply 协作消息 →
|
|
1344
|
+
// 后续 send_message_to_agent 回给发送者时自动盖 in_reply_to(与 peer 回合同源)
|
|
1345
|
+
const trigger = msgs.find(m =>
|
|
1346
|
+
m.metadata?.['x-aw-require-reply'] === 'true'
|
|
1347
|
+
&& typeof m.metadata?.['x-aw-from-agent'] === 'string')
|
|
1348
|
+
if (trigger) {
|
|
1349
|
+
this.replyContext = {
|
|
1350
|
+
fromId: String(trigger.metadata!['x-aw-from-agent']),
|
|
1351
|
+
messageId: trigger.messageId,
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
const text = msgs.map((m, i) => {
|
|
1355
|
+
const from = m.metadata?.['x-aw-from-agent'] ?? '?'
|
|
1356
|
+
const reply = m.metadata?.['x-aw-in-reply-to']
|
|
1357
|
+
? ` (回复 ${String(m.metadata['x-aw-in-reply-to']).slice(0, 8)}…)`
|
|
1358
|
+
: ''
|
|
1359
|
+
// 需回复的消息附带回执指令:对方在等你的结果,须回信并关联原消息
|
|
1360
|
+
const needReply = m.metadata?.['x-aw-require-reply'] === 'true'
|
|
1361
|
+
? ` [需回复:用 send_message_to_agent 回 ${from},in_reply_to=${m.messageId}]`
|
|
1362
|
+
: ''
|
|
1363
|
+
const body = m.parts.map(p => 'text' in p ? p.text : '').join(' ')
|
|
1364
|
+
// 不截到 100 字符:谜面/任务书等长消息截半句会让收件人误判内容不完整
|
|
1365
|
+
return ` [${i + 1}/${msgs.length}] [from ${from}]${needReply}${reply} ${body.slice(0, 2000)}`
|
|
1366
|
+
}).join('\n')
|
|
1367
|
+
return {
|
|
1368
|
+
text:
|
|
1369
|
+
`未消费消息(${msgs.length},已读即取):\n${text}`
|
|
1370
|
+
+ (msgs.length > 1
|
|
1371
|
+
? '\n(收到多条:请按编号逐条处理并逐条回复,不要只回应最后一条或合并敷衍)'
|
|
1372
|
+
: ''),
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
case 'read_channel_mail': {
|
|
1377
|
+
const limit = (req.arguments.limit as number | undefined) ?? 50
|
|
1378
|
+
const agentId = req.arguments.agent_id as string | undefined
|
|
1379
|
+
const mails = await ws.listMail({ limit, agentId })
|
|
1380
|
+
if (mails.length === 0) return { text: 'Channel 无邮件记录(或该成员无往来)' }
|
|
1381
|
+
const text = mails.map((m) => {
|
|
1382
|
+
const from = m.fromAgentId ?? '(系统)'
|
|
1383
|
+
const to = m.toAgentId ?? '(广播)'
|
|
1384
|
+
const body = partsToText(m.parts).trim().slice(0, 2000)
|
|
1385
|
+
const reply = m.metadata?.['x-aw-in-reply-to']
|
|
1386
|
+
? ` [回复 ${String(m.metadata['x-aw-in-reply-to']).slice(0, 8)}…]`
|
|
1387
|
+
: ''
|
|
1388
|
+
const label = m.metadata?.['x-aw-task-kind'] === 'assign'
|
|
1389
|
+
? '[任务指派]'
|
|
1390
|
+
: m.metadata?.['x-aw-msg-priority'] === 'immediate' ? '[实时]' : '[协作]'
|
|
1391
|
+
const state = m.state === 'pending' ? '未读' : m.state === 'consuming' ? '处理中' : '已读'
|
|
1392
|
+
return ` ${m.createdAt.slice(11, 19)} ${label} ${from} → ${to} (${state})${reply}: ${body || '(空)'}`
|
|
1393
|
+
}).join('\n')
|
|
1394
|
+
return { text: `Channel 邮件(${mails.length},倒序;可传 agent_id 查看指定成员信箱):\n${text}` }
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
case 'broadcast_message': {
|
|
1398
|
+
const message = req.arguments.message as string
|
|
1399
|
+
const priority = (req.arguments.priority as string | undefined) ?? 'task'
|
|
1400
|
+
const agents = await ws.listAgents()
|
|
1401
|
+
const others = agents.filter(a => a.id !== this.selfAgentId)
|
|
1402
|
+
for (const agent of others) {
|
|
1403
|
+
await ws.sendMessage({
|
|
1404
|
+
toAgentId: agent.id,
|
|
1405
|
+
parts: [{ text: message }],
|
|
1406
|
+
metadata: { 'x-aw-msg-priority': priority },
|
|
1407
|
+
})
|
|
1408
|
+
}
|
|
1409
|
+
return { text: `已广播给 ${others.length} 个 agent(priority=${priority})` }
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
case 'list_channel_tasks': {
|
|
1413
|
+
const tasks = await ws.listTasks()
|
|
1414
|
+
const text = tasks.map(t =>
|
|
1415
|
+
` ${t.id} [${t.state}] "${t.title}" assignee=${t.assigneeId} progress=${t.progress}%`,
|
|
1416
|
+
).join('\n')
|
|
1417
|
+
return { text: `Channel 任务(${tasks.length}):\n${text || '(空)'}` }
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
case 'get_my_task_queue': {
|
|
1421
|
+
const queue = await ws.myQueue()
|
|
1422
|
+
const fmt = (t: WorkspaceTask): string =>
|
|
1423
|
+
` ${t.id} [${t.state}] "${t.title}" progress=${t.progress}%`
|
|
1424
|
+
return {
|
|
1425
|
+
text: [
|
|
1426
|
+
`我的任务队列(${this.agentRole}):`,
|
|
1427
|
+
`执行中: ${queue.current ? `${queue.current.id} "${queue.current.title}" (${queue.current.progress}%)` : '(无)'}`,
|
|
1428
|
+
`待执行(${queue.queued.length},FIFO):`,
|
|
1429
|
+
...queue.queued.map(fmt),
|
|
1430
|
+
`已完成(${queue.completed.length}):`,
|
|
1431
|
+
...queue.completed.map(fmt),
|
|
1432
|
+
].join('\n'),
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
case 'get_queue_overview': {
|
|
1437
|
+
const overview = await ws.queueOverview()
|
|
1438
|
+
const lines = overview.map(s =>
|
|
1439
|
+
` ${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})`,
|
|
1440
|
+
)
|
|
1441
|
+
return { text: `团队队列总览(${overview.length}):\n${lines.join('\n') || '(空)'}` }
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
case 'reassign_task': {
|
|
1445
|
+
const taskId = req.arguments.task_id as string
|
|
1446
|
+
const toAgentId = req.arguments.to_agent_id as string
|
|
1447
|
+
const task = await ws.reassignTask(taskId, toAgentId)
|
|
1448
|
+
return { text: `任务 ${taskId}("${task.title}")已调配 → ${toAgentId}(state=${task.state})` }
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
case 'update_task': {
|
|
1452
|
+
const taskId = req.arguments.task_id as string
|
|
1453
|
+
const title = req.arguments.title as string | undefined
|
|
1454
|
+
const description = req.arguments.description as string | undefined
|
|
1455
|
+
const task = await ws.updateTask(taskId, { title, description })
|
|
1456
|
+
return { text: `任务 ${taskId} 已更新: "${task.title}"` }
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
case 'cancel_task': {
|
|
1460
|
+
const taskId = req.arguments.task_id as string
|
|
1461
|
+
// 守卫:goal/loop/pipeline 的 mode 父任务是用户的作业主任务,Agent 不得经工具取消
|
|
1462
|
+
// (模型偶发误判会毁掉整个目标;终止主任务只能由用户在界面/REST 操作)。
|
|
1463
|
+
// 目标未达成时的正确动作:dispatch_task 补派子任务;确认无法达成:complete_task 附结论说明。
|
|
1464
|
+
const target = await ws.getTask(taskId).catch(() => null)
|
|
1465
|
+
if (target && extractTaskMode(target)) {
|
|
1466
|
+
return {
|
|
1467
|
+
text: `拒绝:任务 ${taskId} 是 mode 父任务(${target.title}),不能用 cancel_task 取消。若目标未达成 → dispatch_task 派发子任务补齐差距;若确认无法达成 → complete_task 并在交付中说明未达成原因。终止整个作业请由用户操作。`,
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
await ws.cancelTask(taskId)
|
|
1471
|
+
return { text: `任务 ${taskId} 已取消并移出 assignee 队列` }
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
case 'list_team_agents': {
|
|
1475
|
+
const agents = await ws.listAgents()
|
|
1476
|
+
const text = agents.map(a =>
|
|
1477
|
+
` ${a.id} (${a.name}, role=${a.role}, harness=${a.harness})`,
|
|
1478
|
+
).join('\n')
|
|
1479
|
+
return { text: `团队成员(${agents.length}):\n${text || '(空)'}` }
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
case 'get_task_details': {
|
|
1483
|
+
const taskId = req.arguments.task_id as string
|
|
1484
|
+
const task: WorkspaceTask = await ws.getTask(taskId)
|
|
1485
|
+
const artifactText = task.artifacts.map(a =>
|
|
1486
|
+
` artifact ${a.artifactId}: ${a.parts.map(p => 'text' in p ? p.text.slice(0, 100) : '').join('; ')}`,
|
|
1487
|
+
).join('\n')
|
|
1488
|
+
return {
|
|
1489
|
+
text: `任务 ${task.id}\n 状态: ${task.state}\n 标题: ${task.title}\n 描述: ${task.description ?? '-'}\n 指派: ${task.assigneeId}\n 进度: ${task.progress}%\n 成果:\n${artifactText || ' (无)'}`,
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
case 'search_memory': {
|
|
1494
|
+
const query = req.arguments.query as string
|
|
1495
|
+
const scope = (req.arguments.scope as 'auto' | 'private' | 'shared' | undefined) ?? 'auto'
|
|
1496
|
+
const snippets = await ws.recallMemory({ query, scope, limit: req.arguments.limit as number | undefined })
|
|
1497
|
+
if (snippets.length === 0) {
|
|
1498
|
+
return { text: `记忆检索无命中(query="${query}", scope=${scope})。可尝试更换关键词或放宽 scope。` }
|
|
1499
|
+
}
|
|
1500
|
+
const lines = snippets.map(s =>
|
|
1501
|
+
` [${s.source}·${s.kind}·score=${s.score}] ${s.title}\n ${s.content}`,
|
|
1502
|
+
)
|
|
1503
|
+
return { text: `记忆检索结果(${snippets.length} 条, scope=${scope}):\n${lines.join('\n')}` }
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
case 'save_memory': {
|
|
1507
|
+
const title = req.arguments.title as string
|
|
1508
|
+
const content = req.arguments.content as string
|
|
1509
|
+
const scope = req.arguments.scope as 'private' | 'shared'
|
|
1510
|
+
const saved = await ws.saveMemory({
|
|
1511
|
+
title,
|
|
1512
|
+
content,
|
|
1513
|
+
importance: req.arguments.importance as number | undefined,
|
|
1514
|
+
scope,
|
|
1515
|
+
dedupKey: req.arguments.dedup_key as string | undefined,
|
|
1516
|
+
})
|
|
1517
|
+
const where = scope === 'shared' ? 'Channel 公共记忆(全员可检索)' : '本人私有记忆'
|
|
1518
|
+
return { text: `已沉淀到${where}: "${title}"(dedupKey=${saved.dedupKey})` }
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
case 'create_team_agent': {
|
|
1522
|
+
const name = req.arguments.name as string
|
|
1523
|
+
const harness = req.arguments.harness as string | undefined
|
|
1524
|
+
const systemPrompt = req.arguments.system_prompt as string | undefined
|
|
1525
|
+
const reason = req.arguments.reason as string | undefined
|
|
1526
|
+
const agent = await ws.createTeamMember({
|
|
1527
|
+
name,
|
|
1528
|
+
harness,
|
|
1529
|
+
config: systemPrompt ? { systemPromptPrefix: systemPrompt } : undefined,
|
|
1530
|
+
reason,
|
|
1531
|
+
})
|
|
1532
|
+
return {
|
|
1533
|
+
text: [
|
|
1534
|
+
`团队成员已创建并加入 channel:`,
|
|
1535
|
+
` id: ${agent.id}`,
|
|
1536
|
+
` name: ${agent.name}(role=worker, harness=${agent.harness})`,
|
|
1537
|
+
`新成员当前空闲,可立即 dispatch_task 指派任务;list_team_agents 可随时查看团队名册。`,
|
|
1538
|
+
].join('\n'),
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
case 'update_team_agent': {
|
|
1543
|
+
const agentId = req.arguments.agent_id as string
|
|
1544
|
+
const name = req.arguments.name as string | undefined
|
|
1545
|
+
const systemPrompt = req.arguments.system_prompt as string | undefined
|
|
1546
|
+
const enabled = req.arguments.enabled as boolean | undefined
|
|
1547
|
+
const reason = req.arguments.reason as string | undefined
|
|
1548
|
+
const agent = await ws.updateTeamMember(agentId, {
|
|
1549
|
+
name,
|
|
1550
|
+
config: systemPrompt !== undefined ? { systemPromptPrefix: systemPrompt } : undefined,
|
|
1551
|
+
enabled: enabled === undefined ? undefined : (enabled ? 1 : 0),
|
|
1552
|
+
reason,
|
|
1553
|
+
})
|
|
1554
|
+
return {
|
|
1555
|
+
text: `团队成员 ${agentId} 已更新:name="${agent.name}"${enabled !== undefined ? `, enabled=${enabled ? 1 : 0}` : ''};运行时将按新配置重载(下次任务生效)。`,
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
case 'remove_team_agent': {
|
|
1560
|
+
const agentId = req.arguments.agent_id as string
|
|
1561
|
+
const reason = req.arguments.reason as string | undefined
|
|
1562
|
+
const result = await ws.removeTeamMember(agentId, reason)
|
|
1563
|
+
const recycleNote = result.recycledTasks.length > 0
|
|
1564
|
+
? `其 ${result.recycledTasks.length} 个在途任务已回收(排队任务重派给剩余最短队列成员;执行中任务转 FAILED 待调度重试)。`
|
|
1565
|
+
: `该成员无在途任务。`
|
|
1566
|
+
return { text: `团队成员 ${agentId} 已移除。${recycleNote}` }
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
case 'my_industrial_nodes': {
|
|
1570
|
+
return toolMyIndustrialNodes(this.selfAgentId)
|
|
1571
|
+
}
|
|
1572
|
+
case 'dcw_control': {
|
|
1573
|
+
return toolDcwControl(this.selfAgentId, req.arguments as { node_id?: string, value?: number | string, hypothesis?: string, task_id?: string })
|
|
1574
|
+
}
|
|
1575
|
+
case 'daq_query': {
|
|
1576
|
+
return toolDaqQuery(this.selfAgentId, req.arguments as Parameters<typeof toolDaqQuery>[1])
|
|
1577
|
+
}
|
|
1578
|
+
case 'dcw_judge': {
|
|
1579
|
+
return toolDcwJudge(this.selfAgentId, req.arguments as { record_id?: string, verdict?: string, reason?: string })
|
|
1580
|
+
}
|
|
1581
|
+
case 'dcw_rollback': {
|
|
1582
|
+
return toolDcwRollback(this.selfAgentId, req.arguments as { record_id?: string, node_id?: string, to?: string })
|
|
1583
|
+
}
|
|
1584
|
+
case 'dcw_journal': {
|
|
1585
|
+
return toolDcwJournal(this.selfAgentId, req.arguments as { node_id?: string, recipe_id?: string, limit?: number | string })
|
|
1586
|
+
}
|
|
1587
|
+
default:
|
|
1588
|
+
return { text: `未知工具: ${req.toolName}`, isError: true }
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
catch (err) {
|
|
1592
|
+
return {
|
|
1593
|
+
text: `工具执行异常(${req.toolName}): ${err instanceof Error ? err.message : String(err)}`,
|
|
1594
|
+
isError: true,
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|