
Wails v3 的 AI Agent 协作规范与 Streams 运行时架构深度解析【免费下载链接】wailsCreate beautiful applications using Go项目地址: https://gitcode.com/gh_mirrors/wa/wails本篇指南围绕仓库根目录下的 AGENTS.md 展开系统解读该 Wails v3 项目为 AI Agent含 GitHub Copilot、Claude 等编码助手制定的工程协作规范从 GitHub Issue 驱动的任务流转、history/临时文档治理、coderabbit质量门禁到前端运行时双构建产物的机制再到 Streams 传输层内部文档的源码级解读。读完你将掌握在 Wails 仓库中正确发起协作任务、构建运行时、接入 Streams 以及安全收尾会话的完整流程并能在v3代码库中精准定位每项规范背后的实现证据。1. 这份 AGENTS.md 是什么AGENTS.md是 Wails 仓库专门写给AI 编码代理而非人类贡献者的“行为契约”。它解决的核心矛盾是当 AI 以半自主方式参与 Issue 管理、代码生成、文档整理和 Git 提交时如何保证工作流可追踪、可审计、不破坏项目既有约定。从内容结构看它由四大部分组成GitHub Issue 工作流确立 GitHub Issues/PR 为唯一权威追踪器AI 生成规划文档的治理约定临时文档统一放入history/目录前端运行时双构建产物澄清wailsio/runtime两种输出物及其消费方避免“只重建一半”的经典错误会话收尾Landing the Plane规定结束工作会话时的强制步骤。以下各节将逐项拆解并补充仓库中的源码、配置与测试证据。该文档本身不包含可执行代码示例因此本文会将其规范与v3/目录下的实际实现对应起来帮助读者把“规则”映射到“代码”。2. Issue 驱动的协作工作流2.1 权威追踪器原则AGENTS.md 首先强调一条硬性约定GitHub Issues 与 Pull Requests 是本项目唯一的权威追踪器禁止在本地平行维护一套 Issue 数据库或 Markdown 任务清单。这意味着 AI 代理不得在仓库内新建TASKS.md、TODO.md之类的文件来替代 Issue 系统。该约定与仓库中的自动化配置相互印证——.github/workflows/labeler.yml 负责自动打标签、.github/workflows/stale-issues.yml 负责清理陈年 Issue、.github/workflows/redirect-enhancement-issues.yml 负责 issue 重定向全部围绕 GitHub Issues 生态运转。2.2 建议的代理工作流文档给出五步工作流可直接作为 Agent 的 checklist 使用先搜索后创建在新建 Issue/PR 前先检索现有条目避免重复仓库中 .github/workflows/pr-master.yml 等 PR 检查流水线会进一步保证合入质量工作与 Issue/PR 建立关联实现代码必须链接到对应 Issue 或 PR新发现的可行动工作以 GitHub Issue 形式登记附上复现细节、影响范围、验收标准三要素用标签/里程碑/交叉引用表达语义优先级用 labels发布范围用 milestones依赖关系用 cross-references关闭 Issue 的唯一条件工作已完成且验证通过否则必须写明延期或不再适用的原因。2.3 强制规则不得重复已存在的 Issue当 Issue 范围或复现步骤变化时及时更新描述AI 生成的规划文档存放在history/而非仓库根目录提交前必须执行coderabbit --plain提前暴露问题详见第 4 节所有提交必须使用taliesin-ai身份在用户明确手动确认之前绝不 push。3. AI 规划文档的存放治理history/ 目录AI 在开发过程中往往会生成 PLAN.md、ARCHITECTURE.md、DESIGN.md、CODEBASE_SUMMARY.md、INTEGRATION_PLAN.md、TESTING_GUIDE.md、TECHNICAL_DESIGN.md 等临时规划文档。AGENTS.md 明确建议在项目根目录创建history/目录所有临时的 AI 规划/设计文档统一放入其中仓库根目录保持整洁只保留永久性项目文件仅在被明确要求回顾历史规划时访问history/。文档还给出了可选的.gitignore片段# AI planning documents (ephemeral) history/其收益包括根目录整洁、临时与永久文档分离、按需排除出版本控制、保留“考古式”的规划历史、降低浏览项目的噪音。仓库中 history/beta-readiness-audit.html 正是这种治理实践的现存示例——它是一份 beta 就绪审计文档被收纳在history/而非仓库根目录印证了该规范的实际落地。4. 提交前的质量门禁coderabbit 与提交身份AGENTS.md 规定了一条不可省略的命令coderabbit --plain--plain表示以纯文本无交互、无花哨输出方式运行 CodeRabbit 代码审查在提交前捕获潜在缺陷。CodeRabbit 作为 AI 审查机器人其价值在于把“过早合入问题代码”的成本前移——这与仓库的 CI 策略互为补充例如 .github/workflows/semgrep.yml 提供静态安全扫描.github/workflows/build-and-test-v3.yml 提供构建与测试矩阵而coderabbit --plain则是开发者/AI 在本地提交前的最后一道人工机器审查。配套的提交身份约定为所有提交必须使用taliesin-ai身份未经用户显式手动确认绝不 push。这保证了仓库历史中 AI 提交可被统一识别与回溯。5. 前端运行时必须理解的两个构建产物AGENTS.md 明确指出v3/internal/runtime/desktop/wailsio/runtime下的 TypeScript 运行时会产生两个相互独立的产物。只重建其中一个而忽略另一个是常见且极易混淆的错误任务产物消费方task v3:runtime:build:assetsv3/internal/assetserver/bundledassets/runtime.js另有.debug.jswebview以/wails/runtime.js路径被资源服务器提供task v3:runtime:build:packagenpm 包目录下的dist/应用的 frontend通过node_modules导入这两个任务在源码中有精确定义v3/internal/runtime/Taskfile.yaml中的build:assets依赖build:debug与build:production两者均用 esbuild 将src/index.ts打包进../assetserver/bundledassets/而build:package则调用npm run build:code生成 npm 包用的dist/。产物的去向也清晰可见v3/internal/assetserver/bundledassets/ 目录下确实存在runtime.js与runtime.debug.js两个文件。5.1 为什么必须两个都重建webview 侧桌面应用的 HTML/JS 运行时由 Go 侧通过go:embed内嵌的bundledassets/runtime.js提供webview 加载/wails/runtime.js获得运行时能力frontend 侧生成的应用通过 npm 导入wailsio/runtime包Vite 等打包器从node_modules解析该包走的完全是另一条路径。因此只要修改了src/下的任何代码例如stream.ts两个产物都必须重建并一并提交。5.2 CI 如何强制“产物与源码一致”AGENTS.md 提到“CI 会校验提交的 bundle 与build:assets输出完全一致”。仓库中的 .github/workflows/verify-runtime-assets.yml 正是这项强制检查的实现该 job 运行于每个 PR且当 PR 未改动v3/internal/assetserver/bundledassets或v3/internal/runtime相关路径时自动跳过一旦相关路径有改动它会对 PR 源码做一次干净的重新构建然后与提交中的runtime.js/runtime.debug.js逐字节比对由于 minified JS 无法靠肉眼审查该检查将 bundle 视为“内部构建产物”并要求 PR 必须提交与源码严格匹配的字节。这意味着 Agent 修改运行时源码后不能只提交.ts源码而忘记更新 bundle——CI 会直接拒绝。5.3 用本地 checkout 的运行时测试应用一个生成的 Wails v3 应用从 npm 导入wailsio/runtime因此看不到本 checkout 中对运行时所做的改动。要在真实应用上测试工作树中的运行时改动AGENTS.md 给出了命令task v3:install-runtime -- ./path/to/your-app/frontend该任务在 v3/Taskfile.yaml 中定义它首先依赖runtime:build:package重建dist/然后进入目标 frontend 目录执行npm install file:...指向本仓库的运行时包确保安装的一定是当前源码构建的产物。撤销方式同样简单——在相同目录执行npm install wailsio/runtimelatest6. 子系统内部文档索引以 Streams 为例AGENTS.md 建议部分子系统为 Agent 准备了专门的内部文档页面修改对应代码前必须先阅读因为其中若干设计决策看似随意实则都是为了规避某个已被测量的 bug。它给出的示例是Streams涉及文件v3/pkg/application/stream*.goGo 侧与v3/internal/runtime/desktop/wailsio/runtime/src/stream.tsTS 侧内部文档docs/mpress/content/guides/advanced/streams-internals.mpd覆盖 held-poll 设计、缓冲区常量及其选择方法、会话与连接生命周期、传输层选择以及未完成事项若要把现有 WebSocket 实现迁移到 Streams遵循 docs/mpress/content/guides/advanced/streams-from-websockets.mpd——一份机械式检查清单其中标注了“静默失效”的差异点。这一节的深层含义是Wails v3 的文档体系已经为 AI 代理做过索引优化——遇到 Streams 相关改动Agent 应先读内部文档再动手否则很容易“破坏一个被测量的修复”。6.1 源码侧的文件分布AGENTS.md 中列出的 Streams 实现文件均可在仓库中定位v3/pkg/application/stream.go公开 API、StreamConn、streamSink、管理器与注册表v3/pkg/application/stream_session.go单窗口单次页面加载的会话——出站队列、帧类型、连接表v3/pkg/application/stream_transport.go两个 HTTP 端点、二进制分帧、分块重组、运行时 preludev3/pkg/application/stream_server.go-tags server构建下的真实 WebSocket sinkv3/pkg/application/stream_prelude_desktop.go 与 stream_prelude_server.go在 bundle 服务时选择客户端传输层v3/internal/runtime/desktop/wailsio/runtime/src/stream.tsWebSocket形态的客户端实现v3/tests/stream-performance/负载测试工具-upload、-reloads、场景扫描。6.2 Streams 的核心设计非对称传输内部文档指出Go→JS 与 JS→Go 使用不同机制而这种不对称性正是整个设计的核心Go webview ── ─────── Send() ─► per-window queue ─────────► GET /wails/stream/poll (held open) └─ one held request per window, carrying frames for every connection Receive() ◄─ per-conn inbox ◄──────── POST /wails/stream/send (one or more frames)Go→JS 是“保持打开的轮询”held poll请求一直挂起直到有内容可投递。没有轮询间隔也没有自适应机制——服务器挂住直到帧出现投递延迟天然接近 0任何客户端间隔都只会增加延迟。响应在途期间到达的帧会累积到下一次响应往返本身即批处理窗口。实测100/s 时每响应 1.0 帧5000/s 时仍为 1.020000/s 时达到 3.4且 p99 延迟随速率上升反而下降。JS→Go 是普通 POST发送按连接用 promise 链串行化并发fetch不保序而 Go 依赖发送顺序即观察顺序。Go 在响应之前就把被接受的帧或批次前缀追加到连接的 inbox因此客户端不可能越过 Go 尚未排队的字节。每窗口只有一个在途 poll复用所有连接单队列、单 drainer顺序正确性由构造保证同时绕开了 Windows 上 HTTP/1.1 每主机 6 连接的瓶颈这些是http://wails.localhost上真实的 Chromium 网络请求。6.3 为什么做这些特定决策每个都是一道“疤痕”内部文档强调以下每一条都来自事件传输工作的实测教训移除任一决策都会重新打开一个已测量的 bugGo→JS 路径不碰主线程Send在互斥锁下追加后立即返回。事件系统曾在主线程 emit 与 goroutine emit 并发排队时内联执行 eval导致三个平台上 4.4% 的事件乱序。单队列单 drainer 从构造上杜绝了这一点。任何规模下都不使用evaluateJavaScript把 payload 拼接进 eval 源码会保留宿主内存且存在平台相关的“拐点”macOS 11.6 GB、WebKitGTK 6.2 GB均为 100 × 1 MB/s 场景。Streams 完全不经过 eval因此恒定字节速率扫描在每个帧大小下都是平坦的。控制数据走 header不走路由参数或 query stringWebKitGTK 6.0 可能把自定义 URI scheme 的 POST body 当作 query 参数投递transport_http.go为此带了一个 fallbackWebView2 则把 body 投递上限限制在约 2 MB。poll 响应是二进制而非 JSON帧是[]byte若用 JSON 信封装 base64每帧多花 33% 开销还会在 UI 线程上多一次解析。二进制帧格式为magic WS1\0 | flags u8 | count u32 | count × ( connID u32 | kind u8 | len u32 | payload )kind取值 data / open / close / error。没有序号、没有 ack——WebSocket 本就不回放断连就丢失在途数据模拟它比维护一个有界缓冲无法总是满足的游标更简单也更诚实。挂住请求是安全的每个 webview 请求本来就有自己的 goroutine。assetserver_webview.go中的dispatchWorkers被固定为 0且注释明确点名此场景若开启该池必须先为请求生命周期设定上界。6.4 缓冲区常量编译期常量而非选项所有常量集中在 v3/pkg/application/stream.go 中。它们是编译期常量不是配置项——没有Options.Streams也没有按流设置。修改即改源码常量值约束对象streamOutQueueBytes8 MB每窗口等待收集的字节streamOutQueueDepth256每窗口缓冲的帧数streamOutQueueBytesGlobal/streamOutQueueDepthGlobal256 MB / 8192整个应用范围的出站数据缓冲streamInQueueBytesGlobal/streamInQueueDepthGlobal256 MB / 8192整个应用范围等待Receive的入站数据streamMaxConnections256单会话内的活动连接 排队关闭streamMaxConnectionsGlobal4096整个应用范围的活动连接streamOutCloseDepthGlobal4096整个应用范围未投递的关闭通知streamMaxSessionsPerWindow16单窗口可持有的会话数超限时新代次必须取代旧代次streamMaxSessions1024整个应用范围的会话数streamOutControlDepth/streamOutControlDepthGlobal256 / 4096排队的非关闭控制帧每会话 / 全应用streamMaxChunkSets/streamMaxChunkTotal256 / 4096每会话未完成上传数 / 单次上传的分块数streamMaxChunkBytesGlobal/streamMaxChunkPartsGlobal128 MB / 4096全应用范围的分块 payload / 分块元数据streamMaxChunkIDLen64 字节单个客户端提供的 chunk-set 标识符streamMaxResponseBytes1 MB单个 poll 响应streamHoldTimeout20 s空 poll 挂起的最长时间streamSessionTTL60 s无 poll 且无活动连接 ⇒ 会话死亡streamSessionGrace10 min有活动连接但长时间无 poll ⇒ 会话死亡streamSessionSweep20 sjanitor 扫描死亡会话的周期streamMaxFrameBytes64 MB任意方向单帧上限streamMaxNameLen256 字节单个注册或请求的流名称streamInQueueDepth/streamInQueueBytes256 / 8 MB已接收但未被Receive取走的帧如何选择这些值内部文档的调参指南streamOutQueueDepth故意不是eventQueueCapacity64后者是按“一次 eval 排空一个”的队列测出来的深度只增加尾部延迟而 poll 按批排空深度必须覆盖一个往返的生产量——5000 帧/s、5 ms 往返约 25 帧256 给突发留了余量且不阻塞生产者。streamOutQueueBytes才是真正重要的上界256 帧 × 1 MB 就是 256 MB。它是前端停止收集时宿主内存的后盾。两条交互规则第二条极易被无意破坏深度与字节上限约束的是累积量空队列始终接受一帧无论多大。无条件执行字节上限会让“大于上限的帧”根本无法发送——等待条件永远无法成立Send永久阻塞、TrySend永远报告满。帧大小并非总是调用方能选择的带[]byte字段的结构体 marshal 成多大就是多大。streamMaxResponseBytes的存在是因为 WindowsWebView2 响应写入器会先把整个 body 累积在内存中到Finish才交接无界响应即无界分配。调高它不会提升 Windows 吞吐——实测 Windows 瓶颈是按字节而非按响应帧扫描中响应/s 波动 4 倍而 MB/s 稳定在约 90。入站上限是前端等待的原因桌面上deliver报告满时端点返回429客户端用有界退避重试同一帧或未被接受的批次后缀server 模式下 socket 读泵等待由 TCP 施加背压。若无此上限一个迟迟不调Receive的 handler 会让宿主内存无限增长。控制帧绕过数据上限但有独立生命周期上限丢一个数据帧只是变慢丢一个 open ack 会让前端永远停在CONNECTING丢一个 close 会让前端认为死连接还活着。因此非关闭控制帧有独立的有界队列每个被接受的连接还会预留一个 close 槽位。每会话上限都配套全应用级上限否则每个被准入的会话/连接都能同时占满自己的本地配额。出站、入站数据共享独立的 256 MiB / 8192 帧预算活动连接 4096 项预算未投递的关闭通知另有同尺寸预算。两个预算刻意分离每个预留恰好由唯一所有者释放连接的槽位由只运行一次的shutdown释放关闭帧的槽位由处置该帧的一方释放避免所有权迁移导致的永久泄漏早期版本曾因此泄漏。Go 帧转移所有权、JS 帧快照拷贝Go 的Send在传输层写入前保留调用方切片调用成功后不得再修改/复用该存储JS 的send()返回前复制可变二进制输入与原生 WebSocket 所有权语义一致。JS 发送遵循 WebSocket 缓冲契约send()不能阻塞bufferedAmount包含该 socket 保留的所有字节是调用方的背压信号宿主侧队列仍由上述上限独立约束。分块重组共享宿主内存配额每会话可组装最大 64 MiB 的单帧但该配额不能乘以每个准入会话。未完成/可重试的 chunk 集共享 128 MiB 准入 payload 预算完成时短暂同时保留分块与连续组装帧仍处于 256 MiB 有效内存天花板内保留的分块另共享 4096 项元数据配额。poll 重试只针对可恢复失败网络错误、408请求超时、425早期数据、429背压、5xx服务端错误使用 250 ms 起步、最高 5 s 的指数退避其余4xx属于协议或所有权失败立即关闭页面 Streams410是退役会话的干净终止信号。关闭最后一个连接会中止在途 poll 或退避定时器。streamSessionTTL必须明显高于streamHoldTimeout否则会话会在自己的 poll 合法挂起期间被回收。调参经验小消息密集负载先撞深度上限大 payload 先撞字节上限。典型应用无需调整——macOS 上默认值可支撑 634000 帧/s 与 2100 MB/s该数据来自内部文档的实测记录。6.5 会话与连接生命周期会话session 某窗口的一次页面加载以客户端生成的 id类似运行时clientId为键由先到的请求惰性创建。当平台无法识别请求窗口windowID 0时会话 id 仍全局有界但代次generation故意不比较。三种关闭机制按感知速度排序新代次会话 poll 取代旧代次重载会生成新会话 id 并在窗口的sessionStorage中递增代次同一值镜像到window.name存储被禁用时仍能跨重载存活并锚定到performance.timeOrigin旧引擎回退Date.now()。poll 只退役更低的页面代次窗口被销毁时旧会话的连接立即关闭。窗口销毁丢弃该窗口的所有会话类似eventPayloadStore.dropWindow。TTL 回收兜底一切其他情况渲染进程崩溃、机器休眠。只有前两种机制退役页面代次TTL 清理只移除空闲会话而不推进“已退役代次水位线”这样仍加载的页面在最后一个连接关闭后还能再开新流。已经被真正取代的代次仍被封锁因为新页面的 poll 会在旧会话被移除前推进水位线。Apple WebView 会报告已取消的请求macOS/iOS 上 WebKit 的stopURLSchemeTask回调会取消匹配的请求上下文使导航离开后的 poll 立即解除阻塞。Linux/Windows 当前桥接层没有等效的提前中止回调parked 请求会一直挂到 hold 超时连接仍会按规则 1 及时关闭取消在 Linux 上表现为EPIPE在 Windows 上直到Finish才显现。6.6 传输层选择机制Stream(name)会查询window._wails.streamFactoryserver 构建安装一个返回真实WebSocket的工厂webview 构建保持未设置客户端走 poll 传输层。关键约束是工厂必须在任何模块体执行之前安装因为生成的 bindings 会在模块作用域创建流例如export const Telemetry Stream(telemetry)。custom.js无法胜任——它通过loadOptionalScript注入先做 HEAD 请求再追加script标签落地太晚。正确做法是把工厂前置到运行时 bundle 上见 v3/pkg/application/stream_prelude_server.goES 模块依赖先于导入者求值因此 prelude 同步先于任何生成模块运行。若新增第三传输层同样放入 prelude不要回到custom.js。6.7 尚未完成的事项内部文档的诚实清单事项状态平台层请求取消Apple 已完成Linux/Windows 待办缓冲区常量作为选项未做仅编译期类型化流未做有意为之帧按决策就是[]byte流水线第二个在途 poll未做需要 JS 侧有序重组每连接公平性未做——同窗口连接共享一个队列洪泛连接会拖慢邻居JS→Go 帧合并已完成——在途请求后累积的帧以有界批次发送轻载连接仍每 POST 一帧Windows 吞吐约 100 MB/s受WebResourceRequested编组限制候选修复是共享缓冲区PostSharedBufferToScriptbindings 已存在于internal/webview2/pkg/webview2/但未接入pkg/edgewails3 dev/ Vite可用——已验证生成vanilla-js项目Vite dev server 在/代理/wails/stream/*在代理前被资源服务器中间件匹配多窗口负载下未测虽然会话按构造是窗口作用域的6.8 Streams 的测试与验证内部文档给出可直接运行的测试命令go test ./pkg/application/ -run TestStream -race # protocol, ordering, backpressure go test -tags server ./pkg/application/ -run TestServerMode pnpm --dir v3/internal/runtime/desktop/wailsio/runtime test顺序测试是最关键的一个八个 goroutine 并发发送在队列锁下分发计数器排空顺序必须与被接受顺序完全一致。一旦失败说明“单 drainer”不变量被破坏。负载测试工具v3/tests/stream-performance/go run ./tests/stream-performance -duration 20s # full sweep go run ./tests/stream-performance -upload -duration 10s # JS→Go matrix go run ./tests/stream-performance -reloads 6 # connection lifecycle在 Windows 上负载测试必须在交互式控制台会话中运行纯 SSH 调用会在 session 0 中以零长度输出失败且二进制必须放在 SSH 账号与控制台账号都能读取的位置C:\Users\user被 ACL 限制为属主。7. 会话收尾Landing the Plane结束工作的强制流程AGENTS.md 规定了结束工作会话时的强制工作流任何 Agent 都应严格遵守为剩余工作登记 Issue——所有需要跟进的事项创建为 Issue运行质量门禁若改了代码——测试、lint、构建更新 Issue 状态——关闭已完成项更新进行中项准备远端同步git status工作树干净且确认要同步时执行git pull --rebase只有用户明确确认后才git push清理——审查 stash仅移除过时的清理远端已合并分支验证——所有预期变更在请求时均已存在并已提交交接——为下一会话提供上下文。三条关键规则贯穿始终每次提交使用taliesin-ai身份未经用户显式手动确认不 push清晰汇报变更状态未提交 / 本地已提交 / 已推送。8. 结语让 AI 代理在 Wails 中“可预期地工作”从 AGENTS.md 可以提炼出 Wails v3 对 AI 协作的完整治理哲学一切可追踪Issue/PR 是唯一事实源规划文档归档于history/一切有门禁提交前coderabbit --plainCI 层verify-runtime-assets保证运行时产物与源码逐字节一致一切有依据Streams 内部文档把每个看似随意的决策都回溯到被测量的 bug形成“改代码前先读文档”的强约束一切可回退task v3:install-runtime指向工作树、npm install wailsio/runtimelatest撤销双构建产物并提交收尾流程以“不 push 直到用户确认”为底线。对于任何想要为 Wails v3 贡献代码尤其是涉及v3/internal/runtime、v3/pkg/application/stream*.go的改动的开发者或 AI 代理这套规范既是操作手册也是理解代码库演进逻辑的入口。遵守它你的每一次会话都会留下干净、可审计、可交接的痕迹。【免费下载链接】wailsCreate beautiful applications using Go项目地址: https://gitcode.com/gh_mirrors/wa/wails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考