Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,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` (86: 22 daemon_client + 22 app-commands + 17 renderer smoke + 15 i18n + 7 integration + 3 commands); RESPONSE_TYPES mirror daemon protocol verified against `daemon.py`
- Unit tests `npm test` (88: 22 daemon_client + 22 app-commands + 19 renderer smoke + 15 i18n + 7 integration + 3 commands); 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)
Expand Down Expand Up @@ -94,7 +94,7 @@ pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.port; python -m emrg
```

Python: `uv run pytest tests/ -v` (493) — import check: `uv run python -c "from emrg.client.app import run_client"`
GUI: `cd emrg/gui && npm test` (86: 22 daemon_client + 22 app-commands + 17 renderer smoke + 15 i18n + 7 integration + 3 commands) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js`
GUI: `cd emrg/gui && npm test` (88: 22 daemon_client + 22 app-commands + 19 renderer smoke + 15 i18n + 7 integration + 3 commands) — 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 上下文)

## Configuration
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ uv run python -m emrg # launch TUI
cd emrg/gui
npm ci # install deps (production: --omit=dev)
npm start # launch GUI (auto-starts daemon)
npm test # run Node tests (86: 22 daemon_client + 22 app-commands + 17 renderer smoke + 15 i18n + 7 integration + 3 commands; integration runs in CI, local: npm run test:integration)
npm test # run Node tests (88: 22 daemon_client + 22 app-commands + 19 renderer smoke + 15 i18n + 7 integration + 3 commands; integration runs in CI, local: npm run test:integration)
```

CI runs tests and checks for conflict markers automatically via GitHub Actions (`.github/workflows/test.yml`).
Expand Down
5 changes: 5 additions & 0 deletions emrg/gui/renderer/js/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ const Chat = (() => {
if (data.timeout) {
addSystemMessage(EMRG_Copy._t("chat.timeoutWarn"));
}
// 工具调用次数上限中断(跨项目教训:截断的工作不提示 = 用户拿半成品)
// 对齐 TUI:明确提示结果可能不完整 + 可继续(TUI 已有 "Try '继续' to resume")
if (data.content && /exceeded/i.test(data.content) && /max|limit|round/i.test(data.content)) {
addSystemMessage(EMRG_Copy._t("chat.maxRoundsHint"));
}
}

/** 工具友好状态行(进行中 → 完成/失败,默认折叠,点开展示原始输出) */
Expand Down
2 changes: 2 additions & 0 deletions emrg/gui/renderer/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ const I18N = (() => {
"chat.copyFailed": "复制失败",
"chat.fromOtherClient": "(来自其他客户端)",
"chat.timeoutWarn": "⚠️ 响应超时了,我还没说完——可以再试一次。",
"chat.maxRoundsHint": "⚠️ 这次任务因为工具调用次数上限中断了,结果可能不完整——输入“继续”我可以接着做。",
"chat.elapsed": "耗时 {s}",
"chat.expand": "展开全文",
// 时间分组(utils.js/sidebar.js)
Expand Down Expand Up @@ -449,6 +450,7 @@ const I18N = (() => {
"chat.copyFailed": "Copy failed",
"chat.fromOtherClient": "(from another client)",
"chat.timeoutWarn": "⚠️ Response timed out — I wasn't done. Try again?",
"chat.maxRoundsHint": "⚠️ This task hit the tool-call limit and may be incomplete — type \"continue\" and I'll keep going.",
"chat.elapsed": "took {s}",
"chat.expand": "Expand full text",
// Time groups (utils.js/sidebar.js)
Expand Down
23 changes: 23 additions & 0 deletions emrg/gui/test/renderer.smoke.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,26 @@ test("WorkBuddy P1:ResultPanel 折叠切换(⌘\ 与按钮)", async () =>
await vm.runInContext("ResultPanel.toggle()", ctx);
assert.ok(!els["result-panel"].classList.contains("collapsed"), "再次 toggle 应展开");
});

test("工具调用上限中断 → 系统提示可继续(对齐 TUI,跨项目教训)", async () => {
const { ctx, els } = makeSandbox({});
await tick();
// 模拟 daemon 广播:max tool rounds 超限的 done 帧
await vm.runInContext(
'EMRG_Chat.handleDone({ request_id: "rid-max", content: "Exceeded maximum tool call rounds (30).", done: true })',
ctx
);
const texts = (els["chat-view"].children || []).map((c) => c.textContent).join("|");
assert.ok(/继续/.test(texts), `应提示可继续,实际: ${texts}`);
});

test("正常 done 帧不触发上限提示(无假阳性)", async () => {
const { ctx, els } = makeSandbox({});
await tick();
await vm.runInContext(
'EMRG_Chat.handleDone({ request_id: "rid-ok", content: "完成了!", done: true })',
ctx
);
const texts = (els["chat-view"].children || []).map((c) => c.textContent).join("|");
assert.ok(!/继续/.test(texts), "正常完成不应出现继续提示");
});