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
2 changes: 1 addition & 1 deletion Agent.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,7 +118,7 @@ Community needs voiced in HN agent-UI discussions map directly to EMRG's design:
pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.token; python -m emrg
```

Python: `uv run pytest tests/ -v` (1079) — import check: `uv run python -c "from emrg.client.app import run_client"`
Python: `uv run pytest tests/ -v` (1082) — import check: `uv run python -c "from emrg.client.app import run_client"`
GUI: `cd emrg/gui && npm test` (262: 45 daemon_client + 20 conn-manager + 22 app-commands + 132 renderer smoke + 15 i18n + 8 integration + 3 commands + 8 build-config + 7 gui-state + 2 tool-group) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js`
CI: `uv run pytest` (ubuntu + **windows-2025 matrix** — Windows pytest 回归在 PR CI 即失败,v0.2.29 教训 #725) + 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 路径不受影响)
Expand Down
1 change: 1 addition & 0 deletions emrg/server/promote_prompt.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,7 @@ Read the full config of `{{ project.name }}` from `~/.emrg/projects.yml` (path,
#### 0.3 Confirm promotion channel availability

- **Browser-first channel probing (MUST, rant 2026-08-19T21:08:55)**: use `browser-harness` to operate the **real browser** for all channel detection and actions — the login state (cookies/sessions) exists **only in the real browser**. curl/API probes see a logged-out view and misjudge channel availability (e.g. curl returns 000 for Reddit while the browser is logged in and fully usable). When checking whether a channel works / is logged in / can post: **open it in the real browser via browser-harness first**; only fall back to curl for plain public-data reads (public JSON endpoints, docs) when the browser path is unavailable or the data is genuinely public.
- **Direct CDP connection (MUST, rant 2026-08-25T17:57:15)**: all browser operations MUST connect **directly** to the local CDP endpoint `ws://127.0.0.1:57000/devtools/page/...` (HTTP `127.0.0.1:57000/json` returns 200 with the tab list) — following the r47/r48 `_post_*.py` CDP script pattern (websocket to `ws://127.0.0.1:57000/devtools/page/<tab-id>`). **FORBIDDEN**: calling browser-harness's `remote-debugging-setup` / opening `chrome://inspect` — that pops Chrome's "Allow remote debugging?" authorization dialog and blocks waiting for host clicks. The 57000 endpoint is always available on this host; if a direct connection fails, **retry the direct connection** (the tab list may have changed), never switch to the popup flow.
- Check the CLI: `which curl` (public-data reads only — never for login-state judgment)
- Check whether the browser harness skill is available (`/skills` or `ls ~/.emrg/skills/`)
- Channel unavailable → record it in the state file (blocked = channel unavailable); skip channel actions this round, but still write the reflection
Expand Down
5 changes: 5 additions & 0 deletions emrg/server/prompts/system.j2
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,6 +85,11 @@ Index: `{{ session_memory_index_path }}`
**To read history**: use the `read` tool on `history.jsonl` for the current context, or on a specific `history_YYMMDD.jsonl` file for older messages.
Each line is a JSON record with `type`, `role`, `content`, `timestamp` fields.
Message records: `type=message`, tool calls: `type=tool_call`/`tool_result`, compacted summaries: `type=summary`.

## Temp File Rules (rant 2026-08-25T18:10:57)
- Throwaway scripts/scratch files (`.py`, `.ps1`, `.sh`, `.json` payloads) MUST be written under **`{{ session.dir_path }}/tmp/`** (create the directory if missing) — never in the project root, working directory, or `~/.emrg` root.
- Clean up: at session/round end, delete temp files that have already been executed successfully; do not leave `tmp_*.py` / `.tmp-*` clutter in the working root.
- If historical `tmp_*.py` clutter already exists in the working root, move it into `{{ session.dir_path }}/tmp/` or delete it once no longer needed.
{% endif %}

{% if config_dir %}
Expand Down
23 changes: 23 additions & 0 deletions tests/test_daemon.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -282,6 +282,29 @@ def test_system_prompt_rant_handling_section(tmp_path):
assert "/rant" in rendered


def test_system_prompt_temp_file_rules_section(tmp_path):
"""Temp File Rules section renders with the session tmp dir (rant
2026-08-25T18:10:57): throwaway scripts must go under the session
directory's tmp/ subdir, not the project root / workdir / ~/.emrg root."""
server = _make_server()
session = Session.create_with_id("tmpfile-test", tmp_path)
rendered = server._build_system_prompt(session)
assert "## Temp File Rules" in rendered
assert "tmp/" in rendered
# the rendered path must point into the session directory
assert str(session.dir_path) in rendered
assert "never in the project root" in rendered or "project root" in rendered


def test_system_prompt_temp_file_rules_absent_without_session():
"""Without a session the temp-file rules section is skipped (no path to
anchor it to), same as Session & History."""
server = _make_server()
rendered = server._build_system_prompt()
assert "## Temp File Rules" not in rendered
assert "## Session & History" not in rendered


def test_submit_rant_tool_registered():
"""submit_rant is in the daemon tool registry (available in all sessions)."""
server = _make_server()
Expand Down
30 changes: 30 additions & 0 deletions tests/test_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -1259,6 +1259,36 @@ def test_promote_template_homework_first_dehardening():
assert "mention stats" in out, "状态文件提及统计字段应渲染"


def test_promote_template_direct_cdp_rule():
"""promote_prompt.md §0.3 mandates direct CDP 127.0.0.1:57000 for all
browser ops, forbidding the remote-debugging-setup popup flow
(rant 2026-08-25T17:57:15, R49: popup blocked waiting for host Allow)."""
import jinja2

template_path = (
Path(__file__).resolve().parent.parent
/ "emrg" / "server" / "promote_prompt.md"
)
env = jinja2.Environment(undefined=jinja2.Undefined)
template = env.from_string(template_path.read_text(encoding="utf-8"))
out = template.render(
instance_id="test", host_name="host", uptime="0h 0m",
repo_url="https://github.com/x/y.git", owner="x", repo="y",
local_source="/tmp/pm", source_dir="/tmp/pm", session_id="s1",
evolution_cwd="/tmp/evo", timestamp="20260825",
task={"project": "aitokenpool"},
project={"path": "/tmp/proj", "name": "aitokenpool", "description": "d"},
evolution_count=0, git_path="git", gh_path="gh",
)
# A. direct CDP endpoint mandated (positive discrimination)
assert "127.0.0.1:57000" in out, "直连 CDP 端点应渲染"
assert "Direct CDP connection (MUST" in out, "直连 CDP MUST 规则应渲染"
assert "remote-debugging-setup" in out, "禁止的 remote-debugging-setup 应点名"
assert "FORBIDDEN" in out, "禁止词应渲染"
# B. retry-direct-not-popup behavior
assert "retry the direct connection" in out, "直连失败应重试直连而非弹窗"


def test_promote_template_registration_blog_sections():
"""promote_prompt.md §2.x host-authorized account registration + §2.y
blog publishing (rants 2026-08-15T09:04:28 / 09:06:12)."""
Expand Down
Loading