diff --git a/README.md b/README.md
index 4eb5a2c..0aa0d1e 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,14 @@ cp mykey_template.py mykey.py
python launch.pyw
```
+**Also runs on Android** — tested successfully on Termux with `python agentmain.py` (CLI frontend):
+
+```bash
+# In Termux
+cd /sdcard/ga
+python agentmain.py
+```
+
Once running, tell the agent: *"Execute web setup SOP to unlock browser tools"* — it handles the rest. See [WELCOME_NEW_USER.md](WELCOME_NEW_USER.md) for the full bootstrap sequence.
## vs. Alternatives
@@ -193,6 +201,14 @@ cp mykey_template.py mykey.py
python launch.pyw
```
+**同样可在 Android 上运行** — 已在 Termux 上测试通过,通过 `python agentmain.py`(CLI 前端)启动:
+
+```bash
+# 在 Termux 中
+cd /sdcard/ga
+python agentmain.py
+```
+
启动后告诉 Agent:"执行 web setup SOP 解锁浏览器工具"——剩下的它自己搞定。完整引导流程见 [WELCOME_NEW_USER.md](WELCOME_NEW_USER.md)。
## 对比
diff --git a/agentmain.py b/agentmain.py
index 7cf07bd..6a3d3ef 100644
--- a/agentmain.py
+++ b/agentmain.py
@@ -80,7 +80,8 @@ class GeneraticAgent:
handler = GenericAgentHandler(None, self.history, './temp')
if self.handler and self.handler.key_info:
handler.key_info = self.handler.key_info
- handler.key_info += '\n如你确信任务已经改变,请先更新工作记忆去除无用部分\n'
+ if '如你确信任务已经改变' not in handler.key_info:
+ handler.key_info += '\n如你确信任务已经改变,请先更新工作记忆去除无用部分\n'
self.handler = handler
self.llmclient.backend = self.llmclient.backends[self.llm_no]
gen = agent_runner_loop(self.llmclient, sys_prompt, raw_query,
@@ -93,8 +94,9 @@ class GeneraticAgent:
if len(full_resp) - last_pos > 50:
display_queue.put({'next': full_resp[last_pos:] if self.inc_out else full_resp, 'source': source})
last_pos = len(full_resp)
+ if self.inc_out and last_pos < len(full_resp): display_queue.put({'next': full_resp[last_pos:], 'source': source})
if '' in full_resp: full_resp = full_resp.replace('', '\n\n')
- if '' in full_resp: full_resp = re.sub(r'\s*(.*?)\s*', r'\n````\n\n\1\n\n````', full_resp, flags=re.DOTALL)
+ if '' in full_resp: full_resp = re.sub(r'\s*(.*?)\s*', r'\n````\n\n\1\n\n````', full_resp, flags=re.DOTALL)
display_queue.put({'done': full_resp, 'source': source})
self.history = handler.history_info
except Exception as e:
@@ -139,21 +141,25 @@ if __name__ == '__main__':
open('./temp/scheduler.log', 'a', encoding='utf-8').write(f'[{datetime.now():%m-%d %H:%M}] {tag}\n{item["done"]}\n\n')
while True:
now = datetime.now()
- for f in os.listdir('./tasks/pending'):
+ for f in os.listdir('./sche_tasks/pending'):
m = re.match(r'(\d{4}-\d{2}-\d{2})_(\d{4})_', f)
if m and now >= datetime.strptime(f'{m[1]} {m[2]}', '%Y-%m-%d %H%M'):
- raw = open(f'./tasks/pending/{f}', encoding='utf-8').read()
- dq = agent.put_task(f'按scheduled_task_sop执行任务文件 ../tasks/pending/{f}(立刻移到running)\n内容:\n{raw}', source='scheduler')
+ raw = open(f'./sche_tasks/pending/{f}', encoding='utf-8').read()
+ dq = agent.put_task(f'按scheduled_task_sop执行任务文件 ../sche_tasks/pending/{f}(立刻移到running)\n内容:\n{raw}', source='scheduler')
threading.Thread(target=drain, args=(dq, f), daemon=True).start()
break
time.sleep(55 + random.random() * 10)
else:
agent.inc_out = True
while True:
- q = input('> ').strip()
- if not q: continue
- dq = agent.put_task(q, source='user')
- while True:
- item = dq.get()
- if 'next' in item: print(item['next'], end='', flush=True)
- if 'done' in item: print(); break
\ No newline at end of file
+ try:
+ q = input('> ').strip()
+ if not q: continue
+ dq = agent.put_task(q, source='user')
+ while True:
+ item = dq.get()
+ if 'next' in item: print(item['next'], end='', flush=True)
+ if 'done' in item: print(); break
+ except KeyboardInterrupt:
+ agent.abort()
+ print('\n[Interrupted]')
\ No newline at end of file
diff --git a/assets/insight_fixed_structure.txt b/assets/insight_fixed_structure.txt
index 8e9b47c..f2efc13 100644
--- a/assets/insight_fixed_structure.txt
+++ b/assets/insight_fixed_structure.txt
@@ -6,4 +6,4 @@ Insight是索引,L2/L3变更时同步Insight。写记忆前先读META-SOP(L0)
2. 决策前查记忆库;未查证不断言。
3. 分步执行逐步验证;3次失败请求干预。
4. 密钥文件仅引用,不读取/移动。
-5. 写任何记忆前读META-SOP核验。
+5. 写任何记忆前读META-SOP核验,memory下文件只能patch修改(除非新建)。
diff --git a/memory/adb_ui.py b/memory/adb_ui.py
index fd83dbb..b2a5fc1 100644
--- a/memory/adb_ui.py
+++ b/memory/adb_ui.py
@@ -13,8 +13,7 @@ def _dump_u2():
import uiautomator2 as u2
d = u2.connect()
xml_str = d.dump_hierarchy()
- if xml_str and len(xml_str) > 100:
- return xml_str
+ if xml_str and len(xml_str) > 100: return xml_str
except Exception as e:
print(f"[u2 fallback] {e}")
return None
@@ -24,9 +23,7 @@ def _dump_native():
subprocess.run([ADB, "shell", "rm", "-f", "/sdcard/ui.xml"], capture_output=True)
r = subprocess.run([ADB, "shell", "uiautomator", "dump", "--compressed", "/sdcard/ui.xml"],
capture_output=True, text=True, timeout=15)
- if "dumped" not in r.stdout.lower() and "dumped" not in r.stderr.lower():
- print(f"dump failed: {r.stdout}{r.stderr}")
- return None
+ if "dumped" not in r.stdout.lower() and "dumped" not in r.stderr.lower(): print(f"dump failed: {r.stdout}{r.stderr}"); return None
subprocess.run([ADB, "pull", "/sdcard/ui.xml", LOCAL_XML], capture_output=True, timeout=10)
with open(LOCAL_XML, "r", encoding="utf-8") as f:
return f.read()
@@ -36,6 +33,8 @@ def _parse_xml(xml_str, keyword=None, clickable_only=False, raw=False):
root = ET.fromstring(xml_str)
nodes = []
for n in root.iter("node"):
+ pkg = n.get("package", "")
+ if "termux" in pkg.lower(): continue
text = n.get("text", "")
desc = n.get("content-desc", "")
bounds = n.get("bounds", "")
@@ -43,20 +42,17 @@ def _parse_xml(xml_str, keyword=None, clickable_only=False, raw=False):
cls = n.get("class", "").split(".")[-1]
rid = n.get("resource-id", "")
label = text or desc
- if not label and not raw:
- continue
- if clickable_only and not click:
- continue
- if keyword and keyword.lower() not in (label or "").lower():
- continue
+ if not label and not raw: continue
+ if clickable_only and not click: continue
+ if keyword and keyword.lower() not in label.lower(): continue
cx, cy = 0, 0
if bounds:
m = re.findall(r'\[(\d+),(\d+)\]', bounds)
if len(m) == 2:
cx = (int(m[0][0]) + int(m[1][0])) // 2
cy = (int(m[0][1]) + int(m[1][1])) // 2
- nodes.append({"label": label, "click": click, "bounds": bounds,
- "cx": cx, "cy": cy, "class": cls, "id": rid})
+ nodes.append({"text": text or desc, "click": click,
+ "bounds": bounds, "cx": cx, "cy": cy, "class": cls, "id": rid})
return nodes
def ui(keyword=None, clickable_only=False, raw=False):
@@ -66,15 +62,13 @@ def ui(keyword=None, clickable_only=False, raw=False):
raw: 返回原始节点列表而非打印
"""
xml_str = _dump_u2() or _dump_native()
- if not xml_str:
- print("dump failed (both u2 and native)")
- return []
+ if not xml_str: print("dump failed (both u2 and native)"); return []
nodes = _parse_xml(xml_str, keyword, clickable_only, raw)
if not raw:
for n in nodes:
flag = "Y" if n["click"] else " "
coord = f"({n['cx']},{n['cy']})" if n['cx'] else ""
- print(f"[{flag}] {n['label']} {coord} {n['bounds']}")
+ print(f"[{flag}] {n['text']} {coord} {n['bounds']}")
print(f"\ntotal: {len(nodes)} nodes")
return nodes
diff --git a/memory/autonomous_operation_sop.md b/memory/autonomous_operation_sop.md
index c7261d8..1b17f23 100644
--- a/memory/autonomous_operation_sop.md
+++ b/memory/autonomous_operation_sop.md
@@ -3,7 +3,7 @@
授权你进行自主行动,只要不对环境造成副作用都可进行。
请先选择核心目标,再选择一个小目标进行。最终探测结果形成报告(含操作申请),待用户回来确认后再进行可能的写入或修改操作。
-> **启动时写入工作记忆**:`自主探索|≤15回合|只有cwd内可写|用户不在(问题存报告)|收尾:更新history.txt+重读本SOP检查遗漏|产出=报告+记忆提案`
+> **启动时写入工作记忆**:`自主探索|≤15回合|只有cwd内可写|用户不在(问题存报告)|收尾:重读本SOP检查遗漏+更新history|产出=报告+记忆提案`
## 🎯 核心目标(按价值优先级排序)
@@ -44,7 +44,8 @@
### 阶段 1:自主探测(用户离开时)
- **启动检查**:
- 读取可能有的 `./autonomous_reports/history.txt` 了解历史记录。
- - **不连续选择相同方向**。
+ - **TODO优先**:若cwd下存在 `TODO.txt`,优先从中选择任务执行;任务完成后从TODO.txt中移除对应条目。
+ - **不连续选择相同方向**(TODO任务除外)。
- **预期收益声明**:选定任务后,必须先用一句话写明「做这个任务预期带来什么收益」。允许探索失败,但必须事先想清楚为什么值得做。这句话写入报告开头。
- **执行方式**:基于目标自由进行,无需预先批准,直接执行只读或实验性操作。
- **约束**:小步快跑,每次只做一个小任务(剩下的下次再做),控制在15个回合以内。严禁修改核心记忆/系统设置;严禁读取敏感数据(但可以检测存在性)。
diff --git a/memory/scheduled_task_sop.md b/memory/scheduled_task_sop.md
index d2f7526..a2396cc 100644
--- a/memory/scheduled_task_sop.md
+++ b/memory/scheduled_task_sop.md
@@ -1,13 +1,13 @@
# 定时任务 SOP
-目录:`../tasks/{pending,running,done}/`
+目录:`../sche_tasks/{pending,running,done}/`
文件名:`YYYY-MM-DD_HHMM_描述.md`,内容含prompt和schedule
## 流程
-1. [AUTO]唤醒 → `datetime.now()`取当前时间,`ls ../tasks/pending/`,文件名时间≤当前→到期,选择一个
+1. [AUTO]唤醒 → `datetime.now()`取当前时间,`ls ../sche_tasks/pending/`,文件名时间≤当前→到期,选择一个
2. **立即rename到running/**(先占再读,防多进程重复领)
3. 读文件执行
4. 完成→移到done/,**在文件内追加执行报告**供用户查阅
5. schedule非once→算下次时间,新建文件到pending/
-注意tasks目录在../,即你的code root下
+注意sche_tasks目录在../,即你的code root下
diff --git a/memory/tmwebdriver_sop.md b/memory/tmwebdriver_sop.md
index b90aa94..5e24fac 100644
--- a/memory/tmwebdriver_sop.md
+++ b/memory/tmwebdriver_sop.md
@@ -40,3 +40,17 @@
- "访问"链接:遍历所有`a`找`textContent.includes('访问')`的href
- 缩略图base64:结果中`img[src^="data:image"]`可直接提取保存
- 下载大图时注意JS返回的src可能被截断,用`return img.src`获取完整URL
+
+## Chrome下载PDF(fetch+blob方案)
+- 场景:页面上的PDF链接点击后会在浏览器内预览而非下载
+- 方案:用JS `fetch(url)` 获取blob → `URL.createObjectURL(blob)` → 创建隐藏``标签设`download`属性 → 触发click → 自动下载到~/Downloads
+- 模板:
+ ```js
+ fetch('PDF_URL').then(r=>r.blob()).then(b=>{
+ const a=document.createElement('a');
+ a.href=URL.createObjectURL(b);
+ a.download='filename.pdf';
+ a.click();
+ });
+ ```
+- 注意:需同源或CORS允许;跨域时可能需要先导航到目标域再执行
diff --git a/sche_tasks/done/2026-02-15_0800_检查未读邮件_已读.md b/sche_tasks/done/2026-02-15_0800_检查未读邮件_已读.md
new file mode 100644
index 0000000..7e9b7d1
--- /dev/null
+++ b/sche_tasks/done/2026-02-15_0800_检查未读邮件_已读.md
@@ -0,0 +1,36 @@
+schedule: daily 0800
+prompt: 按ezgmail_sop读取所有未读邮件,判断是否有重要信息(紧急/截止日期/需回复的),将摘要写入执行报告。全部设为已读。
+
+---
+执行时间: 2026-02-15 08:01:23
+
+
+---
+## 执行报告
+
+## 未读邮件检查报告
+
+**检查时间**: 2026-02-15 08:02:11
+**未读邮件总数**: 3
+
+### 重要邮件 (2封)
+
+- **624215 是你的 X 验证码**
+ 发件人: X
+ 日期: Sat, 14 Feb 2026 16:34:06 +0000
+ 备注: X平台验证码(624215),如正在登录请使用
+
+- **CACTER邮件安全海外接收隔离区邮件通知目录摘要/CACTER Safe overseas reception quarantine email notification abstract**
+ 发件人: emailgateway@cacter.com
+ 日期: Fri, 13 Feb 2026 18:06:12 +0800 (CST)
+ 备注: 邮件安全隔离区通知,可能有邮件被隔离
+
+### 普通邮件 (1封)
+
+- COLLABORATION OPPORTUNITY FOR RELIABLE SUPPLY OF PHARMACEUTICAL RAW MATERIALS.
+ 发件人: Karen Lucille Hale
+ 备注: 可疑商业合作邮件,疑似垃圾邮件
+
+
+**执行状态**: 成功
+**完成时间**: 2026-02-15 08:02:24
diff --git a/sche_tasks/done/2026-02-16_0800_检查未读邮件_已读.md b/sche_tasks/done/2026-02-16_0800_检查未读邮件_已读.md
new file mode 100644
index 0000000..36fc9ed
--- /dev/null
+++ b/sche_tasks/done/2026-02-16_0800_检查未读邮件_已读.md
@@ -0,0 +1,33 @@
+schedule: daily 0800
+prompt: 按ezgmail_sop读取所有未读邮件,判断是否有重要信息(紧急/截止日期/需回复的),将摘要写入执行报告。全部设为已读。
+
+
+---
+
+# 邮件检查报告 - 2026-02-16 08:01
+
+## 摘要
+- 总计未读邮件:2 封
+- 重要邮件:1 封
+- 普通邮件:1 封
+
+## 重要邮件详情
+
+### 1. ARR January 2026 会议通知
+- **发件人**: ARR - January 2026
+- **主题**: [ARR January] Author-reviewer discussion phase starts now
+- **日期**: 2026-02-15 23:40
+- **重要性**: 高 - 有时间限制
+- **说明**: 学术会议作者-审稿人讨论阶段已开始,需要及时查看审稿意见并准备回复
+- **建议**: 尽快登录 OpenReview 查看审稿意见并准备回复
+
+## 普通邮件
+
+### 1. Academia.edu 引用通知
+- **发件人**: Academia Mentions
+- **主题**: Are you the "JiaQing Liang" cited in Artificial Intelligence papers?
+- **说明**: 营销邮件,通知有论文引用了相关姓名
+
+## 操作记录
+- 所有邮件已标记为已读
+- 执行时间: 2026-02-16 08:01:18
diff --git a/sche_tasks/done/2026-02-17_0800_检查未读邮件_已读.md b/sche_tasks/done/2026-02-17_0800_检查未读邮件_已读.md
new file mode 100644
index 0000000..0b59ed7
--- /dev/null
+++ b/sche_tasks/done/2026-02-17_0800_检查未读邮件_已读.md
@@ -0,0 +1,33 @@
+# 邮件检查执行报告
+
+**执行时间**: 2026-02-17 08:01:14
+**任务**: 检查未读邮件
+
+## 摘要
+- 共检查 3 封未读邮件
+- 重要邮件: 0 封
+- 所有邮件已标记为已读
+
+## 邮件详情
+
+### [1] 普通
+- **发件人**: do-not-reply@iclr.cc
+- **主题**: ICLR 2026 Update Poster Presentation Details
+- **日期**: Mon, 16 Feb 2026 22:47:44 +0000 (UTC)
+
+### [2] 普通
+- **发件人**: ARR - January 2026
+- **主题**: [ARR - January 2026] An author commented on your submission. Paper Number: 4724, Paper Title: "CultureScope: A Dimensional Lens for Probing Cultural Understanding in LLMs"
+- **日期**: Mon, 16 Feb 2026 15:02:09 +0000 (UTC)
+
+### [3] 普通
+- **发件人**: ARR - January 2026
+- **主题**: [ARR - January 2026] An author commented on your submission. Paper Number: 4724, Paper Title: "CultureScope: A Dimensional Lens for Probing Cultural Understanding in LLMs"
+- **日期**: Mon, 16 Feb 2026 14:43:11 +0000 (UTC)
+
+## 结论
+本次检查未发现需要紧急处理的邮件。
+- ICLR 2026 会议通知:海报展示细节更新
+- ARR论文评审:作者评论通知(2封)
+
+所有邮件已标记为已读。
diff --git a/sche_tasks/done/2026-02-18_0800_检查未读邮件_report.md b/sche_tasks/done/2026-02-18_0800_检查未读邮件_report.md
new file mode 100644
index 0000000..33e1d05
--- /dev/null
+++ b/sche_tasks/done/2026-02-18_0800_检查未读邮件_report.md
@@ -0,0 +1,82 @@
+# 邮件检查执行报告
+
+**执行时间**: 2026-02-18 08:00:54
+**任务**: 检查未读邮件 (daily 0800)
+
+## 执行结果
+
+- **总未读邮件数**: 13 封
+- **重要邮件数**: 8 封
+- **处理状态**: 所有邮件已标记为已读 ✓
+
+## 重要邮件摘要
+
+### 1. [ARR - January 2026] Official Review posted to your assigned Paper number: 2021, Paper title: "Sparse-RL: Breaking the Memory Wall in LLM Reinforcement Learning via Stable Sparse Rollouts"
+
+- **发件人**: ARR - January 2026
+- **日期**: Tue, 17 Feb 2026 23:45:24 +0000 (UTC)
+- **摘要**: 'A submission to ARR - January 2026, for which you are an official area chair, has received a re
+
+### 2. [ARR January/ACL 2026] pending review for submission 5119 please reply!
+
+- **发件人**: acl2026
+- **日期**: Tue, 17 Feb 2026 22:09:57 +0000
+- **摘要**: Dear Jiaqing and Nanyun You are the AC and SAC for paper 5119, which still has a missing review. The
+
+### 3. [ARR January/ACL 2026] pending review for submission 4966 please reply!
+
+- **发件人**: acl2026
+- **日期**: Tue, 17 Feb 2026 22:04:02 +0000
+- **摘要**: Dear Jiaqing and Nanyun You are the AC and SAC for paper 4966, which still has a missing review. The
+
+### 4. [ICML 2026] clarification regarding emergency reviewing
+
+- **发件人**: OpenReview
+- **日期**: Tue, 17 Feb 2026 21:40:32 +0000 (UTC)
+- **摘要**: Dear Jiaqing Liang, Thanks again for agreeing to act as an emergency reviewer for ICML during the pe
+
+### 5. [ARR - January 2026] An author commented on your submission. Paper Number: 215, Paper Title: "What Makes an Ideal Quote? Recommending “Unexpected yet Rational” Quotations via Novelty"
+
+- **发件人**: ARR - January 2026
+- **日期**: Tue, 17 Feb 2026 09:29:39 +0000 (UTC)
+- **摘要**: An author commented on your submission. Paper number: 215 Paper title: What Makes an Ideal Quote? Re
+
+### 6. [ARR - January 2026] An author commented on your submission. Paper Number: 215, Paper Title: "What Makes an Ideal Quote? Recommending “Unexpected yet Rational” Quotations via Novelty"
+
+- **发件人**: ARR - January 2026
+- **日期**: Tue, 17 Feb 2026 09:28:56 +0000 (UTC)
+- **摘要**: An author commented on your submission. Paper number: 215 Paper title: What Makes an Ideal Quote? Re
+
+### 7. [ARR - January 2026] An author commented on your submission. Paper Number: 215, Paper Title: "What Makes an Ideal Quote? Recommending “Unexpected yet Rational” Quotations via Novelty"
+
+- **发件人**: ARR - January 2026
+- **日期**: Tue, 17 Feb 2026 09:26:03 +0000 (UTC)
+- **摘要**: An author commented on your submission. Paper number: 215 Paper title: What Makes an Ideal Quote? Re
+
+### 8. [ARR - January 2026] An author commented on your submission. Paper Number: 215, Paper Title: "What Makes an Ideal Quote? Recommending “Unexpected yet Rational” Quotations via Novelty"
+
+- **发件人**: ARR - January 2026
+- **日期**: Tue, 17 Feb 2026 09:23:23 +0000 (UTC)
+- **摘要**: An author commented on your submission. Paper number: 215 Paper title: What Makes an Ideal Quote? Re
+
+## 重要信息分析
+
+本次检查发现以下需要关注的邮件:
+
+1. **ARR/ACL 2026 审稿相关** (5封)
+ - Paper 2021 收到新的 Official Review
+ - Paper 5119 和 4966 有 pending review 需要回复
+ - Paper 215 收到4条作者评论
+
+2. **ICML 2026 紧急审稿** (1封)
+ - 关于 emergency reviewing 的说明
+
+**建议行动**:
+- 优先处理 Paper 5119 和 4966 的 pending review 回复请求
+- 查看 Paper 215 的作者评论并回复
+- 查看 Paper 2021 的新 review
+
+## 执行状态
+
+✓ 任务执行成功
+✓ 所有未读邮件已标记为已读
diff --git a/sche_tasks/pending/2026-02-19_0800_检查未读邮件.md b/sche_tasks/pending/2026-02-19_0800_检查未读邮件.md
new file mode 100644
index 0000000..fd6c24d
--- /dev/null
+++ b/sche_tasks/pending/2026-02-19_0800_检查未读邮件.md
@@ -0,0 +1,2 @@
+schedule: daily 0800
+prompt: 按ezgmail_sop读取所有未读邮件,判断是否有重要信息(紧急/截止日期/需回复的),将摘要写入执行报告。全部设为已读。