Skip to content

feat(research): add bounded Ego X discovery - #6832

Open
loopx-agent wants to merge 3 commits into
mainfrom
codex/ego-x-research-20261019
Open

loopx-agent wants to merge 3 commits into
mainfrom
codex/ego-x-research-20261019

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Goal and delivered outcome

An explicitly configured Ego reader could read a known source URL but did not expose keyword-based X discovery. Add a bounded search_x MCP operation that returns original post references and excerpts through the same browser boundary. Existing external-evidence plans, receipts, parent admission and readback remain the research owner.

为已显式配置的 Ego reader 增加有界 X 搜索:返回原帖链接、时间和摘录,沿用同一 Page、origin、控制权与清理机制;不会自动采纳证据或修改账户。

  • Outcome basis: maintainer request for reusable Ego/X discovery in external research.
  • Before → after: known-URL text/image reads only → discover creator posts by query, then inspect originals through the existing reader.
  • Intended base: loopx-project/loopx:main, baseline d6de821eb3abfd24184a849a7d0bf9ddd7ca2c6b.

Author declaration and specification

  • Written by: OpenAI Codex, GPT-6 family model agent.
  • Specification: no separate written specification; the request summarized above is the basis. Existing contracts: loopx/capabilities/external_research/README.md and docs/integrations/ego-source-reader.md at the baseline above.
Criterion Disposition Owning path Evidence
Real bounded keyword search and original-post references implemented loopx/extensions/ego_x_search.py Real MCP search plus executed DOM-extraction fixtures
Preserve origin, live control, Page isolation and shutdown implemented loopx/extensions/ego_source_reader.py Existing reader suite and process-level shared-lifecycle regression
Loading/login is not empty-search evidence implemented source-specific readiness and result validation Empty-state, loading, malformed-card and redirect cases
Reuse research authority and disclose scope implemented capability README and integration guide Native plan → execution → receipt validation → readback; admission stays separate

The provider-specific Python transport reuses the existing optional adapter; no new Core capability, planner, browser controller, setting or parallel evidence ledger is added. The source-specific extraction callback uses the existing lock and deadline. The -m entrypoint delegates to the canonical module so imported search and URL tools share one lifecycle owner.

Scope and continuation

Complete within the requested adapter scope. The operation becomes available when this optional adapter is upgraded and its idle MCP host is reloaded through the normal lifecycle. This PR does not enable a disabled reader, restart an active chat, expand an origin list or qualify an installed model's future tool selection. Disabling the existing adapter removes the new operation with it.

Scope-fit improvement: fix the module-entrypoint split revealed by adding an imported source tool; a real stdio regression failed with two auto-space creations before that fix and passes with one create/read/read/close lifecycle. No unrelated refactor.

Validation

  • Tested revision: a504f2c9200aad395388c191aa5749a200dab789.
  • Run state: finished for the checks below; CI remains separate.
  • Input classes: synthetic; authorized private read-only browser session. Raw pages, account context and runtime evidence remain private.
Check kind Result Evidence / limitation
unit / integration / regression parity passed uv run --extra test --extra ego-source-reader python -m pytest tests/test_ego_x_search.py tests/test_ego_source_reader.py -q: 241 passed
real entrypoint / real backend passed Actual stdio MCP advertised all three tools and returned bounded X post references using the configured Ego Page; a second focused search also succeeded
CLI regression passed uv run --extra test python -m pytest tests/capabilities/test_external_evidence_cli.py tests/capabilities/test_external_evidence_json_input.py -q: 33 passed
native research contract passed Existing external-evidence plan, caller receipt validation and readback accepted the source lineage; no automatic admission or ledger promotion
regression parity passed Existing URL/image reader cases remain covered; process-level lifecycle fixture fails before the canonical-module fix and passes afterward
static passed Ruff and mypy on affected provider code; diff whitespace check; staged bilingual-RFC check
semantic advisory passed Diff-scoped inventory including the new source; no supported new carriers detected, with the advisory's normal scope limits

Coverage limits: extraction tests use synthetic DOM; live checks qualify this bounded source-checkout execution, not exhaustive search, public visibility, video inspection, every X layout, an upgraded installed host or automatic Agent routing. The stdio lifecycle fixture emulates a POSIX executable; portable input/extraction/boundary cases remain separate. Source visibility and technical claims still require review before public evidence admission.

Frontend / visual evidence

  • UI impact: none. This adds a host MCP source operation used by the existing conversational research route and its native receipt/readback; it does not change dashboard, desktop or documentation chrome.
  • First viewport / preview: N/A. No private browser screenshots are published.

Type / boundary

  • Feature: optional source adapter and focused validation/documentation.
  • Explicit paths only; no credentials, local paths, account context, private source dumps or internal links.
  • No paired RFC changed; bilingual staged check passes.
  • DCO sign-off included.
  • PR proposal only; no merge, install, account write or background-search action.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@songoow songoow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; model=GPT-6 Astra Pro; provider=OpenAI; declaration_source=self_reported

结论:REQUEST_CHANGES。确认 1 个 P2:有效长帖中的 emoji 跨过摘录上限时,整次 search_x 会在 MCP 序列化阶段失败。 定位 ego_x_search.py:47;其余范围、复用与验证结论如下。

动机

已配置 Ego 阅读器的用户想按主题找作者演示时,原来的两个工具需要先有原帖地址;Agent 只能另外组织一次浏览器搜索,再把找到的地址交回阅读器。

本 PR 增加同一阅读器内的关键词搜索,正常路径可以先取得原帖链接与摘录,再在原会话内继续阅读原文;不过,有效长帖在摘录边界遇到 emoji 时,当前实现会使整次 MCP 搜索报错。

普通来源发现、原文继续阅读、范围拒绝和控制权恢复已有独立验证;完整的有界 Unicode 摘录交付尚未成立,不能把现有测试通过等同于这个新操作已经可靠可用。

本次只评审可选来源 adapter;它不自动启用浏览器、修改账户、采纳研究证据或证明模型已经选用材料,也不把合成来源测试算成真实 X、视频或已升级安装宿主的验收。

改动思路

复用已有 reader 的执行锁、权限边界和生命周期是正确位置;新文件只负责 X 页面查询和摘录,不需要在 Core 里再建搜索控制器、证据账本或授权规则。 保留这个有界 adapter 增量与 canonical-module 生命周期修复;当前 PR 内应补好 Unicode 截断和实际 MCP 回归,浏览器平台、模型采用及安装宿主的资格仍由原有验收边界承担。

源码所表达的流程是:search_x 把 query 当作 URL 数据,经原 reader 的配置、origin、控制权和 Page 锁检查,读取有限的已加载帖卡,再把链接和摘录交给调用者。研究计划、receipt 校验、父 Agent 采纳及账本读回继续走原 TypeScript owner。这张表说明精确 head 的边界,执行证据在后文:

情况 当前实现与评审结论
已配置 reader、授权 origin、正常帖卡 可以搜索,再沿同一个 auto-space 阅读原帖;独立 MCP 路径通过
未配置或越出 origin 范围 浏览器调用前拒绝;旧工具与基线完全一致
用户控制 Page、随后返回 Agent 控制 停止时不 resolve/navigate Page,恢复后原空间继续产生可用结果
加载中、登录页、不认识的帖卡 不当作空搜索成功;显式平台空状态才返回空结果
Unicode 代理对跨过 4000 上限 当前生成不完整字符,MCP 报错;这是需修改的 F1

做一个新浏览器控制器或证据缓存会扩大维护面;只保留已安装 Ego CLI 又无法提供这个可复用的 MCP 操作。当前的窄 callback 方向合理。-m 入口改为 canonical module 也有实际必要:导入的新 search 工具与原工具必须共享 Page 锁和退出清理 owner。

具体改动

本评审绑定 head a504f2c9200aad395388c191aa5749a200dab789,冻结 base d6de821eb3abfd24184a849a7d0bf9ddd7ca2c6b。五文件 +431/−4:新 X adapter 121 行,原 reader +16/−3,两份文档 +79/−1,以及 215 行新测试。没有新 Core capability id、持久 schema、CLI 参数、用户设置或前端视口。

在 diff 之前,我读取了基线的 docs/integrations/ego-source-reader.md 和 loopx/capabilities/external_research/README.md。作者没有声称另有新 RFC;这两份原契约与当前请求组成评审 frame。逐项结论:有界来源输出未满足,F1 使合法内容无法交付;既有权限与生命周期已实现并验证;研究采纳归属保留原 owner;失败与原来源退路在已测正常边界成立,Unicode 错误另列;交付资格边界保留,未升级安装宿主或验证真实 X/模型采用。

关键代码讲解

  1. loopx/extensions/ego_x_search.py:61 — search_x 接收 query、数量和 Latest 选择,检查类型与上限,再通过 urlencode 生成搜索 URL。它复用 _read,随后校验原帖 URL、author、摘录和 flags。这个层没有研究账本写入或账户操作。
  2. loopx/extensions/ego_x_search.py:20 — _script 复用 navigation 和 exact-URL fence,以帖卡自己的时间链接确定原帖,再去重并输出有限结果;排除隐藏/外域卡片和账户 chrome。危险点在 第 47 行:slice 的单位是 UTF-16 码元,可能截断一个 emoji 的一半。
  3. loopx/extensions/ego_source_reader.py:465 — _read 只增加内部可信 callback。原来的配置检查、origin、共用锁、一个总 deadline、confirmed-missing recovery、返回验证与 finally 释放保持原 owner;调用者不能提交脚本或任意 executable。
  4. loopx/extensions/ego_source_reader.py:548 — main 与 -m 委托 把三个工具放在同一个 canonical module 上注册。独立进程测试确认 search→原文→退出仅创建/关闭一个空间;临时恢复旧 main() bootstrap 的 mutation 出现两次创建、一次关闭,并留下一个合成空间,说明这项小重构修复了真实生命周期差异。

F1 — [P2] 截断时不要拆开 Unicode 代理对

当一个正常帖卡的内容是 3999 个 BMP 字符 + 🙂 + tail,max_posts=1 仍会失败。生成脚本先 text.slice(0,4000),保留的是 emoji 的单独高代理项;外层 JSON 能把它转义并通过 reader 的验证,search_x 解开 JSON 后,真实 MCP 序列化无法再把它编码成 UTF-8。

我的完整入口反例使用 python -m loopx.extensions.ego_source_reader,通过 MCP 客户端实际调用工具;子进程执行 PR 生成的 JS,来源是有效的 jsdom 帖卡。结果为 CallToolResult.isError=true,错误是 UnicodeEncodeError,没有返回原帖列表。同一份合法源文本走 read_public_url 成功;把前缀改为 3998 个 BMP 字符,相邻 search control 通过。 因此问题在新摘录边界,不是 query、origin、登录、原文本编码或浏览器环境。

最小修复:在保留现有 4000 预算和 excerpt_truncated 含义的同时避免拆开 Unicode 代理对,例如回退被切开的末尾高代理项;用真实 stdio MCP 验证有效 posts 能序列化,而不是只检查 Python helper 的字典。 无需扩大 API、提高预算或添加静默丢弃整帖的 fallback。

对主干的风险

正常实现的边界有明确正向证据。独立 MCP/jsdom 验证最终为 9 passed、1 failed(F1);覆盖正常搜索与原文继续阅读、完整旧工具结果、未配置、数量/去重/来源范围、跨域重定向先于 DOM、控制权恢复、空状态与 loading/login/malformed 区分,以及共享 owner 清理。冻结 base 使用同一脚本的两个旧工具/disabled 对照均通过;完整 text/image envelope、MCP 原工具 schema、失败诊断和恢复结果逐字段相等,未归一化或删除返回字段。image bytes 在这里是明确的传输 fixture,不能称为实际像素读取。

仓库相关 Python suite 274 passed、0 skipped(271.13s);另跑原 research TypeScript owner 17 passed。改动文件 Ruff、仓库 required Ruff、改动文件 mypy(2 个 source files)、configured mypy(17 个 source files)、编译、diff whitespace 和 bilingual guard 通过。差异范围 semantic advisory 检查两个生产源文件,没有检测到其支持的新增 carrier,保留“扫描不证明全部语义不存在”的限制。这些通过记录没有覆盖 ASCII-only 原测试遗漏的 F1。

独立运行环境为 Python 3.12.14、该 base/head 实际固定的 mcp 1.30.0、Pydantic 2.14.1、Node 24.19.0、jsdom 26.1.0。MCP server、SDK formatter、Python 子进程、生成的 JavaScript 和 DOM selector 都真实执行;Ego API、layout/readiness 与 PNG bytes 是合成后端。曾尝试隔离 Chromium,但所需二进制未就绪、下载不是有效 zip,因此没有宣称 Chromium、真实 X/Ego 页面或实际渲染已验证。F1 由 JS 字符串边界与实际 MCP formatter 复现,并不依赖一个尚未确认的 X 布局。

当前捕获的 Windows job 在 synthetic merge d3a86e65e 上失败于 long-path budget fixture 的临时目录创建:Path('/tmp').resolve() 落到不存在的 D:\tmp,在被测 CLI 调用前报 WinError 3。同基线另一 PR 的 hosted job 有相同栈与原因;本 base/head 的 workflow、smoke 与 test Git blob 相同,且该路径没有导入新 Ego adapter。这解释了故障与本 diff 的因果范围,不等于在 immutable base 上独立跑过 Windows。完整远程 CI、Windows、安装宿主 reload、真实浏览器/视频和模型采用仍未资格化;captured kernel success 也不替代这些观察。

默认关闭方面,没有新增自动启用入口或设置;文档是条件式检索建议,既有 origin/account/control 权限仍在。已经开启的宿主获得新的工具,但需要遵循原有授权的升级与 idle reload;当前评审没有对用户活跃宿主、账户或真实 Goal 执行这些动作。

我的整体评价

这项增量的目标和代码位置合理,change_proportionality=proportionate,已有 URL/image 行为与生命周期得到有意义的基线验证;保留 canonical module 是值得一起交付的同域修复。当前不需要引入更大的框架或完成无关 roadmap。

不过,合法源文本无法通过工具的最终序列化,直接违背新增操作提供可用摘录与原帖引用的承诺。problem_context=not_yet_proven 与 user_experience=not_yet_proven 在这里指这项已证实缺陷使交付条件未满足,并不是缺少作者自述。long_horizon=preserved 只说明原来源读取、有限恢复和 state owner 保留,不声称模型长期效果提高。

REQUEST_CHANGES,绑定上述 exact head。 先修复 F1 并保留实际 MCP Unicode 回归,再复核该更改后的完整 source/scope/lifecycle 路径。现有其他通过记录和未验证边界继续保留;这份评审没有 merge、安装或账户写入授权。

English verdict: REQUEST_CHANGES — Exact head a504f2c. One P2: slicing a post excerpt after 3999 BMP characters splits the following emoji and makes the real MCP response fail UTF-8 serialization, even with max_posts=1. The original URL read and adjacent 3998-character control succeed. Preserve the 4000 budget without splitting a Unicode pair, and assert usable posts through actual stdio MCP. Related Python tests: 274 passed; native research owner: 17 passed; independent MCP/DOM probes: 9 passed, 1 confirmed failure. Existing-tool base/head observations match completely, and the lifecycle mutation detects a second owner. jsdom/Ego/layout/image fixtures do not qualify live X, Chromium, model adoption or an installed host; Windows/full CI limits remain explicit. No merge is authorized.

"if(!match)continue;const source='https://x.com/'+match[1]+'/status/'+match[2];"
"if(seen.has(source))continue;seen.add(source);"
"const text=article.querySelector('[data-testid=\"tweetText\"]')?.innerText||'';"
"posts.push({url:source,author_handle:match[1],published_at:time.getAttribute('datetime'),"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 截断摘录时保留完整 Unicode 码点

当合法帖卡含 3999 个 BMP 字符后接一个 emoji 时,这里的 UTF-16 slice(0,4000) 会留下单独高代理项。完整 stdio MCP search_x(max_posts=1) 实际返回 isError=true / UnicodeEncodeError,整次搜索没有 posts;相同原文由 read_public_url 可正常返回,3998 字符的相邻 control 也通过。请在保持现有 4000 预算和 truncated 语义的同时避开拆分代理对,并让回归通过真实 MCP formatter 验证有效 posts,而不是只断言 helper 返回字典。来源是合成有效 DOM,MCP 1.30.0/Pydantic 2.14.1 实际执行;未声称真实 X 页面验收。

…6832-unicode-fix

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
@loopx-agent
loopx-agent requested a review from maxliux5 as a code owner October 18, 2026 18:40

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants