@@ -40,6 +40,7 @@ import path from 'node:path'
4040import os from 'node:os'
4141import fs from 'node:fs'
4242import fsp from 'node:fs/promises'
43+ import { pathToFileURL } from 'node:url'
4344import * as Y from 'yjs'
4445import { WebsocketProvider } from 'y-websocket'
4546import WebSocket from 'ws'
@@ -688,7 +689,7 @@ async function upsertBitableRecord(appToken, tableId, fields) {
688689}
689690
690691/** 元认知一轮跑完 → 同步云文档 + 多维表格 (异步, 失败不影响 IM 卡发送) */
691- async function archiveMetacognition ( { ctx, conclusionNode, newNodes, room, pngPath, screenshotPngUrl, screenshotSvgUrl } ) {
692+ export async function archiveMetacognition ( { ctx, conclusionNode, newNodes, room, pngPath, screenshotPngUrl, screenshotSvgUrl } ) {
692693 const result = { docUrl : null , bitableUrl : null }
693694 if ( ! FEISHU_BITABLE_APP_TOKEN && ! FEISHU_DOCS_FOLDER_TOKEN ) return result // 全没配, 跳过
694695
@@ -704,13 +705,39 @@ async function archiveMetacognition({ ctx, conclusionNode, newNodes, room, pngPa
704705 const rootNode = newNodes . find ( ( n ) => n . id === conclusionNode . data ?. projectRootId ) ||
705706 newNodes . find ( ( n ) => n . data ?. variant === 'goal' && n . data ?. projectMode )
706707 const profile = rootNode ?. data ?. project_profile || conclusionNode . data ?. project_profile || { }
707- const taskDagArr = rootNode ?. data ?. structure ?. task_dag || conclusionNode . data ?. structure ?. task_dag || [ ]
708+ const structure = rootNode ?. data ?. structure || conclusionNode . data ?. structure || { }
709+ const taskDagArr = structure ?. task_dag || [ ]
710+ // task DAG — bullet 列出, T-ID 当编号 (飞书 docx 数字列表会全渲染为 "1.", 改 bullet 避免误导)
708711 const taskDag = taskDagArr . map ( ( t , i ) => {
709- const deps = ( t . deps && t . deps . length ) ? ` (依赖: ${ t . deps . join ( ', ' ) } )` : ''
710- return `${ i + 1 } . **${ t . id || '' } ${ t . title } ** — ${ t . desc || '' } ${ deps } `
712+ const deps = ( t . deps && t . deps . length ) ? ` _(依赖: ${ t . deps . join ( ', ' ) } )_` : ''
713+ const desc = t . desc ? ` — ${ t . desc } ` : ''
714+ const io = [ ]
715+ if ( t . input ) io . push ( `输入: ${ t . input } ` )
716+ if ( t . output ) io . push ( `产出: ${ t . output } ` )
717+ const ioStr = io . length ? ` _[${ io . join ( ' · ' ) } ]_` : ''
718+ return `- **${ t . id || `T${ i + 1 } ` } · ${ t . title } **${ deps } ${ desc } ${ ioStr } `
711719 } ) . join ( '\n' )
712- // 真分支: 排除 root (variant=goal) 和 conclusion. 实际 task entity (variant=entity) 才是分支
713- const branches = newNodes . filter ( ( n ) => n . type === 'ontologyNode' && ! n . data ?. isConclusion && n . data ?. variant !== 'goal' && ! n . data ?. projectMode )
720+ // 执行阶段拓扑 — 哪些角色 / 任务并行, 哪些串行
721+ const stages = structure ?. execution_topology ?. stages || [ ]
722+ const rolesArr = structure ?. roles || [ ]
723+ const KIND_CN = { parallel : '并行' , sequential : '串行' , serial : '串行' , single : '单角色' }
724+ const stageLines = stages . map ( ( s ) => {
725+ const kind = KIND_CN [ s . kind ] || s . kind || '?'
726+ const rids = ( s . role_ids || [ ] )
727+ const roleNames = rids . map ( ( rid ) => {
728+ const r = rolesArr . find ( ( x ) => x . id === rid )
729+ return r ? `${ r . name || rid } ` : rid
730+ } ) . join ( ' / ' )
731+ return `- **阶段 ${ s . stage_index } ** _(${ kind } )_: ${ roleNames } `
732+ } ) . join ( '\n' )
733+ // 派生分支: 真正的拆解/反驳 (非 task 本体, 非 root, 非 conclusion)
734+ const branches = newNodes . filter ( ( n ) =>
735+ n . type === 'ontologyNode' &&
736+ ! n . data ?. isConclusion &&
737+ n . data ?. variant !== 'goal' &&
738+ ! n . data ?. projectMode &&
739+ ! n . data ?. projectTaskId // 排除 6 stage 自动建的 task entity (DAG 章节已展示)
740+ )
714741 const branchLines = branches . map ( ( n ) => `- ▸ **${ n . data ?. title || '' } ** — ${ n . data ?. description || n . data ?. content || '' } ` ) . join ( '\n' )
715742 // agentRole 节点 — 角色名 + 职责 + 工具 + 任务
716743 const roles = newNodes . filter ( ( n ) => n . type === 'agentRoleNode' )
@@ -723,12 +750,23 @@ async function archiveMetacognition({ ctx, conclusionNode, newNodes, room, pngPa
723750 return `- **${ name } ** — ${ resp } ${ tools } ${ tasks } `
724751 } ) . join ( '\n' )
725752
726- // 摘要扩充 — 合并 summary + reasoning + pros + cons + next_steps
753+ // 摘要扩充 — 把决策引擎所有结构化字段都摊开 (summary + key_insights + improvements + next_steps)
754+ // 决策引擎实际 schema: {verdict, score, summary, key_insights[], improvements[], next_steps[]}
755+ // 历史兼容: pros/cons/risks/reasoning 字段也保留拼接, 万一有旧版本数据
756+ const arrSection = ( label , arr ) => Array . isArray ( arr ) && arr . length
757+ ? `\n**${ label } **:\n\n${ arr . map ( ( s ) => `- ${ s } ` ) . join ( '\n' ) } `
758+ : ''
759+ const reasoningExtra = ( cObj . reasoning && cObj . reasoning !== cObj . summary )
760+ ? `\n\n**推理**:\n\n${ cObj . reasoning } `
761+ : ''
727762 const detailedSummary = [
728- summary ,
729- Array . isArray ( cObj . pros ) && cObj . pros . length ? `\n**优势**:\n${ cObj . pros . map ( p => `- ${ p } ` ) . join ( '\n' ) } ` : '' ,
730- Array . isArray ( cObj . cons ) && cObj . cons . length ? `\n**劣势**:\n${ cObj . cons . map ( c => `- ${ c } ` ) . join ( '\n' ) } ` : '' ,
731- Array . isArray ( cObj . next_steps ) && cObj . next_steps . length ? `\n**下一步**:\n${ cObj . next_steps . map ( s => `- ${ s } ` ) . join ( '\n' ) } ` : '' ,
763+ summary + reasoningExtra ,
764+ arrSection ( '核心洞察' , cObj . key_insights ) ,
765+ arrSection ( '改进建议' , cObj . improvements ) ,
766+ arrSection ( '优势' , cObj . pros ) ,
767+ arrSection ( '劣势' , cObj . cons ) ,
768+ arrSection ( '风险' , cObj . risks ) ,
769+ arrSection ( '下一步' , cObj . next_steps ) ,
732770 ] . filter ( Boolean ) . join ( '\n' )
733771
734772 // 1) 云文档 (markdown)
@@ -753,14 +791,15 @@ async function archiveMetacognition({ ctx, conclusionNode, newNodes, room, pngPa
753791 '' ,
754792 taskDag || '_无_' ,
755793 '' ,
756- '## 分支节点 ' ,
794+ '## 执行阶段拓扑 ' ,
757795 '' ,
758- branchLines || '_无_' ,
796+ stageLines || '_无_' ,
759797 '' ,
760798 '## 涌现角色' ,
761799 '' ,
762800 roleLines || '_无_' ,
763801 '' ,
802+ ...( branches . length ? [ '## 派生分支 (拆解 / 反驳)' , '' , branchLines , '' ] : [ ] ) ,
764803 '## 摘要 / 推理' ,
765804 '' ,
766805 detailedSummary || '_无_' ,
@@ -1219,4 +1258,7 @@ function start() {
12191258
12201259}
12211260
1222- start ( )
1261+ // 仅在直接 node 运行时启动 daemon — import 测试不会启动
1262+ if ( import . meta. url === pathToFileURL ( process . argv [ 1 ] ) . href ) {
1263+ start ( )
1264+ }
0 commit comments