Lskj.AgentPet.Host
Host 不信任页面传入的 sessionScopeToken,而是用启动时绑定的 ERP 会话范围覆盖并注入 v3 令牌,再发送到命名管道;页面无法借此切换数据库、用户、账套或子系统。
.NET 8 WPF + WebView2 的独立 Windows 桌宠宿主。透明窗口使用 WebView2CompositionControl,并显式启用 WebView2 非客户区支持:面板标题栏和宠物上方把手可拖动,所有业务按钮保持 no-drag,面板内提供退出按钮。退出消息只接受来源固定页面、无额外字段的 lserp.window.close。它不进入旧 ERP 的 .NET Framework 4.0/DevExpress 进程,只通过当前 Windows 用户可访问的 Named Pipe 与 Lskj.AgentBridge 通信。
启动前配置:
LSERP_ASTRBOT_BASE_URL=http://127.0.0.1:6185
LSERP_PET_SPRITE_PATH=%USERPROFILE%\.codex\pets\guga\spritesheet.webp
正式宿主必须由商用包内 Start-LserpAgentPet.ps1 启动。启动器会先选定唯一 ERP AgentBridge,再同时注入格式为 lserp-pet-p<PID>-s<启动Unix秒>-c<32位会话作用域令牌>-<32位随机数> 的 LSERP_ASTRBOT_SESSION_ID、完全一致的 LSERP_AGENT_BRIDGE_PROCESS_ID,以及预检时人工批准的数据库作用域指纹、用户、账套和子系统。作用域令牌按版本化、UTF-8 字节长度前缀合同共同绑定这四项值;宿主会独立重算并与会话后缀及显式配置固定时序比较。缺失任一值、缺启动时间/作用域令牌、PID 或作用域不一致、沿用旧式会话,或直接双击宿主都会失败关闭,避免桌宠静默连接另一账套或同进程内已切换的 ERP 会话。
宿主对页面发起的 health/capabilities.list/command.execute 不只依赖启动时预检:它会在实际请求前后分别读取严格 context.get,逐字核对数据库作用域指纹、用户、用户名、账套、子系统和管理员属性。请求前不符返回 erp_session_scope_mismatch 且不发送目标操作;请求期间身份或权限范围切换则丢弃结果、撤销宿主内全部待确认计划并返回 erp_session_scope_changed,要求从目标 ERP 重新启动桌宠。health/capabilities.list 还要求子系统显示名、当前模块和已打开模块集合前后相同;command.execute 可能按可信计划打开模块或完成初始化追踪,因此允许 UI 模块集合发生预期变化,但不会放宽身份范围。直接读取上下文也必须命中批准范围。这层桌面保护之外,ERP CommandKernel 仍会在计划与确认执行时独立复核当前权限、配置、目标模块和数据库作用域。
生产环境把只授予 chat + file scopes 的 AstrBot API Key 写入当前 Windows 用户的通用凭据 Langsu.Lserp.AstrBot.ApiKey;宿主优先从 Windows 凭据管理器读取。LSERP_ASTRBOT_API_KEY 仅作为本地开发兼容入口,也可用 LSERP_ASTRBOT_CREDENTIAL_TARGET 指定其他凭据目标。
当前桌宠只接受同机 loopback AstrBot,因为插件需要访问当前 Windows 用户边界内的 ERP 命名管道;即使是 HTTPS,远程 AstrBot 也会失败关闭。中央服务模式必须先实现客户端主动出站、双向设备身份的 Agent Gateway,不能把命名管道或仅靠 bearer key 的直连伪装成已支持。MiniMax Key 只保存在 AstrBot 服务端,不能配置到本宿主。HTML、CSS 和 JavaScript 全部作为资源嵌入已签名宿主,只按固定内存 URL 提供,不再加载可被预检后替换的外部 Web 目录;宿主同时关闭重定向、DevTools、外部导航、自动填充、Host Object 和外部窗口。
宿主启动前会用一个禁止写入/删除共享的文件句柄把素材有界读入内存,并对同一批字节解析 RIFF/WebP 的 VP8X、VP8 或 VP8L 头;只接受 1536×1872 的静态图集。扩展名伪装、错图、截断、尺寸溢出、非零填充和符号链接都会失败关闭。宿主不再映射素材父目录,只在固定 https://lserp-sprite.local/atlas.webp 返回这份已验证内存副本,其他路径为 404,避免校验后替换和相邻文件暴露。页面随后还必须由 WebView2 完整解码并复核 naturalWidth/naturalHeight,成功前不会连接 ERP、发送聊天或开放附件/执行按钮;压缩流损坏或十秒超时都会停在错误状态。商用预检的素材哈希也来自同一个已验证文件句柄,素材仍必须放在包外并另附书面授权证据。
宿主只信任精确锁定并通过启动预检的 AstrBot 4.27.2 成对工具事件:tool_call 必须声明 erp_plan_command,随后的 tool_call_result 必须具有相同调用 ID,内部计划命令还必须与调用参数一致。只识别采购、请假和动态模块 Lookup 三条固定的服务器自动续接转换;动态模块只有在 ERP 桥实际发布 module.record.create、返回可执行 write 计划且通过客户事务适配器验收时才点亮执行按钮,回退到 module.record.prepare-create 的参数预演仍不可执行。普通工具结果、模型正文和无调用身份的旧格式都不能注册为可执行计划。
工具结果中的 ERP 计划还必须精确符合共享 erp-agent-wire-contract-v1.json:15 个字段一个不能少也不能多,命令版本、模块、服务端输入指纹、稳定结果码、创建/过期时间、风险和执行标记必须合法,title/preview/outcomeCode 必须与 data 投影一致。宿主只接受同机生成、创建时间未漂移且总有效期不超过 15 分钟的计划;这一步在页面显示和进入命名管道前完成。module.record.create 还必须精确显示有界的主表/明细参数 ID、业务标签、类型和值,与 data.parameterPreview 逐字一致,并带签名适配器证据摘要;缺值、多字段、重复 ID、超限或伪造物理字段都不进入确认。
AstrBot 本机桥会把经过协议校验的 bridgeCorrelationId 随最终计划交给宿主;解析命令与固定 create 续接共用该 ID。宿主把它与计划一同保存在可信表,页面只能原样带回;替换、遗漏或使用另一个关联 ID 会在触达命名管道前返回 plan_correlation_mismatch。因此最终解析、计划、桌宠点击、ERP 原生确认、执行审计及请假后续提交计划可以用同一个 ERP 关联 ID 贯通,页面自己的聊天错误关联 ID 不能覆盖它。
WebView 入站 JSON 必须使用精确消息、请求和 payload 字段,任何层级的重复字段、未知字段、错误类型或非 32 位服务器计划 ID 都会在触达命名管道前被拒绝。宿主读取命名管道时还会校验发现文件的 PID/文件名/进程启动时间,并把响应绑定到当前请求和关联 ID;浏览器层的校验不能替代这一宿主边界。
页面发送的 idempotencyKey 只参与协议形状检查,不会直接进入 ERP。宿主在登记可信服务器计划时生成并保存独立的 pet-* 幂等键:同一计划在超时或可重试失败后始终复用同一键,请假创建返回的新提交计划则获得不同键;执行请求进入串行队列后还会再次复核计划是否仍然可信。
已确认写命令完成后,宿主只会把同一 ERP 命名管道响应中的 followupPlan 登记为下一条可信计划。允许的状态转换固定为 hr.leave.create -> hr.leave.submit/module.navigate 与 purchase.invoice.create -> module.navigate,且后续计划的模块编号必须和刚完成的写计划逐字一致:前者把“请假创建后提交”拆成第二次预览和第二次点击,导航则用于采购/请假写适配器返回 needsUi=true 的情况;其他命令或跨模块计划即使结构本身合法也不能伪装成写入后续动作。后续计划会重新执行全部字段、风险、时间、关联 ID 和容量校验;无效对象在交给 WebView 前被替换成 followupPlan=null/followupCode=followup_plan_rejected,不会短暂显示成可执行按钮。计划执行成功后立即从可信表移除,不能重复点击执行。
成功响应和失败响应使用同样的失败关闭边界。宿主按可信计划命令与固定结果码重建浏览器回执,不转发客户处理器的自由 message/data;普通成功只保留固定消息、记录号和重放标记。module.trace-initialization 的丰富 ERP 结果会先经过精确字段、计数、哈希、结果码和计划关联校验,再缩减为稳定分类、计数、固定说明和不可反查别名,原始 SQL 事件、异常文字、物理标识和提示词样式文本均被丢弃。
这份诊断投影最多保留一条、十分钟,并只作为 Host 拥有的独立消息部件注入下一次 AstrBot 对话。完整 SSE 流结束后按随机令牌消费;流失败不消费,方便用户重试。后续正常对话不会再次带入同一证据,ERP 会话范围变化会清空待用证据,用户正文包含保留标记则在发往 AstrBot 前拒绝。因此“执行后继续追问原因”可以使用刚才的真实执行结果,同时不能由页面或用户伪造诊断上下文。
附件链路使用 Windows 原生选件窗口。WebView 只能收到 AstrBot attachment_id、脱敏文件名、类型和大小,不能收到本地路径或内容摘要。宿主默认允许 PNG/JPG/WebP/PDF/XLSX/UTF-8 CSV,最多 3 个、单个 12 MB、总计 36 MB;会校验文件签名、XLSX ZIP 结构和整份 CSV 编码,并在禁止并发写入的同一文件句柄上计算内容 SHA-256。附件在普通回复、零/多候选、其他业务计划和流中断后继续保留;只有可信的 purchase.invoice.resolve -> purchase.invoice.create 计划同时给出相同附件数量与相同附件集合摘要时才会消费。摘要算法与写入验收一致:源文件 SHA-256 去重、Ordinal 排序、单个换行连接且末尾无换行,再计算 SHA-256。数量或摘要不匹配时,宿主移除可信计划、返回 attachment_plan_binding_invalid 并禁止确认。可调整:
LSERP_ATTACHMENT_MAX_COUNT=3
LSERP_ATTACHMENT_MAX_FILE_MB=12
LSERP_ATTACHMENT_MAX_TOTAL_MB=36
LSERP_ATTACHMENT_UPLOAD_TIMEOUT_MS=120000
LSERP_ATTACHMENT_MAX_COUNT 只允许 1-3,LSERP_ATTACHMENT_MAX_FILE_MB 只允许 1-12;总量不得超过“数量 × 单文件上限”。12 MB 上限与 AstrBot 受限文档解析子进程一致;超出时 Host 在选件/上传前失败关闭,避免附件上传成功后才被预处理器拒绝。
AstrBot 会在服务端保存已上传附件。上线前必须配置独立低权限运行账号、存储加密、租户隔离和定时清理;在已验证的 AstrBot 4.27.2 源码中,OpenAPI 的附件 DELETE 路由当前未实际删除文件,不应把它当作数据销毁保证。
构建:
dotnet build -c Release
生成不依赖目标机预装 .NET 8 Desktop Runtime 的 Windows x64 发布目录:
dotnet publish -p:PublishProfile=WinX64
发布目录仍要求目标机安装 Microsoft Edge WebView2 Runtime。禁止开启 trimming 或把 WebView2 强行合并为单文件;正式交付还需代码签名、安装包,以及 Windows 10/11 的透明窗口与高 DPI 回归测试。
桌宠最多保留 64 个尚未过期的可信执行计划;注册新计划前先清理过期项,达到上限后返回稳定的 plan_store_capacity_exceeded,不会清空仍待用户确认的计划或继续增长内存。
跨平台宿主契约测试当前为 50 项,覆盖精确 ERP 进程会话绑定、AstrBot 4.27.2 首个 SSE session_id 回显与后续 message_id 单次运行绑定、串线后计划撤销、共享 ERP 线协议的八个业务场景与篡改负例、严格桥协议、可信计划链及容量边界、成功/失败回执安全投影、诊断证据下一轮单次绑定、浏览器关联 ID 替换拒绝、严格窗口控制消息、宿主绑定幂等键、执行队列后二次计划复核、采购/请假执行边界、WebP 图集结构/尺寸、附件隔离、澄清轮次保留、集合摘要绑定失败关闭和上传契约。