aardio 文档

aardio 范例: 全自主智能体

import autos;
if(_WIN7_LATER) ::Shell32.SetCurrentProcessExplicitAppUserModelID(autos.appUserModelId) //在任务栏显示为分离图标(避免合并到 aardio.exe 分组)

import fonts.fontAwesome;
import com.interface.IRichEditOleCallback;
import win.clip;
import process.admin;
import win.inputBox;
import win.ui;
/*DSG{{*/
mainForm = win.form(text=autos.title;right=900;bottom=600;bgcolor=0xFFFFFF)
mainForm.add(
bkPrompt={cls="bkplus";left=222;top=547;right=788;bottom=594;border={color=0xC0C0C0;radius=16;width=1};db=1;dl=1;dr=1;z=1};
btnClear={cls="plus";left=134;top=556;right=169;bottom=585;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-22;name='FontAwesome');padding={left=8}};iconText='\uF014';notify=1;textPadding={left=25};z=6};
btnCopy={cls="plus";left=176;top=556;right=211;bottom=585;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-22;name='FontAwesome');padding={left=8}};iconText='\uF0C5';notify=1;textPadding={left=25};z=7};
btnFeishu={cls="plus";left=49;top=556;right=84;bottom=586;align="left";color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-22;name='FontAwesome')};iconText='\uF1D8';notify=1;textPadding={left=25};z=9};
btnImageAttachment={cls="plus";left=752;top=556;right=784;bottom=585;align="left";bgcolor=0xFFFFFF;border={radius=4};color=0x3C3C3C;db=1;dr=1;font=LOGFONT(h=-13);foreRepeat="scale";iconStyle={font=LOGFONT(h=-19;name='FontAwesome');padding={top=2}};iconText='\uF030';notify=1;paddingBottom=2;paddingLeft=2;paddingRight=2;paddingTop=2;textPadding={left=25};z=11};
btnSend={cls="plus";text="运行";left=794;top=550;right=889;bottom=590;bgcolor=0xD77800;border={radius=4};color=0xFFFFFF;db=1;dr=1;font=LOGFONT(h=-14);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=15;top=1}};iconText='\uF1B9';notify=1;textPadding={left=15};z=4};
btnSetting={cls="plus";left=6;top=556;right=41;bottom=586;align="left";color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-22;name='FontAwesome')};iconText='\uF013';notify=1;textPadding={left=25};z=5};
btnWeixin={cls="plus";left=91;top=556;right=126;bottom=586;align="left";color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-22;name='FontAwesome')};iconText='\uF1D7';notify=1;textPadding={left=25};z=8};
customChat={cls="custom";text="聊天区域";left=0;top=0;right=900;bottom=540;db=1;dl=1;dr=1;dt=1;z=2};
editPrompt={cls="richedit";left=228;top=554;right=748;bottom=589;autohscroll=false;db=1;dl=1;dr=1;multiline=1;z=3};
lableMessage={cls="plus";left=66;top=474;right=821;bottom=528;bgcolor=0x008000;border={radius=12};color=0xFFFFFF;db=1;dl=1;dr=1;font=LOGFONT(h=-14);hide=1;z=10}
)
/*}}*/

mainForm.btnSend.skin({
    background={
        default=0xFF0078D7; 
        hover=0xFF0088EE;   
        active=0xFF005BB5;  
        disabled=0xFFCCCCCC;
    };
    color={
        default=0xFFFFFFFF;
        disabled=0xFF888888;
    };
    checked={
        background={
            default=0xFFD13438; 
            hover=0xFFE35458; 
            active=0xFFA82A2D;
            disabled=0xFFCCCCCC;
        };
        color={
            default=0xFFFFFFFF;
            disabled=0xFF888888;
        }
    }
})

mainForm.btnSetting.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF6D6D6D;
        hover=0xFFFF0000        
    }
})

mainForm.btnFeishu.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF6D6D6D;
        hover=0xFFFF0000        
    }
    checked = {
        color={
            active=0xFF00FF00;
            default=0xFF3370FF;
            disabled=0xFF6D6D6D;
            hover=0xFFFF0000        
        }   
    }
})

mainForm.btnClear.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF999999;
        hover=0xFFFF0000        
    }
})

mainForm.btnCopy.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF999999;
        hover=0xFFFF0000        
    }
})

mainForm.btnWeixin.skin({
    color={
        active=0xFF00FF00;
        default=0xFF3C3C3C;
        disabled=0xFF6D6D6D;
        hover=0xFFFF0000        
    }
    checked = {
        color={
            active=0xFF00FF00;
            default=0xFF07C160;
            disabled=0xFF6D6D6D;
            hover=0xFFFF0000        
        }   
    }
})

mainForm.btnImageAttachment.skin({
    background={
        active=0xFFCCCCCC;
        default=0x00000000;
        hover=0xFFE6E6E6;
    };
    color={
        active=0xEE333333;
        default=0xFF3C3C3C;
        disabled=0xFF999999;
        hover=0xFFFF0000        
    }
})

mainForm.editPrompt.adjustInputLayout = function(focus){
    var w,h = mainForm.dpiScale(1,1);
    h = h * 50;

    focus = focus && !mainForm.btnSend.busy();

    if(focus){
        if( ! mainForm.editPrompt["(focusedHeight)"] ){
            mainForm.bkPrompt.top = mainForm.bkPrompt.top - h;
            mainForm.bkPrompt.redraw();
            mainForm.customChat.bottom = mainForm.customChat.bottom - h;
            mainForm.editPrompt.top = mainForm.editPrompt.top - h;      
            mainForm.editPrompt["(focusedHeight)"] = true;
        }
    }   
    else{
        if( mainForm.editPrompt["(focusedHeight)"] ){
            mainForm.bkPrompt.top = mainForm.bkPrompt.top + h;
            mainForm.bkPrompt.redraw();
            mainForm.customChat.bottom = mainForm.customChat.bottom + h;
            mainForm.editPrompt.top = mainForm.editPrompt.top + h;  
            mainForm.editPrompt["(focusedHeight)"] = false; 
        }
    }
}

var defaultPrompt = "获取我的公网 IP,然后打开 必应 搜索此 IP";
mainForm.editPrompt.onFocusGot = function(){
    if(#defaultPrompt){
        if(owner.text == defaultPrompt){
            owner.text = "";
        }   

        defaultPrompt = ""; 
    }

    mainForm.editPrompt.adjustInputLayout(true);
}
mainForm.editPrompt.onFocusLost = function(){
    mainForm.editPrompt.adjustInputLayout(false);
}
mainForm.editPrompt.langOptions = 2/*_IMF_AUTOFONT*/;

mainForm.afterDpiChanged = function(){ 
    mainForm.setIcon("/autos.ico"); 
}

mainForm.show();

import fsys.table;
import web.rest.aiChat.settingForm;

var userDataPath = io.appData("aardio/autos");
var agentConfigPath = io.appData("aardio/autos/agent.table");
/*UPDATE{{*/
if(!io.exist(userDataPath)){
    var oldUserDataPath = io.appData("aardio/std/example/ai/autos");
    if(io.exist(oldUserDataPath)){

        if(!fsys.move(oldUserDataPath,userDataPath)){
            userDataPath = oldUserDataPath;
        }       
    }


    if(!io.exist(agentConfigPath)){
        var oldAgentConfigPath = io.appData("aardio/std/example/ai/agent4.table");
        if(!fsys.move(oldAgentConfigPath,agentConfigPath)){
            agentConfigPath = oldAgentConfigPath;
        }
    }
}
/*}}*/
var agentWorkspace = io.createDir( io.joinpath(userDataPath,"workspace") ); 

var config = fsys.table(agentConfigPath,{
    itemNames = ["默认"];
    selItem = 1;
    itemData = [{
        url="https://api.deepseek.com/v1";
        key= '';
        model="deepseek-v4-pro";
        temperature=1;
        reasoning={"effort":"max 全力以赴"}
    }] 
});
var aiChatConfig = config.itemData[config.selItem];

import thread.event;
// 手动复位事件:set 后 wait(0) 会持续返回 true,直到下一轮请求调用 reset()。用于表示“用户已请求停止”的持续状态。
var eventStop = thread.event(,true);

//创建 AI 聊天界面
import web.form.chat;
var chatUi = web.form.chat(mainForm.customChat);

import com.interface.ITaskbarList3;
var taskbar = _WIN7_LATER && com.interface.ITaskbarList3.Create();

var sendApiMessage = function(resentCot,prompt){

    if(!aiChatConfig[["url"]] || string.find(aiChatConfig[["url"]],"aardio.com") ){
        mainForm.btnSend.checked = false;

        mainForm.msgboxErr('请在设置中指定支持交错思考+工具调用的大模型。\n建议使用 DeepSeek Pro。\n\n请不要使用 ai.aardio.com 接口。');
        mainForm.btnSetting.oncommand();
        return;
    }

    if(!resentCot){
        var clearPrompt;
        if(!prompt) {
            prompt = mainForm.editPrompt.text;
            clearPrompt = true;
        }

        if(mainForm.btnSend.text!=="重试"){
            // 检查输入是否为空(移除首尾空白)
            if(!#string.trim(prompt)){
                mainForm.btnSend.checked = false;

                mainForm.msgboxErr("请输入提示词");
                mainForm.editPrompt.setFocus();
                return;
            }   
        }
        else{
            chatUi.clearErrorMessage();
        }

        // UI 状态变更
        if(clearPrompt)mainForm.editPrompt.text = ""; // 清空输入  

        ..thread.set("resumeChainOfThought",null);
        ..thread.set("resumeChainOfThoughtProtocol",null);
    }
    elseif(!..thread.get("resumeChainOfThought")){
        mainForm.btnSend.checked = false;

        mainForm.msgbox("没有意外中断的交错思维链需要恢复!"," aardio autos ","warn");
        return; 
    }

    mainForm.btnClear.disabled = true; 

    eventStop.reset(); 
    var loading = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250' }
    mainForm.btnSend.text = "停止";
    mainForm.btnSend.checked = true;

    //显示动画,但启用按钮
    mainForm.btnSend.reduce(loading,function(value,index){
        if(value) mainForm.btnSend.iconText = value;
        return 150;
    } ) 

    if(!resentCot){
        chatUi.showLoading(" 正在思考...");
        chatUi.prompt(prompt);
    }

    mainForm.editPrompt.adjustInputLayout(false);
    mainForm.btnSend.setFocus();

    config.chatMessages =  chatUi.chatMessages;

    if(taskbar) taskbar.SetProgressState(mainForm.hwnd,1/*_TBPF_INDETERMINATE*/);

    // 创建 AI 对话线程
    chatUi.aiChatThread = thread.create( 
        function(chatUi,mainForm,aiChatConfig,aiConfigItems,eventStop,fromWeiXinClawBotMessage,fromFeishuMessageId,feishuClientInstance,resentCot,hasImageAttachment){
            import web.rest.aiChat;
            import autos.tools.schemas;
            import autos.tools.handlers;
            import autos;

            aiChatConfig.tools = autos.tools.schemas;
            aiChatConfig.userAgent = "autos/" + autos.version +"(Windows NT "+ _WIN_VER_MAJOR +"."+_WIN_VER_MINOR+"; +https://aau.cn)";

            // 创建 AI API 客户端
            var aiClient = web.rest.aiChat(aiChatConfig);

            aiClient.referer = "https://aau.cn";
            aiClient.setHeaders({
                 "X-Title":"aadio autos",
            });

            import win.inputBox;
            ..subscribe("afterConsoleOpen",function(){
                if(console){
                    console.setTitle("autos console - 点击本窗口关闭控钮将关闭对话系统")

                    console.setCtrlHandlerThread( function(ctrlType){
                        mainForm.saveChatMessage(); //用户直接关闭 AI 打开的 console 窗口时保存对话
                    },mainForm);            
                }
            } );

            aiClient.external = autos.tools.handlers;
            autos.tools.handlers.__init__(null,{
                aiChatConfig = aiChatConfig;
                aiConfigItems = aiConfigItems;
                chatUi = chatUi;
                fromWeiXinClawBotMessage = fromWeiXinClawBotMessage;
                fromFeishuMessageId = fromFeishuMessageId;
                feishuClientInstance = feishuClientInstance;
                mainForm = mainForm;
            });

            var resumeChainOfThoughtProtocol;
            var chatMessages = chatUi.chatMessages;
            if(resentCot){
                chatMessages = ..thread.get("resumeChainOfThought");
                resumeChainOfThoughtProtocol = ..thread.get("resumeChainOfThoughtProtocol");

                if( resumeChainOfThoughtProtocol){
                    if(resumeChainOfThoughtProtocol!=aiClient._protocol){
                        chatUi.onComplete(false); 
                        chatUi.errorMessage("恢复思维链时不能将原接口使用的协议类型 " + resumeChainOfThoughtProtocol + " 更改为 " + aiClient._protocol);
                        mainForm.btnSend.text = "继续";
                        return;
                    }
                }

                var userPrompt = string.trim(mainForm.editPrompt.text);
                if(#userPrompt){
                    ..table.push(chatMessages,{
                        role = "user";
                        content = userPrompt;
                    });
                }
                else{
                    if(chatMessages[#chatMessages][["role"]]=="assistant"){
                        ..table.push(chatMessages,{
                            role = "user";
                            content = "继续";
                        });
                    }
                }


                mainForm.editPrompt.text = "";
            }
            elseif(hasImageAttachment){
                var msg = chatMessages[#chatMessages];
                if(msg.role=="user"){
                    msg.content = msg.content + '\n\n用户提供了附件图像,请使用工具 analyze_image 识别图像(`imageUrlOrPath` 参数请指定为 `attachment`)。'
                }
            }

            aiClient.beforeToolCalling = lambda () !eventStop.wait(0);
            aiClient.onToolCalling = lambda (name,args) !eventStop.wait(0);
            aiClient.afterToolCalling = function (chatMessages){
                 ..thread.set("resumeChainOfThought",chatMessages);
                 ..thread.set("resumeChainOfThoughtProtocol",aiClient._protocol);
                 return !eventStop.wait(0);
            }

            var ok,err,errCode,sendBackProtocol = aiClient.messages(chatMessages,
                function(deltaText,reasoning){

                    if(eventStop.wait(0)){
                        chatUi.assistant(null);
                        return false; 
                    }

                    if(reasoning) chatUi.showThinking(reasoning);
                    chatUi.assistant(deltaText);
                },,resumeChainOfThoughtProtocol
            );

            if(err){ 
                if(errCode==12029){
                    chatUi.errorMessage(`无法与服务器建立连接!<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">点这里更换其他接口</a>`)
                }
                elseif(aiClient.lastStatusCode==401){
                    chatUi.errorMessage(`API 密钥错误!&nbsp;<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">点这里设置新密钥</a>`)
                }
                else{
                    if(type.isString(err) 
                        && string.find(string.lower(err)
                            ,`<%<<balance>|<credit>|<quota>><<insufficient>|<exhausted>|<exceeded>|<run\s*out>>>|<%<<insufficient>|<exhausted>|<exceeded>|<depleted>><<balance>|<credit>|<quota>|<fund>>>`)){
                        chatUi.errorMessage(string.concat("余额不足,请及时充值或联系 API 接口服务商("+aiChatConfig.url+")。<code>",err,"</code>"))
                    }
                    elseif(#err){
                        chatUi.errorMessage(err)
                    }
                    elseif( (errCode===0 /*无其他错误*/) && ( !eventStop.wait(0) /*非用户中止*/ ) ){
                        chatUi.errorMessage("服务器回复调用成功,但并没有发送有效正文。请稍后点击「继续」重试(如果一直如此,请更换服务商)。")
                    }
                } 
            }

            // 恢复按钮状态
            chatUi.onComplete(ok); 

            if((sendBackProtocol && err ) || eventStop.wait(0)){
                //仅在交错思考回传工具调用结果失败或者用户手动点击“停止”才有必要恢复思维链(修改本工具源代码时请保留此注释)。
                ..thread.set("resumeChainOfThought",chatMessages);
                ..thread.set("resumeChainOfThoughtProtocol",sendBackProtocol);
                mainForm.btnSend.text = #err ? "重试" : "继续";
            }
            elseif(err){
                mainForm.btnSend.text = "重试";
            }

        },chatUi,mainForm,aiChatConfig,config.itemData,eventStop,mainForm.fromWeiXinClawBotMessage,mainForm.fromFeishuMessageId,mainForm.feishuClientInstance,resentCot,type(mainForm.btnImageAttachment.foreground)==="table"
    )

    // 发送后焦点切回输入框
    mainForm.editPrompt.setFocus();
}

chatUi.onComplete = function(success){

    mainForm.saveChatMessage();

    mainForm.btnSend.checked = false;
    mainForm.btnSend.disabledText = null;
    mainForm.btnSend.reduce(false);

    mainForm.btnSend.text = "运行";
    mainForm.btnSend.iconText = '\uF1B9';

    mainForm.btnClear.disabled = false; 
    mainForm.editPrompt.setFocus();

    if(chatUi.aiChatThread){
        ..raw.closeHandle(chatUi.aiChatThread); 
        chatUi.aiChatThread = null; 
    }

    if(mainForm.stopTimerId){
        mainForm.clearTimeout(mainForm.stopTimerId);
        mainForm.stopTimerId = null;
    }

    if(taskbar) taskbar.SetProgressState(mainForm.hwnd,0/*_TBPF_NOPROGRESS*/);
}

mainForm.saveChatMessage = function(){ 
    config.save();
}


mainForm.onDestroy = function(){

    if(mainForm.feishuClientInstance){
        mainForm.feishuClientInstance.stop();
        mainForm.feishuClientInstance = null;
    }
    feishuClient = null;

    if(weixinListeningThread){
        ..thread.terminate(weixinListeningThread,0)
        ..raw.closeHandle(weixinListeningThread);
        weixinListeningThread = null;
    }

    eventStop.set();
    if(chatUi.aiChatThread){
        if(!..thread.wait(chatUi.aiChatThread,200)){
            ..thread.terminate(chatUi.aiChatThread,0);
        }
        ..raw.closeHandle(chatUi.aiChatThread);
        chatUi.aiChatThread = null;
    }

    mainForm.saveChatMessage();
}

chatUi.external = {
    alert = function(msg){
        mainForm.msgbox(msg," 🚗 aardio autos ");
    };
    updateApiKey = function(){
        mainForm.btnSetting.oncommand();
    };
    updateSearchKey = function(){
        var webSearchConfig = fsys.table(io.joinpath(userDataPath,"tavily.table"));
        var key = mainForm.inputBox("请输入密钥(自动识别 Tavily,Exa 等搜索接口密钥)","AA(aardio autos) - 搜索接口密钥",webSearchConfig.key,,true);
        if(key){
            webSearchConfig.key = key;
            webSearchConfig.save();
        }
    };
    openMemory = function(){
        raw.explore(io.createDir(io.joinpath(userDataPath,"~memory/")))
    };
    showMessage = function(msg){
        mainForm.showMessage(msg,,true)
    };
    importChatMessages = function(){
        import fsys.dlg;
        var path = fsys.dlg.open("*.jsonl|*.jsonl|*.json|*.json||","自 jsonl 或 json 文件导入对话");
        if(!path) return;

        chatUi.importChatMessagesFromFile(path);
    };
} 

mainForm.btnSend.oncommand = function(id,event){

    if(mainForm.btnSend.text == "停止"){
        eventStop.set();

        mainForm.btnSend.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}

        mainForm.stopTimerId = mainForm.setTimeout( 
            function(){
                mainForm.stopTimerId = null;

                if( mainForm.btnSend.text == "停止"){

                    if(chatUi.aiChatThread ){
                        ..thread.terminate(chatUi.aiChatThread,0)
                        ..raw.closeHandle(chatUi.aiChatThread)
                        chatUi.aiChatThread = null;

                        // 恢复按钮状态
                        chatUi.onComplete(); 

                        if( ..thread.get("resumeChainOfThought") ){ 
                            mainForm.btnSend.text = "继续"
                        }
                    }
                }

                return 0;
            },6000
        )
        return;
    } 

    if(mainForm.btnSend.text=="继续"){
        chatUi.clearErrorMessage();
        sendApiMessage(true);
    }
    else{
        mainForm.fromWeiXinClawBotMessage = null;
        mainForm.fromFeishuMessageId = null;
        sendApiMessage();   
    }
}

//回车换行(Ctrl+Enter 发送)
mainForm.editPrompt.onOk = function(ctrl,alt,shift){
    if(ctrl){
        mainForm.btnSend.checked = true;
        mainForm.btnSend.oncommand();
        return true;
    }
}

var resetMessages = function(restore){

    chatUi.clear();

    var tip = /***  
[AA( aardio autos )](http://aau.cn) - 开源,免费的全自主 AI 智能体(Autonomous Agent)  
本工具拥有自主编程、调试、操作电脑的能力。 **使用本工具造成的一切后果自负,如不同意请勿使用!** 

#### 🚗 起步
请 <a href="javascript:void(0)" onclick="javascript:external.updateApiKey()" title="打开 API 设置窗口(试试右键点配置名 » 智能粘贴)。请选择支持交错思考(边思考边调用工具)能力的模型。">» 设置接口</a> / [推理强度](http://www.aardio.com/zh-cn/docs/library-guide/std/web/rest/aiChat.html#reasoning) (磨蹭卡壳时调低,粗心草率时调高) [» 新手资源包](http://aau.cn/pack)  
请 <a href="javascript:void(0)" onclick="javascript:external.updateSearchKey()" title="兼容并自动识别 AI 搜索厂商 Tavily/Exa/Bocha 的密钥格式。设置此密钥 AI 才能具有联网搜索的能力。">» 设置联网密钥</a> (Tavily <a href="https://app.tavily.com/home" target="_blank">注册</a> 免费 1000 次/月)

#### 🚗 技巧

- 输入框按 `Ctrl+Enter`快速发送,`Ctrl+V`粘贴文件或图像附件(点 &#xF030; 截屏,右键清除)
- 展开「推理过程」可查看 <a title="AI 调用工具执行的代码仅临时显示在推理过程(不带入下轮对话,重启丢弃)。智能体的默认行为是执行代码而非生成代码。如果需要将代码回复到正文(默认 AI 会自动判断是否有必要这么做),请避免使用可能误导 AI 「仅执行代码」的提示词(或带有类似歧义)。如果需要保存代码到本地文件(这本就是智能体的基本能力)也请明确告知 AI。" href="javascript:void(0)">执行代码</a>,`Ctrl+单击` 或 3 连击选中代码块
- 中止任务后可点「继续」恢复思维链(可输入临时性提示词纠正 AI 思维路径)
- 点( &#xF014; )按钮清除对话(减负提效),<a title="如果 AI 打开控制台(黑窗口)请不要直接关闭(不保证能保存对话)" href="javascript:void(0)" >正常关闭自动保存</a>,输入框右键 <a href="javascript:void(0)" onclick="javascript:external.importChatMessages()" title="点这里自 json,jsonl 文件导入对话">导入/导出</a> 对话
- 新建对话或重启程序可重新加载 <a href="javascript:void(0)" onclick="javascript:external.openMemory()" title="点这里打开记忆文件存储目录,main.md 为主记忆,手工操作时请注意备份">长期记忆</a>,可显式要求 AI 写入、清除、切换`主记忆`
- 可自主按需加载 / 创建技能包(高效复用) [» 技能包仓库](http://aau.cn/skills)
***/
    if(!restore) {
        config.chatMessages = null;
        config.save();

        chatUi.write(tip);
    }
    else{
        config.chatMessages =  chatUi.chatMessages;
        config.save();
    }

    var systemPrompt = "";

    var aiChatConfig = config.itemData[[config.selItem]]
    if(#aiChatConfig[["systemPrompt"]]){
        systemPrompt = aiChatConfig.systemPrompt + '\n\n';
    }

    systemPrompt = systemPrompt + "

## 角色

你是 Autos,高级 aardio 自动编程智能体。

你擅长编写、调试、测试和改进 aardio 代码,
并可使用工具运行代码、检查文件、搜索文档、自动化操作电脑。


## 要求

- 优先用最短可靠路径验证关键风险;避免无价值的重复探索、冗长解释和不必要的工具调用
- 必要时应主动调用工具验证关键假设,复杂任务可充分使用工具,关键目标应尽最大能力思考,优先探索高信息增益、能验证关键假设或降低主要风险的方法;必要时可以充分尝试不同路径,但避免无意义的重复试错,避免做无用功。
- 编程任务请调用工具执行必要的单元测试(验证关键代码、你的想法、思路的可行性)
- 微信/飞书来源的消息必须用对应工具回复

"

    systemPrompt = systemPrompt + "

## 复杂任务开发流程

以下是 default workflow / playbook,不是硬性清单;可按任务复杂度跳过、合并、重排。

1. Align:明确目标、约束、上下文、验收标准(Definition of Done)。若缺失信息可以安全假设,就说明假设并继续;若会显著影响方向或风险,则先询问。
2. Plan:拆分任务,选择技术路线,识别关键风险、依赖、验证方式与必要的回滚/备份策略。
3. De-risk:优先验证最不确定、最可能阻塞的点;必要时查文档/范例/源码,或构造 minimal repro 验证关键 API、协议、环境差异。
4. Implement:做最小但完整的有效改动,保持简单、可维护、可回滚;避免无关重构和扩大任务范围。
5. Validate:运行聚焦的测试或实际验证,观察错误与输出,用结果修正方案;必要时扩大验证范围,直到达到足够置信度。
6. Deliver:简洁总结已完成内容、验证证据、剩余风险与建议下一步;若任务仍很长,可给出可恢复的 checkpoint / State Summary。

流程是迭代的:Observe » Orient » Decide » Act。请根据证据持续调整计划,尽最大努力交付高质量结果;关键任务不要吝惜必要的推理和验证,但始终避免 busywork。

### 任务边界、Checkpoint 与上下文预算

本节是 soft heuristic / meta-guidance,不是硬性限制;请按当前任务的最佳实践自主权衡。

默认保持 Autonomy:用户的显式目标是 priority anchor,而不是探索上限。为完成目标所需的隐含子任务、短闭环验证(micro-loop)可自主推进;避免 zero-value confirmation,不要机械请示。

避免跑偏:复杂任务中请留意 task boundary / scope creep / context budget。工具调用应优先服务当前目标;若新发现的问题能解除阻塞或显著降低风险,可自主处理;否则先记录为 risk / technical debt / follow-up,避免把相关问题自动升级为当前任务。

分而治之:当你自行判断方向不确定性升高、可能进入 rabbit hole、上下文或思维链明显膨胀、连续行动的边际收益下降,或即将进入高影响新阶段时,可主动结束本次对话以创建“阶段性 checkpoint”。阶段性 checkpoint 的目的不是降低探索性,而是 alignment 与 context compaction。

阶段性 checkpoint 的正文应作为可恢复的 State Summary,简短说明:已确认事项、当前判断或风险、下一步默认方案,或确实需要用户决策的问题。若下一步短小、低风险,且能验证关键假设或完成当前 micro-loop,请继续完成小闭环,不要过度频繁打断或要求用户确认。

## 提示

执行 aardio 代码时,你可以:

- 使用 `return` 语句返回你需要的值。
- 自动捕获 `print` 函数的所有输出。 
- 调用 `util.testRunner` 执行自动测试,并使用 `return $.report()` 方法收集测试结果。

即使本系统没有提供相关工具,
你也可以调用 aardio 强大的标准库与 ide 扩展库。

- 获取库路径: `var path,dir = io.libpath(lib)` ,库不存在时返回 null 。创建库时应当硬编码路径 `~/lib/namespace...`(公共库)或  `/lib/namespace...`(用户库)
- aardio 范例目录:  `~\examples\` ,可调用工具 `search_text(path='examples')` 搜索范例
- aardio 调用其他编程语言:  `~\examples\Languages\`,例如 `\examples\Languages\R\`
- aardio 文档与指南目录: `~\docs\` ,可调用工具 `search_text(path='docs')` 搜索文档。文档中的虚拟路径`~/docs/examples/`指向物理路径 `~/examples/`
- `https://www.aardio.com/zh-cn/docs/` 协议: 文档中 `https://www.aardio.com/zh-cn/docs/` 链接的根目录为 IDE 目录 `~/docs/`,`https://www.aardio.com/zh-cn/docs/examples/` 对应 IDE 目录 `~/examples/` ,`https://www.aardio.com/zh-cn/docs/library-references/` 下面的库参考文档则由 ide.doc.libraryMd 动态生成
- 库文档分为 3 类: 
    1. 库参考:调用 lookup_library_reference 工具获取,由库源码内智能提示声明(必不可少)生成(基于 ide.doc.libraryMd )
    2. 库指南:位于 `~\docs\library-guide` 目录,内置库与部分标准库
    3. 库文档:位于 `~\docs\library` 目录,扩展库安装的文档
- 探查库源码: 工具 get_library_source
- 重新加载已修改的库: 工具 loadcodex_clean
- 自动化: 标准库 winex(控制外部窗口),key(模拟键盘),mouse(模拟鼠标) 等命名空间
- 进程操作:  process 命名空间
- 文件操作: fsys 命名空间,io 内置库
- 网页自动化: web.view(WebView2 ) 提供 preloadScript 方法可注入并修改 JS 内置函数,cdp 系列方法也非常有用,也可以利用 waitEle,waitEle2 方法实现 XPath 查询、检测、等待网页元素并回调 JS 非常方便,范例 `~/examples/WebUI/web.view/XPath.aardio`
- Markdown 转 HTML: string.markdown 基于 C 组件速度极快,推荐
- HTML 转 PDF: web.view + cdp('Page.printToPDF')
- PDF: fsys.pdfium 
- Word 文档(*.docx):  标准库 com.doc (基于 `com.TryGetObject('Word.Application','wps.Application')`),兼容 WPS
- PPT: `com.TryGetObject('PowerPoint.Application', 'WPP.Application')` 创建 COM 对象操作 PPT 幻灯片文件
- Excel: com.excel ,兼容 WPS
- 系统自动化: mouse, key, winex, process 库
- OCR: 标准库自带的 dotNet.ocr 或工具 analyze_image
- 读写剪贴板: win.clip 命名空间
- 操作系统版本信息: win.version
- SYSTEM_INFO 结构体:  sys.info
- PowerShell: 标准库自带的 dotNet.ps 或工具 process_powershell ;仅在有必要时才使用 PowerShell ,编辑文本或代码请改用 patch_text_file, edit_text_file 等更可靠的工具。
- CMD/外部进程: 工具 process_popen;代码内可用 process.popen
- 批处理(*.bat): process.batch
- 创建美观的图形界面: 用 plus 控件, skin 方法美化。要求不高的简单图例使用 gdip.chart.bar 等基于 plus 控件的简单图表
- GDI+ 自绘: plus 控件适合 GDI+ 自绘,例如写见缝插针小游戏、简单动画可能比网页还方便,但 GDI+ 不适合大面积密集绘图或对性能要求高的动画
- 创建复杂的适合用网页展示的界面:  web.view(WebView2) 加载前端是最佳实践,例如显示数据看板、统计报表用网页非常方便,与 aardio 交互也非常方便。
- ImTip 超级热键: process.imTip 库,以及超级热键指南( `~/docs/library-guide/std/key/hotkey.md` )
- 调用 .NET 框架或程序集: 标准 dotNet
- 编译运行 C# 代码: `dotNet.createCompiler('C#')` ,注意 aardio 中的 CLR 编译器最高支持 C# 4.0( .NET 4.0+)/5.0( .NET 4.5+) 语法
- 调用 WinRT 组件:  dotNet.uwpCompiler,参考 `\examples\Languages\dotNet\WinRT`目录
- JScript 或 VBScript:  标准库  web.script
- 现代 JavaScript:  标准库 nodeJs 或 web.view 
- 调用 Python:  py3 扩展库
- 调用或编译执行 C 语言,生成 DLL:  tcc 扩展库
- 调用 HTTP API: aardio 通常不需要专门的 SDK,大多时候只需要 web.rest.jsonClient 或 web.rest.jsonLiteClient,`\examples\Web\REST` 目录下提供了很多示范代码

以上只是部分提示与建议,你可以根据用户需求与最佳实践进一步调研分析、权衡取舍

## 共享浏览器

当你需要调用浏览器显示网页并希望其持久驻留(跨工具调用、跨对话线程),并且获得控制这个浏览器的能力时(例如执行 JavaScript,探查网页状态,自动化操作,跟踪或辅助用户操作),
请使用 `web.view.shared({text=""title""})` 创建共享浏览器窗 —— 这会在独立线程中创建共享 WebView2 窗口;相同 `text` 复用同一实例,不同 `text` 可创建不同实例。

shared 浏览器应通过 JavaScript 或 CDP 交互;尽量避免绑定依赖当前对话线程上下文的 aardio 回调闭包。

示例:

```aardio 
import web.view;

var wb = web.view.shared(text='shared-demo');

wb.go('https://example.com');
wb.waitEle(`//h1[contains(., ""Example Domain"")]`,5000);

wb.html = /***
<button id=""btn"" onclick=""this.innerText='已点击'"">点我</button>
***/

wb.waitEle(`//button[@id=""btn"" and normalize-space(.)=""已点击""]`/*,5000*/);

return {
    text = wb.eval(""document.getElementById('btn').innerText"");
}

```
"
    //保持不变以利于构建缓存
    chatUi.aardioSystem(systemPrompt);

    //技能包
    import autos.skills;
    var systemPrompt = autos.skills.buildRoutePrompt();

    var lunar;
    try{
        import time.lunar;
        lunar =  '\r\n- 当前农历:' +  tostring(time.lunar());
    }
    catch(e){
        lunar = ""; //兼容 XP 系统
    }

    //主记忆
    var mainMemoryPath = io.joinpath(userDataPath,"~memory/main.md")

    systemPrompt = systemPrompt + "


## 背景

- 当前日期:" + tostring(time(str,"%Y年%m月%d日","chs"))
    + '\n- 当前操作系统:"' + ..win.version.format() + '"'
    + lunar 
    + '\n- aardio 开发环境所在目录:`' +  io.fullpath("~/") + '`'
    + '\n- 操作系统临时目录:`' +  fsys.getTempDir() + '`'
    + '\n- 你可以自由读写文件的工作区目录:`' +  agentWorkspace + '`'
    + '\n- 本系统名称为: AA( aardio autos ) 版本: ' + autos.version
    + '\n- 本系统源代码位于:`' + io.fullpath("~\examples\AI\autos.aardio") + '` 如果用户有需要,你可以改进系统源代码,但需经过用户确认同意。如果用户要求你介绍自己,你可以先读取运行你的这个系统源代码作为参考。'
    + '\n- 本系统进程权限:' + (..process.admin.isRunAs() ? "以管理权限启动" : "以普通权限启动") + "。如果你需要以管理权限执行工具或代码,请提醒用户可按住 Shift 键单击 aardio 工具面板顶部的`AA`按钮以管理权限启动 Autos 。"
    + '\n\n'    
    + "## 长期记忆

### 记忆文件系统

- 长期记忆根文件夹 `" + io.joinpath(userDataPath,"~memory/") + "` 是你的大脑。
- main.md 是你的主记忆,创建会话时自动加载,阶段性成果/大坑教训应使用 write_memory 写入主记忆。
- 除 main.md 以外的其他记忆文件则用于保存记忆分枝。

但应当控制写入长期记忆的频率,避免过度频繁地写入长期记忆。
几乎没有可重用价值的信息(对以后的对话和任务几乎没有帮助)不应当写入长期记忆。
写入主记忆不会影响当前上下文,只有用户清除当前会话并创建新会话时才会加载新的主记忆。

### 整理与修剪主记忆

当前主记忆文件(main.md)的体积:" + math.size64(io.getSize(mainMemoryPath)).format() + ",超过 30KB 你应当在会话结束前整理与修剪主记忆(调用工具 read_memory 读取最新的主记忆,然后调用工具 write_memory 替换主记忆文件)。

整理与修剪主记忆的原则是丢弃对任务推进没有价值的信息,归纳整理并保留有价值的知识、技能与重要记忆,暂时不需要的记忆可以存为其他分枝记忆文件(在主记忆中记录其路径与摘要)。

如果项目切换或者用户要求,可以调用工具 switch_memory 备份并切换主记忆。

### 利用长期记忆分解并逐步完成复杂任务

避免在一次对话中完成复杂的任务的所有步骤。
你应当做好规划,分而冶之,把复杂的任务目标拆分为可以逐个完成的简单小目标。
在长期记忆中记录复杂任务的规划、步骤,以及需要逐个攻克的难点。
每次完成一个小目标或里程碑并结束对话时你应当将重要的信息、研究成果、任务进度、下一步建议执行的任务写入长期记忆。
即使用户清除对话上下文,你仍然可以使用长期记忆继续推进项目,不会遗忘重要的信息。

### 刷新主记忆

在用户创建新对话(清除原对话)或重启 Autos (保留原对话)时都会自动读取新的主记忆(注入系统提示词)。

在用户清除上下文并新建对话之前,继续原对话不会重新读取新的主记忆(这是为了重用大模型 API 的前缀缓存)。

因此增量写入主记忆的重要信息你也要输出到回复正文(除非在上下文中已存在)。
";

    var memPath = io.joinpath(userDataPath,"~memory/main.md") 
    if(..io.exist(memPath)){
        var main = string.load(memPath)
        if(main) systemPrompt = systemPrompt + '\n\n\n---\n\n\n以下是你之前记录的长期记忆和关键信息(主记忆 main.md)。\n\n'+main;
    }

    chatUi.system(systemPrompt);
    //assert(#chatUi.chatMessages===2);//超长而自动清理时最多保留头部 2 个系统提示词

    mainForm.btnImageAttachment.foreground = null;
    mainForm.btnImageAttachment.iconText = '\uF030';

    mainForm.btnSend.text = "运行";
}

mainForm.btnSetting.oncommand = function(id,event){

    table.assign(config,{
        title = "AA( aardio autos ) - 设置"; // 标题  
        hyperlink1Title = "获取 key"; // key 输入框显示嵌入链接
        f1Title = "允许使用此接口的视觉能力";
        f1MultiSelect = true;
        allowKatex = true;//Win10+
    });

    //打开 AI 设置窗口,参数 2 指定配置文件
    var frmSetting = web.rest.aiChat.settingForm(mainForm,config);

    //用户点击了「保存」           
    if( frmSetting.doModal(mainForm) ){ 
        //获取用户当前选择的配置
        var configItem = config.itemData[config.selItem]; 
        if(configItem){

            if(  configItem.model && string.find( configItem.model,"!\wgpt!\W") ){

                //OpenAI 透传缓存
                if(configItem.key && type(configItem.extraParameters)!="table"){
                    import crypt;
                    configItem.extraParameters  = {
                        //隔离缓存 key,避免缓存溢出
                        prompt_cache_key = crypt.sha256( configItem.key + ".aardio.autos" + io._exepath);
                    }
                    config.save()
                }
            }

            aiChatConfig = table.assign({},configItem); 
            if(!chatUi.started()){
                resetMessages(); 
            } 
        } 
    }
}

mainForm.btnClear.oncommand = function(id,event){
    mainForm.btnSend.disabled = true;

    if(chatUi.aiChatThread){
        ..thread.terminate(chatUi.aiChatThread,0)
        ..raw.closeHandle(chatUi.aiChatThread)
        chatUi.aiChatThread = null;
    }

    io.close();
    resetMessages();//清除聊天上下文
    mainForm.showMessage("✅ 已重新读取并刷新主记忆 ……");
    mainForm.editPrompt.setFocus();

    mainForm.btnSend.disabled = false;
}

mainForm.btnCopy.oncommand = function(id,event){
    var md;
    if(::GetKeyState(0x10/*_VK_SHIFT*/) & 0x8000){
        md = chatUi.getMarkdown();
    }
    else{
        md = chatUi.lastAssistantMessage(true)
    }

    if(!#md) return mainForm.msgboxErr("未复制任何内容,AI 回复内容为空!");

    win.clip.write( string.crlf(md,'\r\n') );
    mainForm.btnCopy.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250';text=''} 
    thread.delay(800);
    mainForm.btnCopy.disabledText = null;

    mainForm.editPrompt.setFocus(); 
}

mainForm.weixinSendMessage = function(fromMsg,requestText,attachmentNotice){

    if(mainForm.btnSend.busy()){
        return "抱歉我正在思考,请稍后联系我。";
    }
    else{
        mainForm.fromWeiXinClawBotMessage = fromMsg;

        if(attachmentNotice){
            chatUi.prompt(attachmentNotice,,true);
            config.chatMessages =  chatUi.chatMessages;
            mainForm.saveChatMessage();
        }

        if(requestText){  
            sendApiMessage(false,requestText); 
        }
    }   
}

// 飞书消息处理函数
mainForm.feishuSendMessage = function(messageId, requestText,attachmentNotice){
    if( mainForm.fromFeishuMessageId == messageId ){
        return mainForm.btnSend.busy()?"我正在思考 ……":null;
    }

    if(mainForm.btnSend.checked){
        return "抱歉我正在思考,请稍后联系我。";
    }
    else{
        mainForm.fromFeishuMessageId = messageId;

        if(attachmentNotice){
            chatUi.prompt(attachmentNotice,,true);

            config.chatMessages =  chatUi.chatMessages;
            mainForm.saveChatMessage();
        }

        if(requestText){
            sendApiMessage(false,requestText); 
        }
    }   
}

import win.debounce;
var hideMessage = win.debounce(
    function(){
        mainForm.lableMessage.show(false);
    },3000);

mainForm.lableMessage.orphanWindow(true);
mainForm.showMessage = function(content,msgType,popup){
    if(!content){
        mainForm.lableMessage.show(false);
        return ;
    }

    if(mainForm.btnSend.busy() || popup){
        content = string.replace(content,'\r?\n'," ");
        if(msgType=="error") mainForm.lableMessage.foreground = 0xFFD13438;
        elseif(msgType=="loading") mainForm.lableMessage.foreground = 0xFF107C41;
        else/*if(msgType=="success")*/ mainForm.lableMessage.foreground = 0xDD107C41;

        mainForm.lableMessage.text = content;
        mainForm.lableMessage.show();
        hideMessage();
    }
    else{
        var overwrite = !chatUi.started();

        if(msgType=="error") chatUi.errorMessage(content,overwrite);
        elseif(msgType=="loading") chatUi.showLoading(content,overwrite);
        else/*if(msgType=="success")*/ chatUi.successMessage(content,overwrite); 
    } 
}

mainForm.editPrompt.oleCallback(


    GetDragDropEffect = function(drag,grfKeyState,effect){
        return 0,1/*_DROPEFFECT_COPY*/
    }

    QueryAcceptData = function(ptrDataObj,format,reco,really,hMetaPict){

        if(really){

            var dataObj = com.interface.IDataObject(ptrDataObj);


            if(dataObj.isText()){
                var txt = dataObj.getText();
                if(txt){
                    var b,e = mainForm.editPrompt.getSel();
                    if(!e) mainForm.editPrompt.setSel(-1);
                    mainForm.editPrompt.selText = txt;  
                }
            }
            elseif( dataObj.isFile()){
                var files = dataObj.getDropFile();
                if(#files){
                    mainForm.editPrompt.setSel(-1);
                    mainForm.editPrompt.selText = " `" + string.join(files,",")+"` ";
                }
            }
            elseif( dataObj.isBitmap()){
                var bmp = dataObj.getBitmap();
                if(bmp){
                    var oldBmp = mainForm.btnImageAttachment.setForeground(bmp);
                    if(oldBmp) oldBmp.delete();
                    mainForm.btnImageAttachment.iconText = null;    
                }
            }

            /*返回 0 继续默认处理,文本放入控件,返回 _S_FALSE 阻止默认处理。*/
            return 1/*_S_FALSE*/
        }

        return 0; 
    }
)

var weixinListeningThread;
var weixinTokenPath;
mainForm.btnWeixin.oncommand = function(id,event){
    import web.rest.iLinkClient.login;

    if(!mainForm.btnWeixin.checked){
        if(weixinListeningThread){
            thread.terminate(weixinListeningThread,0)
            raw.closeHandle(weixinListeningThread);
            weixinListeningThread = null;
        }

        if(weixinTokenPath){
            io.remove(weixinTokenPath)
            weixinTokenPath = null;
        }

        mainForm.showMessage("✅ 微信机器人已断开连接,再次点击微信图标重新扫码连接。");
        return;
    }

    var bot,err = web.rest.iLinkClient.login(parent=mainForm;text="使用微信连接 aardio autos");
    if(bot) mainForm.showMessage('✅ 微信机器人已上线,可接收 / 发送文本、图片、文件 ... 再次点击微信图标断开连接。');
    else {
        if(#err ) mainForm.msgboxErr( err );
        mainForm.btnWeixin.checked = false;
        return;
    }

    weixinTokenPath = bot.config.tokenPath;
    weixinListeningThread = thread.create( 
        function(mainForm,chatUi){
            import autos.weixinClient;
            autos.weixinClient(mainForm,chatUi);    
        },mainForm,chatUi
    )
}

// 飞书配置和连接
var feishuClient;
var feishuConfig = fsys.table(io.joinpath(userDataPath,"feishu.table"));

var frmFeishu;
mainForm.feishuOnReady = function(){
    feishuClient = mainForm.feishuClientInstance;
    mainForm.btnFeishu.checked = true;
    if(frmFeishu && frmFeishu.valid){ 
        frmFeishu.setStatus("已连接", 0x00AA00);
        frmFeishu.close();
    }

    mainForm.btnFeishu.checked = true;
    mainForm.showMessage('✅ 飞书机器人已上线,可接收 / 发送文本、图片、文件。\n\n再次点击飞书图标断开连接。');
}

mainForm.feishuOnError = function(err){
    if(!mainForm.btnFeishu.checked && (frmFeishu && frmFeishu.valid)){
        frmFeishu.setStatus("连接失败:" + (err || "未知错误"), 0xFF0000);
        frmFeishu.btnConnect.disabled = false;
    }
}

mainForm.feishuOnClose = function(reason){
    feishuClient = null;
    mainForm.feishuClientInstance = null;
    mainForm.btnFeishu.checked = false;
}

mainForm.btnFeishu.oncommand = function(id,event){
    import web.feishu.settingForm;

    // 如果已连接,断开连接
    if(!mainForm.btnFeishu.checked && feishuClient){
        feishuConfig.autoConnect = false;
        feishuConfig.save();

        feishuClient.stop();
        feishuClient = null;
        mainForm.feishuClientInstance = null;
        mainForm.btnFeishu.checked = false;
        mainForm.showMessage("✅ 飞书机器人已断开连接,再次点击重新连接。","success");
        return;
    }


    mainForm.btnFeishu.checked = false;

    // 打开配置窗口
    frmFeishu = web.feishu.settingForm(mainForm, feishuConfig);

    // 连接回调
    frmFeishu.onConnect = function(appId, appSecret){
        frmFeishu.setStatus("正在连接...", 0x0078D4);
        frmFeishu.btnConnect.disabled = true;

        thread.invoke(
            function(mainForm, chatUi, appId, appSecret){
                import win.ui;
                import autos.feishuClient;

                mainForm.showMessage("✅ 飞书机器人正在连接","loading");

                var client = autos.feishuClient(mainForm, chatUi, appId, appSecret);
                mainForm.feishuClientInstance = client;

                client.start();
                win.loopMessage();
            }, mainForm, chatUi, appId, appSecret
        );
    }

    frmFeishu.doModal();
}

if(feishuConfig.autoConnect && feishuConfig.appId && feishuConfig.appSecret ){
    mainForm.btnFeishu.checked = true;

    thread.invoke(
        function(mainForm, chatUi, appId, appSecret){
            import win.ui;
            import autos.feishuClient;

            mainForm.showMessage("✅ 飞书机器人正在连接","loading");

            var client = autos.feishuClient(mainForm, chatUi, appId, appSecret);
            mainForm.feishuClientInstance = client;

            client.start();
            win.loopMessage();
        }, mainForm, chatUi, feishuConfig.appId, feishuConfig.appSecret
    );  
}

chatUi.importChatMessagesFromFile = function(path){
    var json = string.load(path);
    if(!#json){
        return mainForm.msgboxErr("无效对话数据")
    }

    var messages;

    if(..string.endsWith(path,".jsonl",true)){
        messages = JSON.ndParse(json);
    }
    else {
        messages = JSON.parse(json);
    }


    if(!#messages){
        return mainForm.msgboxErr("无效对话数据")
    }   

    chatUi.importChatMessages(messages)
}

chatUi.importChatMessages = function(messages){
    mainForm.btnClear.disabled = true;
    mainForm.btnSend.disabled = true;
    mainForm.btnSetting.disabled = true;
    mainForm.showMessage("✅ 正在刷新主记忆 + 恢复会话 ……");

    resetMessages(true);

    var last = messages[#messages];
    if((last[["role"]]=="assistant") && (!#last.content)){ 
        table.pop(messages);
    }

    for(i=1;#messages;1){
        var msg = messages[i];

        if(type(msg.content)!="string"){

            if(msg[["role"]]!="user"){
                //resetMessages();
                break;
            }
            elseif(!..table.isArrayLike(msg.content)){
                //resetMessages();
                continue;
            }
        } 

        if(msg[["role"]]=="system"){
            if(i<= #chatUi.chatMessages){
                continue;
            }
            chatUi.system(msg.content)
        }
        elseif(msg[["role"]]=="user"){
            if(i<#messages){
                chatUi.promptOnce(msg.content)
            }
            else{
                mainForm.editPrompt.text = msg.content;
            }
        }
        elseif(msg[["role"]]=="assistant"){

            var content = msg.content;  
            if(#content) chatUi.assistantOnce(content);
        }
        else {
            resetMessages();
            break;
        } 
    }   

    config.save();
    mainForm.showMessage(false);

    mainForm.btnClear.disabled = false;
    mainForm.btnSend.disabled = false;
    mainForm.btnSetting.disabled = false;   
}

mainForm.editPrompt.enablePopMenu(lambda(){
    { /*分隔线*/ };

        { '导入对话';  function(id){
            import fsys.dlg;
            var path = fsys.dlg.open("*.jsonl|*.jsonl|*.json|*.json||","自 jsonl 或 json 文件导入对话");
            if(!path) return;

            chatUi.importChatMessagesFromFile(path);
        }; mainForm.btnSend.busy() ? 1/*_MF_GRAYED*/ : 0}; 

        { "导出对话";  function(id){
            import fsys.dlg;
            var path = fsys.dlg.save("*.jsonl|*.jsonl|*.json|*.json||","导出对话到 *.jsonl");
            if(!path) return;

            var file,err = io.file(path,"w+b");
            if(!file) return mainForm.msgboxErr(err);

            var chatMessages = chatUi.chatMessages;

            if(..string.endWith(path,".jsonl",true)){
                for(i=1;#chatMessages;1){
                    var msg = chatMessages[i]
                    var line  = JSON.stringify(msg,false,false);
                    line = string.crlf(line,"")
                    file.write(line,'\r\n');
                }
            }
            else {
                file.write((JSON.stringifyArray(chatMessages,true,false)));
            }

            file.close();

        }; #chatUi.chatMessages<3 ? 1/*_MF_GRAYED*/ : 0}; 
});

mainForm.btnSend.disabled = true;   
if(config.chatMessages){
    chatUi.importChatMessages(config.chatMessages);
}
else{
    resetMessages();
    mainForm.editPrompt.text = defaultPrompt
}
mainForm.btnSend.disabled = false;  

mainForm.createHbitmapFromAttachment = function(){
    var bmp = mainForm.btnImageAttachment.foreground;
    if(type(bmp)==="table") return bmp.copyHandle();
}

mainForm.btnImageAttachment.onRightMouseUp = function(wParam,lParam){
    mainForm.btnImageAttachment.foreground = null;
    mainForm.btnImageAttachment.iconText = '\uF030';
}
mainForm.btnImageAttachment.oncommand = function(id,event){

    mainForm.show(0);

    import gdip.snap;
    import mouse.screenArea;
    var screenArea = mouse.screenArea();
    screenArea.onSelectionChanged = function(rc){
        mainForm.btnImageAttachment.iconText = null;    
        var oldBmp = mainForm.btnImageAttachment.setForeground( (gdip.snap(screenArea.hwnd,rc)) );
        if(oldBmp) oldBmp.delete();

        owner.endModal(true);

        mainForm.editPrompt.setFocus();
    }

    screenArea.doModal();

    mainForm.show(5/*_SW_SHOW*/);
}

win.loopMessage();
Markdown 格式