diff --git a/Agent.md b/Agent.md index 721cbb6..2334587 100644 --- a/Agent.md +++ b/Agent.md @@ -37,6 +37,25 @@ EMRG is a self-evolving AI agent architecture experiment. Python implementation, - **README language**: `README.md` = English (default), `README.cn.md` = Chinese - **Project context files**: `README.md` = English, `Agent.md` = English +## Terminology + +Unified vocabulary for the agent's execution model (code terms in `emrg/server/daemon.py`): + +- **Tool loop** (工具循环) — the complete process triggered by one user message: the agent calls tools and sends LLM requests repeatedly until a round produces no new tool calls. Code: "tool loop" (`_run_tool_loop`). +- **Round** (轮) — one iteration inside a tool loop: one LLM request + zero or more tool calls + tool executions. Code: `round_num` (daemon.py:1775 `for round_num in range(1, self._max_tool_rounds + 1)`), bounded by `max_tool_rounds`. +- **Evolution cycle** (演化周期) — a distinct concept: one full run of the self-evolution task ("Prepare → Review → Discover → Improve → Submit → Record"), unrelated to tool loop rounds. + +Hierarchy: +``` +user message + └── tool loop (the whole process) + ├── round 1: LLM request → tool calls → execute + ├── round 2: LLM request → tool calls → execute + └── round N: LLM request → no tool calls → loop ends +``` + +Usage: say "tool loop" for the whole process, "round N" for a single LLM request + tools. Do not call evolution cycles "rounds". + ## Current Features - **TUI Client** — Rich terminal UI with Markdown rendering, syntax highlighting, diff display @@ -66,7 +85,7 @@ EMRG is a self-evolving AI agent architecture experiment. Python implementation, - Streaming chat with delta rendering (16ms batching), markdown on done (marked + DOMPurify + local highlight.js subset), tool call status cards (2000-char truncation + expand) - Session list/switch/new/delete + right-click rename (context menu, #423) synced with daemon; own-stream busy lock (G65); broadcast streams from other clients tagged "来自其他客户端" - Disconnect/reconnect: red status dot, auto daemon respawn (stale-port detection), session resume, input bar restored on disconnect (no 30s fake-timeout) - - Unit tests `npm test` (178: 43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config + 7 gui-state); RESPONSE_TYPES mirror daemon protocol verified against `daemon.py` + - Unit tests `npm test` (179: 43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 4 build-config + 7 gui-state); RESPONSE_TYPES mirror daemon protocol verified against `daemon.py` - **Auto project tracking** — Automatically detects and records working directories; project-scoped sessions - **Rant-driven evolution** — User feedback via `/rant` drives automatic self-improvement cycles - **Headless GitHub auth** — Non-interactive evolution auto-extracts `GH_TOKEN` from git credential store (osxkeychain / credential helper); PR comment/LGTM queries fall back to REST API (GraphQL needs `read:org` scope) @@ -94,7 +113,7 @@ pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.port; python -m emrg ``` Python: `uv run pytest tests/ -v` (681) — import check: `uv run python -c "from emrg.client.app import run_client"` -GUI: `cd emrg/gui && npm test` (178: 43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config + 7 gui-state) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` +GUI: `cd emrg/gui && npm test` (179: 43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 4 build-config + 7 gui-state) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` CI: `uv run pytest` + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文) Re-trigger: `scripts/re-trigger-ci.sh [branch]` (workflow_dispatch, #527 — 替代空 commit 重触发:Actions outage 会整段丢弃 push 事件,dispatch 走 API 路径不受影响) diff --git a/README.cn.md b/README.cn.md index 16ef417..678b1fb 100644 --- a/README.cn.md +++ b/README.cn.md @@ -144,7 +144,7 @@ EMRG 不只是追赶——它自己追上来。 贡献指南、源码安装、架构、详细 FAQ → [DEVELOPMENT.md](DEVELOPMENT.md)。 -快速检查:`uv run pytest tests/ -v`(当前 681 项)· `cd emrg/gui && npm test`(178 项:43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config + 7 gui-state) +快速检查:`uv run pytest tests/ -v`(当前 681 项)· `cd emrg/gui && npm test`(179 项:43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 4 build-config + 7 gui-state) --- diff --git a/README.md b/README.md index c15ca52..580b85d 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ They're products. EMRG is an experiment in *closing the loop* — the AI improve Contributing, source installs, architecture, and the full FAQ → [DEVELOPMENT.md](DEVELOPMENT.md). -Quick checks: `uv run pytest tests/ -v` (currently 681 items) · `cd emrg/gui && npm test` (178: 43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config + 7 gui-state) +Quick checks: `uv run pytest tests/ -v` (currently 681 items) · `cd emrg/gui && npm test` (179: 43 daemon_client + 19 conn-manager + 22 app-commands + 59 renderer smoke + 15 i18n + 7 integration + 3 commands + 4 build-config + 7 gui-state) --- diff --git a/emrg/gui/package.json b/emrg/gui/package.json index 45ae169..e7f9dde 100644 --- a/emrg/gui/package.json +++ b/emrg/gui/package.json @@ -35,6 +35,8 @@ "preload.js", "renderer/**", "vendor/**", + "conn-manager.js", + "gui-state.js", "daemon_client.js", "package.json", "scripts/**" diff --git a/emrg/gui/test/build-config.test.js b/emrg/gui/test/build-config.test.js index 42bf0ea..739afe1 100644 --- a/emrg/gui/test/build-config.test.js +++ b/emrg/gui/test/build-config.test.js @@ -1,16 +1,19 @@ "use strict"; /** - * build-config.test.js — 打包配置守卫(rant 2026-08-10T11:03:51)。 + * build-config.test.js — 打包配置守卫(rant 2026-08-10T11:03:51 + 2026-08-10T20:37:08)。 * - * 打包版 GUI Markdown 不渲染的根因:electron-builder `files` 白名单漏掉 - * `vendor/**` → marked/DOMPurify/highlight 三个第三方脚本没进安装包 → - * renderMarkdown 静默降级纯文本(源码模式正常、打包版中招)。 + * 打包版 GUI 故障的根因(两类同根):electron-builder `files` 白名单漏掉 + * 运行时模块 → 打包后 require 失败 / 功能缺失: + * - #612:漏 `vendor/**` → marked/DOMPurify/highlight 没进包 → Markdown 静默降级 + * - rant 20:37:08:漏 `conn-manager.js` / `gui-state.js` → main.js require 直接抛 + * "Cannot find module './conn-manager'"(v0.2.21 Windows 打包版启动崩溃) * - * 本测试直接钉死三类回归: + * 本测试钉死四类回归: * 1. files 白名单必须包含 "vendor/**"(有人删掉即红) * 2. vendor/ 目录必须存在全部 3 个运行时脚本(build-vendor.js 产物) * 3. renderMarkdown 的 `!window.marked` 降级路径必须带 console.warn - * (静默降级 → 可诊断,问题可快速定位) + * 4. **main.js / preload.js 的每个本地 require("./x") 必须被 files 白名单覆盖** + * (新增本地模块而忘记加白名单 → 即红。conn-manager.js/gui-state.js 即此漏洞的实例) */ const { test } = require("node:test"); const assert = require("node:assert"); @@ -23,6 +26,18 @@ const VENDOR_DIR = path.join(GUI_ROOT, "vendor"); const REQUIRED_VENDOR_FILES = ["marked.min.js", "dompurify.min.js", "highlight.custom.js"]; +/** 判断文件名是否被 electron-builder files 白名单覆盖(支持 "x.js" 与 "dir/**" 形态) */ +function whitelistCovers(whitelist, relPath) { + for (const w of whitelist) { + if (w.endsWith("/**")) { + if (relPath.startsWith(w.slice(0, -3))) return true; // "renderer/**" covers "renderer/js/x.js" + } else if (relPath === w || relPath.startsWith(w + "/")) { + return true; + } + } + return false; +} + test("electron-builder files whitelist bundles vendor/**", () => { const files = (PKG.build && PKG.build.files) || []; assert.ok( @@ -47,3 +62,32 @@ test("renderMarkdown fallback logs when marked is missing", () => { "silent markdown degradation must be diagnostic (console.warn when marked is missing)" ); }); + +test("every main.js/preload.js local require is covered by files whitelist", () => { + const files = (PKG.build && PKG.build.files) || []; + const localRequires = []; + for (const entry of ["main.js", "preload.js"]) { + const src = fs.readFileSync(path.join(GUI_ROOT, entry), "utf-8"); + // 收集 require("./xxx") 与 require("./xxx.js") 形态 + const re = /require\(\s*"(\.[^"]+)"\s*\)/g; + let m; + while ((m = re.exec(src)) !== null) { + let rel = m[1].replace(/^\.\//, ""); + if (!rel.endsWith(".js") && fs.existsSync(path.join(GUI_ROOT, rel + ".js"))) { + rel += ".js"; + } + localRequires.push({ entry, rel }); + } + } + assert.ok(localRequires.length > 0, "no local requires found — scan is broken"); + const uncovered = localRequires.filter(({ rel }) => !whitelistCovers(files, rel)); + assert.deepStrictEqual( + uncovered.map((u) => `${u.entry} → ${u.rel}`), + [], + `local modules required by main.js/preload.js but MISSING from build.files whitelist: ` + + `add each to package.json build.files (e.g. "conn-manager.js", "gui-state.js"). ` + + `These files exist in the repo but would NOT be packed into app.asar → packaged GUI ` + + `crashes with "Cannot find module './'"` + ); +}); +