Repository navigation
Releases
PI 0.1.6(pi 1.3.0)· 富渲染
引擎:@earendil-works/pi-coding-agent 1.3.0(与上一包相同)。
从 v0.1.5 到这里共 99 笔提交。这一版是一个大功能:富渲染。代码块不再只是等宽文字——Mermaid、公式、JSON/XML/YAML 树、CSV、堆栈、HTML、SVG、动图都能画出来。
富渲染
聊天里的围栏代码块按内容分派到对应的渲染器,源码随时可切回来看。
Mermaid 图表。 在离屏 WebView 里跑 mermaid 12.2.0,输出 SVG 经清洗后由 AndroidSVG 画成矢量图,可 1×–8× 缩放。共享一个宿主、串行有界队列、同 cacheKey 合并请求、LRU 缓存、超时后重建,渲染进程被杀也不带崩 App。旧版的 ASCII 降级路径保留为运行时回退。
数学公式。 MathJax 真排版,显示式与行内式都走它。渲染结果 LRU 缓存、状态可保存、占位固定高度——不会因为公式落笔而跳版。
JSON / XML / YAML 树。 后台解析并缓存,整行点击展开,缩进参考线,子节点分页,深层缩进封顶保证深嵌套仍有内容宽度。展开状态可保存,不随流式重置。
CSV / TSV 表格。 RFC 4180 解析(引号、字段内换行、CRLF 全认),表头冻结、横滑、数值列右对齐、隔行淡底、单元格可复制。
堆栈跟踪。 识别常见语言格式,函数名可换行,库帧调淡,消息可展开,分页。
安全 HTML 块。 jsoup 解析 + 白名单重建:危险标签整棵子树丢弃、URL 协议校验、三层上限。流式、拒绝或超限时原样回退源码。<details> 状态可保存,<hr> 变淡,引用去掉合成斜体。
SVG。 独立围栏、HTML 里夹的 <svg>、以及裸 SVG 链接都交给 SVG 渲染器;动画 SVG(SMIL / CSS)可播放。
脚注与链接卡片。 脚注保留未引用的定义;链接渲染成卡片,长按复制,流式末段不画卡片。
围栏别名与内容嗅探。 mmd / latex / geojson / rss 等别名认了;没标语言的块按内容猜 svg / xml / mermaid / json。
公共卡片语言
所有富内容共用一套卡片:胶囊标题 + 复制图标 + 右上角 ⋮ 菜单,统一的卡片头、分隔线、折叠条、状态位。工具栏不再摊在正文里,低频动作收进 ⋮。树与 CSV 的「源码 ↔ 视图」切换也并入卡片头。
动图与媒体
- URL 动图(GIF / WebP / APNG)与动画 SVG 可播放,随生命周期暂停,限制并发
- 图片查看器支持动图播放与 SVG 打开
- 音频、视频链接渲染成播放卡片
- 远程图片带常规 UA 与 Accept,手动跟跳转(含 http→https),认 SVG 与 AVIF 并栅格化
- 图片加载失败时用共用卡片占位(破图图标 + alt + 缩短来源,点按复制地址),Markdown 与 HTML 一致
流式光标
回复生成时 ▍ 光标接在 Markdown 末尾,紧跟最后一个字,不占布局宽度。
排版重做
舒适档重做为母版 16/27,紧凑 15/25、宽松 17/28 按同一组比例推出;标题行高固定按比例走,不再随行距档时变时不变。
- 用户气泡右对齐、贴内容宽度(非经典档)——单行短句不再撑满整行
- 标题贴近正文,代码块上下对称,表格圆角与内边距统一
- 代码块边线 100% → 28% 并铺 6% 同色淡底;分割线 100% → 35%;行内代码加 14% 淡底;h1 去掉下划线(金色 + 下划线像链接)
- 思考块与引用去掉伪斜体(中文斜体是假斜体)
- 经典档全部保持原样
阅读字体瘦身
从官方 SourceHanSerifCN 可变字体(25 MB,APK 内约 15 MB)裁出 wght 450 / 600 两个静态 TTF,裁到 GB2312 + 常用符号(约 7700 字形),每个 3.0 MB,合计 6.1 MB。
- 正文 400 → 450:深色背景下宋体细笔画在 16sp 偏虚,450 更接近原来系统字体的密度
- 粗体落在真实 600
- 字体内部名改为 Pi Reading Serif(OFL 要求修改版不得保留 Reserved Font Name),版权与许可声明原样保留
- 生成脚本入库
tools/build-prose-font.py,字体加入noCompress以便平台 mmap,只有实际绘制到的字形页驻留内存
工作区认领
点开别的工作区的会话时,App 认领那个工作区,不重启引擎——以前这一下会打断正在跑的那一轮。
编辑安全
工作区里的文件编辑补上了一整套保护:
- 只读门槛、读原文、保留换行
- 冲突检测:文件被别人改过就不覆盖
- 原子写
- 草稿恢复
- 删除不跟随符号链接
模型页
- 自定义厂商可以建任意多个,按名字生成不冲突的 id
- 已配置的厂商可以再换
- 文件里已有的自建厂商也出现在选择列表里
- 订阅账号挪到厂商卡后面,并显示已登录数
- 对话页选模型按厂商分段
- 导入表单里自建厂商的名称框与 Base URL 提到 Key 前面
构建信息
| 引擎 | @earendil-works/pi-coding-agent 1.3.0(未变) |
| 变体 | sideload28(targetSdk 28,默认选这个) |
| 架构 | arm64 |
| 安装包 | 151,709,389 字节(144.7 MiB) |
| sha256 | 3fb62942643a767393151f1c1dd7380b0308fb10cdeb9ea7a356512196a269ee |
| 来源 | CI run #608,commit 506d2b70 |
同一密钥签名,可以覆盖安装,升级不丢数据。
这一版新增了 Mermaid / MathJax / AndroidSVG / jsoup / SnakeYAML 五个依赖,许可证清单已同步。
PI 0.1.5(pi 1.3.0)· 订阅登录与界面重做
引擎:@earendil-works/pi-coding-agent 1.3.0(与上一包相同,不含引擎升级)。
从 v0.1.4 到这里共 153 笔提交。这一版是新功能 + 整体界面重做:订阅登录、配色方案、悬浮输入框、模型能力编辑、思源宋体、拆底栏、排版重定标。
新功能
订阅登录:原生 OAuth,不经过终端
以前登录订阅账号要开终端敲 pi /login。现在模型与供应商屏顶部有一张「订阅账号」卡,点进去就是完整的登录流程:
- 清单不硬编码:直接取 pi 的
builtinProviders()过滤auth.oauth,pi 认几家就有几家 - 登录就是 pi 自己的登录:调同一个
pi-ai的auth.oauth.login(interaction),auth_url/device_code/progress通知直通界面 - 按 pi 的提示画界面:设备码、浏览器链接、
select/text/manual_code四种交互全支持;粘贴整条回调地址的入口一直可见 - 写盘走 pi 自己的规矩:
PiAuthStorage.setOAuth—— 锁、0600、镜像目录、等重启标记;只校验 pi 会拒绝的那几条,其余键(accountId/enterpriseUrl/scope)原样保留 - 失败会说话:
providers()加 20 秒超时,进程 stderr 留最后 40 行带进错误;读不到时用内置九家清单撑起页面并标明「运行时没响应」,可以重试
从订阅登进来的厂商不再显示「换 Key / 改模型」——Key 与模型清单都不是人填的,只剩「删除」。
配色方案 / 系统主题
pi 的 system 主题本来就是按调色板推导出全部 56 个 token 的。现在这个推导搬到了手机上,调色板由你选:
- 四档预设底色:pi 默认深底
#1E1E1E、浅底#F5F5F5、#121212、纯黑#000000;ANSI 16 色仍是 pi 的DEFAULT_ANSI,只换底色 - 自定义底色:
#RRGGBB输入 + 色相/饱和度/明度三条滑杆(HSV 直改,不走 RGB 反算,修掉色相 360° 回跳与饱和度归零丢色相) - 色彩强度三档(素 / 标准 / 鲜艳)走 pi 推导函数的
saturation参数 - 拖动只预览,按「应用」才写盘:页内用真实
deriveSystemPalette做页面 / 卡片 / 文字 / 强调四个预览块,不触发全 App 重组 - 默认主题改成
system:底色一换,整套 56 个 token 跟着重算;明暗由底色亮度决定(这是 pi 自己的规则) - 修掉「整页染蓝」——页面与卡片底色改用选中的背景色本身,不再从
userMessageBg缩放
悬浮输入框
输入框从贴着屏幕底边改成真正悬浮:
- 12 / 8 边距、26dp 圆角、中性阴影、聚焦时描边 1.5 → 2dp、运行中呼吸
- 6 行自适应高度
- 工具行 32 圆形按钮 / 20 字形,引导 / 后续回到键行
- 输入框下方不再实心铺到屏底:卡片背后实心,卡片下边缘画 24dp 的页面底色 → 透明渐隐,与上边缘对称,8dp 缝和导航条自然透出列表
- 去掉 6dp 投影——浅色下它落在下面那行卡片上,就是输入框与导航条之间那条灰带
- 关掉导航条系统遮罩:
SystemBarStyle.auto会在 API 29+ 把isNavigationBarContrastEnforced重新设成 true,覆盖 XML 的设置,浅色下系统于是在导航条上盖一层约 90% 白遮罩
模型能力:每条模型都能改
以前只有导入那一刻能设能力,之后只能看。现在导入时与导入后共用同一支表单:
- 可调:上下文 token、最大输出 token、支持图片、输入 / 输出价、缓存读 / 写价(美元 / 百万 token)、推理
- 留空 = 不写这个键,交还 pi 的默认(128000 / 16384 / 仅文本 / 0),与「填 0」严格区分
- 没碰过的开关不写:只改价格不会顺手把 reasoning 或图片能力写成 false
- 预填顺序:申报过的定义 → 引擎快照 → pi 目录(反过来会把上次改的值一打开就盖回官方值)
- 「恢复 pi 默认」= 删掉这条申报
- 写入只动
models[]里同 id 的那一条,其余模型与厂商块上的未知键按 id 合并保留
models.dev 接入:自动补思考档位与缺失能力
没有任何厂商的 /models 会回思考档位——这就是以前只有一家能思考的原因。models.dev 是 pi 目录的上游(pi 自己的 generate-models.ts 就吃它),232 家厂商、几千个模型:
- 解析
api.json→ 精确厂商/模型+ 裸模型 id两级索引 reasoning_options按 pi 自己的getEffortThinkingLevelMap逐条生成thinkingLevelMap(off/minimal/low/medium/high/xhigh/max,未支持写 null,一个都没有就不写)- 约 6 MB 缓存到本地、7 天有效;只在扫描回来的模型缺上下文时取一次,失败或断网就跳过,绝不让导入失败
- 候选的元数据来源补齐为五级:目录 → 引擎 → 本次扫描 → 跨厂商目录 → models.dev
顺带修掉一批元数据采集:中转站的 /models 通常只回 id,以前一律落到 pi 默认值,看起来像随便填的。现在按语义认五家拼法——
| 字段 | 认得的写法 |
|---|---|
| 上下文 | context_length / context_window / max_context_length(LM Studio) / max_model_len(vLLM、SGLang) / max_input_tokens / inputTokenLimit(Google) / limit.context(models.dev) |
| 最大输出 | top_provider.max_completion_tokens / max_output_tokens / max_completion_tokens / outputTokenLimit / limit.output |
| 图片 | architecture.input_modalities / capabilities.vision(Ollama、LM Studio) / modalities.input / supports_vision |
| 思考 | reasoning / supports_reasoning / supports_thinking / capabilities 里的 thinking / supported_parameters 里的 reasoning 系列 |
| 缓存价 | OpenRouter pricing.input_cache_read|input_cache_write / models.dev cost.cache_read|cache_write / LiteLLM cache_read_input_token_cost |
价格三种单位各按各的换算(OpenRouter per-token、models.dev per-million、LiteLLM per-token),厂商 per-token 价只在 PiScanVendorMeta 里乘一次到 per-million,表单与 models.json 都是 per-million。
跨厂商借定义:中转站转的 gpt-4o / claude-sonnet-4-5 / deepseek-chat 在 pi 随包目录里本来就有真数据,只是不在这一家的文件里。精确 id 与 vendor/model 两种拼法都试——但价格不跨厂商借,定价不是一回事。
- 采样参数:OpenRouter 的
default_parameters(468 个模型里 271 个有)读成 pi 的samplingParams原样写进模型定义——那是厂商给的默认值,不是替它调参 - 接回「切换到下一个模型」:切完立刻更新
meta.model并提示「已切到 <名字>(provider)」,没得循环时明说
思源宋体:聊天正文换字体
- 随包内置 Source Han Serif(OFL-1.1),最终采用官方 CN 可变字体,只保留
wght400/600 两档,SemiBold注册为Bold——**粗体**用真实 600,不再是合成加粗 - 从全量 SC 静态双字重(49.2 MB / APK +40.3 MB)瘦身到 CN 子集可变字体(25.1 MB / APK 约 +15.5 MB)
- 只作用于聊天 Markdown 正文:标题、引用、列表、表格用宋体;行内代码与代码块仍是 JetBrains Mono;界面文字仍是系统 Sans
- 列表圆点改用界面字体——思源宋体的全宽
U+2022会把列表正文整体右移 - 许可证全文、组件清单、生成器 pin 同步更新
UI 一致性门禁(CI)
新增三条规则,无 JDK、无 SDK,纯 grep / sed / awk:
- 硬失败:只有
PiSheet.kt能调ModalBottomSheet - 棘轮:按文件的
MaterialTheme.typography计数只许降不许升 - 棘轮:
screens/chat/settings的裸.dp/.sp总数对着基线只许降不许升
界面重做
导航:拆掉底栏
Scaffold不再有 bottomBar,底栏组件与图标集整套删除- 工作区 / 设置改走顶栏入口;对话页 ⋯ 菜单末尾也加了这两项
- 关闭 ✕ 换成左侧返回箭头,退回行为不变
- 返回键按条件互斥:覆盖层开着先关覆盖层,否则按设置栈逐级回退
- 底栏拆掉后底距自动变成系统导航条 inset;全程只有
Scaffold一处imePadding
弹层统一:PiSheet
新增全 App 唯一弹层外壳 PiSheet:顶部圆角 20、36×3 把手、遮罩 0x8C000000、surfaceContainerHigh 底、一次展开。聊天的六个 sheet(模型选择 / 思考等级 / 会话工具 / 上下文 / 会话统计 / 分叉选择)全部换壳,设置的三处弹层也并了进来。
顶栏
- 共享
PiTopBar:56 高、16 SemiBold 标题、48 热区 + 24 字形、圆形按压区 - 颜色从
surfaceDim改为页面底色(pi 的pageBg),修掉浅色主题下状态栏那条色块 - 发丝线与 12dp 渐隐只在滚动时出现,停下即收回
- 对话页删掉模型 chip,右侧只剩搜索 / 工作区 / ⋮,标题拿回完整会话名的宽度
排版:四档全部重新定标
- 经典档不再是「最挤最细」那一档:正文从 14/23 字重 400 改为 15/24 500,块距、列表、缩进、消息间隙、行内代码全部对齐舒适档
- 列表:紧凑/舒适/宽松项上下内边距 4/8/8 → 3/4/5,项间实际 6/8/10
- 分割线:非经典 0.75dp、上下各加 4dp,总间距 12/16/24;经典 1dp 不加
- 标题:h1–h6 额外上边距 16/12/8/4(首块、或前一块也是标题时不叠加);h1 去掉下划线,靠字号与字重分层
- 标题行高:舒适 25/30…17/22,紧凑/宽松同比例,经典保持原密度
- 行内代码:字重 600 → 500
- 表格:列宽不再固定 160,按可用宽拟合(最小 120,低于才横滑并画右缘渐隐);表头底色裁上圆角
会话页
- 搜索框默认收起,顶栏加放大镜(展开动画 + 自动聚焦),把常驻的 48dp 还给列表
- 分段控件、筛选 chip、筛后条数合成一行
- 行改两行:标题 + 相对时间 /「模型 · N条」+ 按需出现的标签
- 当前会话标识改版:描边胶囊 → 行底
selectedBg35% + 左缘 3dp 主色竖条 + 标题 SemiBold - 卡内行间线 1dp 不透明 → 0.5dp
outlineVariant - 新建按钮改用主题容器色
工作区:拆成两页
- 顶栏只留返回 + 标题 + 刷新 / ⋯;引擎状态行删除,两段常驻说明挪进 ⋯ →「关于工作区」
- 新增**「文件 / 资源」分段**
- 文件页:目录卡压成芯片、面包屑只显示最后三级(前面折成可点的省略号)、文件名改无衬线 15/Medium、图标放进 32dp 圆角底
- 资源页:技能/提示词/扩展/主题改成带计数的 chip,按来源分组,描述最多两行,行尾动作改成「复制名字」
- 图标列统一 32dp 圆角底,行间线对齐到文字列;等宽只留给路径与命令
- 修 YAML:
description的|/>/>-块标量按缩进取正文,不再把标记符当描述
模型页
- 一屏收口:导入按钮上移、厂商卡头直接显示「N 个模型 · x 可用 · y 缺凭证 · z 等待重启」
- 导入页分节、能力收成 chip、默认值说明从每行重复改为列表上方出现一次、手动模型名默认收起
- 「pi 目录里有」不再每行重复;扫描与保存改成真按钮
- 删掉**「本地模型(llama.cpp)」按钮**——它和「导入模型」是同一个 sheet,只是预选一档;手机上本来也跑不了 llama.cpp(pi 的
/llama只在 TUI 里工作) - 凭证区加「移除」:只删 Key,保留厂商块与模型
- 默认模型 / 默认厂商 / 循环模型三行收进同一张卡
- 候选列表改懒加载、一次最多画 500 行(覆盖 OpenRouter 的约 470 个端点)
设置
- 整块移除设置搜索(页面、入口、注册表 search API、测试一并删掉)
- 首页重排:高频在上、与 pi 无关的合并到底部;安全与隐私、开源许可收进「其他」
- 主题改成全屏选择、跟随系统改为列表
- 统一卡片与行组件,去掉分组标题与文字尾巴;长文本行只显示字数,值封顶 40%
系统主题调色页
- 三条色相/饱和度/明度滑杆改用 M3 Slider:游标居中、可拖、两端可达、48dp 触摸高度
- 滑杆直接改 HSV,修掉色相 360° 回跳 0、饱和度为 0 或明度为 0 时丢色相
- 页面重排:常用底色 → 三条滑杆(带数值读数)→ 色彩强度 → HEX → 应用
修复
- 触底跟随锚点:
pinToTail以viewportEndOffset - contentPadding.bottom为列表终点,不再把屏幕底边当终点;流式越过输入框安全区的情况加了反例测试 - 顶栏颜色:
surfaceDim→background,状态栏透明时顶栏与页面同面 - 订阅状态读错文件:引擎把 agentDir 绑到持久那份,
/login与订阅登录写的是镜像里的auth.json,此前只读「优先 primary」那份会看不见 oauth 条目 - 订阅登录失败信息:Node 的
fetch failed把真正原因藏在error.cause(ENOTFOUND / ECONNREFUSED / 证书),现在最多三层 cause 与 code 拼进错误信息 - 新会话被顶掉:不再被「第一个 scoped 模型」抢占默认模型
- 编辑官方模型:不再抹掉思考档位与采样参数;6 MB 的 models.dev 下载不再卡住扫描;Key 去空白
- 删除厂商/模型:立刻从清单消失,不用等引擎重启;删厂商时清掉指向它的默认与循环条目
- 两个开关点了没反应(推理设置);思考等级不再提供
off,默认显示 pi 的medium - 工作区新建:落在当前层,不再拼 rootfs 相对路径;名字输入框改 48 高并带占位与校验
- 错误卡去描边,保留错误底色与左侧色条
- 思考卡去掉右侧「展开」+ 箭头,整块仍是点按切换
- 回到底部按钮:视觉 36 / 点击 48;下箭头只在离开底部时出现,视觉底边在输入框顶边上方 12;上箭头约两屏后才出现
- 清理重构后没有调用方的东西,扫掉过期注释
构建信息
| 引擎 | @earendil-works/pi-coding-agent 1.3.0(未变) |
| 变体 | sideload28(targetSdk 28,默认选这个) |
| 架构 | arm64 |
| 安装包 | 157,701,973 字节(150.4 MiB) |
| sha256 | 5e8a4540f87ae40723f66ed650c3ddc62e1e997aa514c782d850f4617108bb83 |
| 来源 | CI run #559,commit f2875ea6 |
同一密钥签名,可以覆盖安装,升级不丢数据。
这一版 APK 比上一版大 约 15 MiB,是内置的思源宋体 CN 可变字体。
PI 0.1.4(pi 1.3.0)· 设备桥 v2
引擎:@earendil-works/pi-coding-agent 1.3.0(与上一包相同,不含引擎升级)。
从 v0.1.3(313309f)到这里共 17 笔提交。这一版的主线是设备桥重做——工具面从 26 个砍到 7 个,四组能力被删掉,动作开始自带反馈。
设备桥 v2:26 个工具 → 7 个
原来那份工具面的问题很具体:26 个工具的描述合计 1.2 万字(≈4k token/轮),而其中大半是同一个能力的别名——ui / ui_dump / tap / swipe / input / key / screenshot 七个工具讲的是同一块屏幕,io / fs / files / files_list / download 五个讲的是同一批文件。
现在是一个工具一个闭集 action:
| 工具 | action |
|---|---|
android_status |
(无,常驻) |
android_screen |
look / tap / type / swipe / key / wait / shot / steps |
android_app |
list / launch / stop |
android_files |
dirs / list / read / write / download / import / export |
android_shell |
(无) |
android_comm |
clipboard / notify / toast / vibrate / share / open / tts |
android_device |
battery / torch / location / sensors / sensor |
省下来的正是每轮都要付的那份钱:以前每个别名都各带一份 description + promptSnippet + promptGuidelines + 完整参数 schema,现在一个端点只付一次。合并丢掉的参数精校由每个 action 自己的 [BAD_PARAM] 补回来。
结果是提示词大幅变短,而能力一点没少。
动作即反馈:点一下就告诉你变了没有
这是这一版真正的加强点。以前「点一下输入框再打字」要三次工具调用(tap → dump → input),模型在两次调用之间看不到任何反馈,点错了也不知道。
现在 tap / type / swipe / key 执行后立刻做一次界面 diff,同一份结果里给出:
{
"changed": true,
"screen": "com.tencent.mm",
"added": ["发送"], "removed": [], "updated": ["输入框"],
"text": "1 个节点出现:发送"
}- 界面真变了 → 列出新增/消失/变化的节点;
- 界面没变 →
changed: false,并明说「界面没变,可能没点中」,模型可以直接重试,而不是接着往下错; - 没有上一屏可比 →
changed: null,如实说「还没有上一屏可对比」,不编一个结论。
steps:一次调用连做八步
{ "action": "steps", "steps": [
{ "action": "tap", "text": "搜索框" },
{ "action": "type", "text": "天气" },
{ "action": "key", "key": "enter" }
] }每一步先校验 action 再动手,逐步带自己的 diff,遇错即停并说清停在第几步。上限 8 步。这样一段确定的操作序列不用再来回四次。
删掉四组能力
VPN / 输入法 / 投屏 / 自动化规则 —— 整组删除,连同它们的服务类、清单项、提示词段落、技能正文与文档条目一起清干净:
PiVpnService.kt、PiInputMethodService.kt、PiCaptureService.kt、PiScreenCapture.kt、PiAutomation.kt全部移除pi_input_method.xml移除- 最后两处
allows/android_say残留清掉
升级后这四项不可用,是设计决定,不是回归。
设备能力屏:1303 行 → 148 行
用户原话是「说明文字太多了」。现在收成四个状态点 + 三行开关 + 一个高级折叠,删掉 allows 字段。整个文件的净变化是 −1147 行。
权限闸门
danger.ts的级别表对齐 7 个工具- 「按调用才危险」的两处改由新的
approvalRuleFor判:android_shell的 7 条不可逆命令,以及android_app(action="stop") - 记住的粒度是规则 id,不是工具名
ShellPolicyMirrorCheck的两条集合判据仍成立(工具名 7 = 7,硬黑名单两侧都是空)
模型可见文字统一英文
- description 每条 ≤60 字符,只写「是什么 + action 列表」
- promptSnippet ≤20 字符,不与 description 重复
- promptGuidelines 只留 3 条安全规则(
stop会丢未保存内容、shell受策略守卫、不可逆动作会二次确认) - 工具返回的正文仍是中文——那是给工具卡上的人看的
技能按能力组裁剪
SKILL.md(23 行)+ references/ui.md + references/env.md,正文合计 52 行。只开 basic 时不写 ui.md、索引里也没有屏幕那一行;全关时不写并删目录。上一版写过、这一版不再有的附件在写新文件前主动 rm——pi 只按目录扫盘。
diff 卡:折叠态对齐工具卡
- 删掉折叠态的第二行(「新增 N 行 · 删除 M 行」+ 一个不可点的"展开/收起"死标签)——前者把头行的
+N/−M用中文复述一遍,零新信息 - 去掉描边:工具卡早就不带描边了,diff 卡留着就成了并排时的唯一异类
- 圆角 10 → 8(复用
TOOL_CARD_SHAPE),内边距改为工具卡同一套(横 10 / 纵 7),工具名加粗与头行一致
折叠态现在只有一行:工具名 + 路径 + +N + −M + chevron。
文档与 CI
- 新增
docs/device-bridge-v2.md:v2 工具面与屏幕重构的设计契约(工具表、动作即反馈的返回形状、技能裁剪规则) - 工具表、技能表格、包列表、模型提示里的旧工具名全部同步到 v2 的 7 个
- CI 触发收紧:纯 markdown / 截图 / 标志图改动不再触发完整构建(两个 targetSdk 变体 + ~65 MB 运行时装配)
构建信息
| 引擎 | @earendil-works/pi-coding-agent 1.3.0(未变) |
| 变体 | sideload28(targetSdk 28,默认选这个) |
| 架构 | arm64 |
| 安装包 | 142,126,674 字节(135.5 MiB) |
| sha256 | 281a16dfe897487ff4ff185556599b08fad6e3e6a217373f7452a8fa289773d4 |
| 来源 | CI run #427,commit 005dea49 |
同一密钥签名,可以覆盖安装,升级不丢数据。
注意:这一版删除了 VPN / 输入法 / 投屏 / 自动化规则四组能力,升级后不可恢复(回退需装回旧包)。
PI 0.1.3(pi 1.3.0)
引擎:@earendil-works/pi-coding-agent 1.3.0(与上一包相同,不含引擎升级)。
从 v0.1.2(93945ea)到这里共 7 笔提交,其中 1 笔是撤销。下面是这一版的全部内容。
对话页:收起卡片的位置与抖动(这一版主线)
用户报的三层现象,逐层修到位:
- 收起来人不在原地 —— 展开/收起此前根本没有锚点。整个对话页只有贴底 pin、窗口恢复、搜索跳转三处程序化滚动,展开/收起一处都没有;而
LazyColumn的锚点只是「第几个 item + 行内像素」,这一行自己变矮以后那个偏移就被当成别的位置用了。现在收起时记住你点开它时那一行的顶端位置(y),收回后把卡还回那个 y,而不是屏幕最顶端。 - 收完原地抖一下 —— 卡片一矮,下面的行被拉进视口,而没量过的行首帧高度是 0(画一个空
Box,解析落地才长出来)。于是"先空一帧再长出来"。改成同步解析的闩不再放过这一帧。 - 展开状态会丢 —— 滚出窗口再回来,卡片不再自己收起来;展开状态按行 key 记住,不随 composition 消失。
中间那一笔「把卡还回到屏幕最顶端」(
23f6301) 已被下一笔撤销,最终落地的是「还回你点它时的位置」。
设置 · 模型:只有三个厂商(bug)
- 根因:
officialCatalog()读的是硬编码的嵌套路径…/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data,而 pi 1.3.0 的依赖树里@earendil-works/*全部提升到顶层,那层嵌套node_modules根本不存在 → 目录读空 → 导入 sheet 里只剩三个厂商,搜也搜不出来。 - 顺带重做了这一屏:选择器与导入流程按「更少文字、更少步骤」重排,默认模型可以直接点选,不再只能自己填。
设置 · 系统提示词:框上沿写了不存在的键
- 那两个输入框都在用、不是重复:上面那个整份替换 pi 的
SYSTEM.md,下面那个追加APPEND_SYSTEM.md。 - 过时的是框上沿那行小字——显示的是旧键名
app.runtime.systemPrompt/app.runtime.appendSystemPrompt,那是它们还作为"App 设置项"时的身份,现在已经改成 pi 的官方文件。改掉。
设置:砍掉没用的一整组
- 删掉「终端与 Shell」整组(
shellPath/shellCommandPrefix/npmCommand/ 终端页字号 / 终端键栏 五行)——手机上没有第二个 shell 可选,填错只会让每次 bash 命令失败;另两项只服务已叫停的终端页。 - 说明文字砍到一句:设置项下面那一堆意义不大的注解收掉。
- 重启卡只在真的需要时出现——此前啥也没干也让你重启;现在只在你改过需要重启的配置之后才出现。
构建信息
| 引擎 | @earendil-works/pi-coding-agent 1.3.0(未变) |
| 变体 | sideload28(targetSdk 28,默认选这个) |
| 架构 | arm64 |
| 安装包 | 142,237,718 字节(135.6 MiB) |
| sha256 | ed079273e338e0a8b687184f3bb775b9ac32110a16718827ae1d471883dba82e |
| 来源 | CI run #417,commit 313309f5 |
与上一版同一个密钥签名,可以覆盖安装,升级不丢数据。
PI 0.1.2(pi 1.3.0)
引擎:pi-coding-agent 1.0.1 → 1.3.0(Node 24.19 不变)。
从上一个包 v0.1.1 到这里共 130 笔提交。下面是这一版新增、修复与加强的全部内容,按面分组。
引擎与运行时
- 引擎跨三个小版本:1.0.1 → 1.0.3 → 1.1.0 → 1.2.0 → 1.3.0,App 侧零代码改动接入
- 引擎依赖树自己锁起来,钉住 1.3.0,构建不再受上游漂移影响
- 官方厂商的连接字段改成「没意见就不写」——不再往配置里塞多余字段
- provider id 列表跟上 pi 的改名(
azure-openai-responses→azure) - 覆盖解压不再把软链写到自己目标上(修真机上的
ELOOP死循环) - 升级不再必须重建运行时:覆盖解压前先清掉「占了目录位置」的旧节点
- 覆盖解压去掉了那条空操作的 agent 目录迁移
设备能力(这一版改动最大的一块)
- 设备 Shell 全放开:白名单取消,守卫只剩一个空黑名单
- 闸门改为按不可逆命令审批,工具收敛为 6 个常驻
- 审批补 3 项:
pm/cmd uninstall与android_admin(wipe) - 接线第二轮能力:输入法 / 管理员 / 通知 / 自动化 / 网络 / 投屏 + 无障碍可靠性
- 五组能力改为默认全关——要用自己去「设置 → 设备能力」开
- 取消
deferred与tool_search依赖,全部工具由能力开关控制 - 提示词里 16 个「已被合并入口转交」的细粒度工具改为 hidden,提示词降 49%
- 工具耗时改用 pi 1.1.0 记录的执行时间
- ADB 那句给出明确触发条件,不再是含糊的 "when needed"
修掉的设备侧 bug
- 输入法:切到 PI 后打不了字而且回不去 —— 加了一颗「切换输入法」,警告缩成一句
- VPN 停不掉;投屏入口被误报为不存在
- 广播的端点清单少了 11 个 —— 补上,并加一道检查让它不能再漂
- 全链路审查:补上 2 个死端点,消掉 1 处能力/门禁错配
- 把波 1 的可靠性函数接出来(此前根本够不到)
- 诊断端点被自己要诊断的前置挡住;「激活设备管理器」的按钮指向一个已不存在的页面
- 全关时技能仍在显示、环境段仍被注入 —— 两处「关了没关干净」
- 删除「管理员」能力组时留下的两处语法残骸 —— 闸门因此早已加载不了
- 审批弹窗两个真 bug:「记住」的作用域,以及超时被说成拒绝
- 拦住「超时后盲目重试」这个坏指令,拦截消息说清是哪个操作
scroll降级、apps枚举取并集、无障碍被系统关掉时提醒- 扩展里一个反引号让扩展加载不了;「已失效机制」的残留说法清干净
- 去掉每次启动都弹的「屏幕」能力失效提醒
外观与交互
- 排版/行距/字号三行取代原来的字号微调与消息密度 —— 四档预设 + 一次性读时迁移
- 紧凑/舒适/宽松重新定标,舒适与宽松不再松垮;按第三版修订校正预设数值
- 转录的消息级间距 —— 换人时更大,同一条消息内部更小(舒适档 24 → 20)
- 表格:字号跟排版档、表头一层极淡底、横滑时右缘渐隐;单元格不再无限折行
- 段间距不再加在文档开头 —— 这是用户气泡文字偏下的根因
- 启动图标换成用户定的 pi 字标(矢量化);启动页不要徽章、单色层 = 图标本身、通知栏用图标本身
- 用户气泡时间戳移出气泡;错误卡只改外形;扩展三张卡对齐 24dp 模块
- 工具卡:折叠态两行并一行、删掉执行轨道与节点、卡片变矮变宽(缩进 11dp、内边距纵 7、页脚条件化、读数并进头行),有图保留专属排版
- 工具卡折叠态和官方不一致的地方对齐官方(折叠态冻结)
- 思考行改版:颜色条随展开延伸到正文末行、改定宽格(消掉流式时整行每秒重测导致的整屏乱跳)
修掉的外观/交互 bug
- 工具卡展开/收起乱跳这一串(根因排查后一次修到位):
- 跟随的 pin 只由「内容真的变了」的帧付账
- 收起卡片时钉住读者点的那一行,缺口用正好差额的底部空白兜底
- 连着收起两张卡时不再丢上一次的底部空白(band 记的是总缺口,不是本次位移)
- 收起后被推开的内容不再原地空一帧
- 展开/收起不再把视口抢到列表底部;收回后整条内容跳到另外半屏
- 展开过的卡片滚出窗口再回来不再自己收起来
- 展开/折叠的点击与拖动同等对待
- 被拒/失败的
grep、find、ls整卡消失(回归修复) EditBlock页脚的 remember key 收窄得过头- codemode 的实时调用清单根本到不了卡片;脚本与调用行接上高亮与分段上色
- 插话/排队按 pi 一比一:去掉乐观回显、判据用引擎事实、用词统一到「引导 / 后续」
- 日期分隔行自己给下沿留白(新模型下唯一漏的一行)
- 思考耗时改定宽格;run 中间行不再每次绘制都构造一个
Path(性能)
会话与工作区
- 导出的 JSONL 改成「会话文件本身,逐字节复制」 —— 一次导出同时给 HTML 与会话文件
- 带参数的
/export <名字>回到 pi 的分派(一比一):后缀决定写哪一种、只写那一份 - 排队消息不再冒充已发出的消息,并补上官方式的待发区
- 工作区文件菜单补上「用其他应用打开」与「导出到 Download」
- 修掉导出改成流式时写错的一个扩展函数(那一版 APK 编不过)
设置与提示词
- 提示词改走 pi 的官方文件;紧急停止收拢到一处
- 只读读数覆盖全部四份提示词文件,形状与用词沿用资源读数
- pi 的路径列表落到只读行的证据块里,值不再被截断
- 运行时占用按已分配块算(不再虚高)
- 修掉一个让上一版 CI 判红的类型可见性问题
本机可跑的检查(不插设备)
- 纯逻辑 harness 清单被多删的 224 行放回来
- 钉住「收起这一拍跟随不该掉」以及下一个 token 会收回末端
tool-state的断言改成对契约断言,不再钉调用写法(CI 曾FAIL X3)- date-separator 的 harness 补上覆盖面断言
顺带
- 文档全面对齐当前实现:过期说法按代码改对,统一「引导 / 后续」用词,能力面账本核到当前基线
- 删掉三个没有生产调用者的旧函数(连带钉着它们的 6 条断言)
- 修 5 个编译错误(一直没人验过,CI 挂了两次)
构建信息
| 引擎 | @earendil-works/pi-coding-agent 1.3.0 |
| 变体 | sideload28(targetSdk 28,默认选这个) |
| 架构 | arm64 |
| 安装包 | 142,202,286 字节(135.6 MiB) |
| sha256 | ec39288a9415caf6f45a14f2cf88c9619600ea780c6b308f7b55b7ad06f1c4b4 |
| 来源 | CI run #410,commit 93945ea |
与上一版同一个密钥签名,可以覆盖安装,升级不丢数据(升级也不再必须重建运行时)。
PI 0.1.1(pi 1.0.1)
引擎:pi-coding-agent 0.99.2 → 1.0.1(Node 24.19 不变)。下面是从上一个包 v0.1.0 到这一版的全部 80 笔提交,按类型分组,原始标题一字未改。
新功能(6)
- render — 裸 HTML 照 pi 原样排字(块级走 custom 新分支,行内走 annotator 认领钩子)
- render — 保行通道落地(候选 B,只在含显示式网格的消息里打开)
- render — 移植 pi 的竖直排版(renderLayout + 环境),118 条块级目标逐字节断言
- pi — 设备工具按能力注册 —— 关掉的能力一个字都不进提示词
- workspace — 图片自己渲染,渲染不了的交系统;修掉图片查看器里被背景层吃掉的手势
- render — markdown 图片按视口采样、表格解禁一行限制、http(s) 真的加载
修复的 bug(36)
- session — 分叉的用词与那条缺了的提示(落另一个代理交回的两处 diff)
- chat — 分叉与分支按上游分开 —— 会话列表改说「来自谁」,树行仍说「分叉」
- session — 重启时同一个 id 有两份就不猜 —— 改用
--session <path> - session — 列表不再把「劈成两半的对话」折成一行 —— 只有一份的字节是另一份的前缀才合并
- session — 一次引擎重启劈开一段对话 ——
--session-id用同一个 id 再建一个文件 - workspace — 提示条被底栏盖住 —— 它挂在没吃 contentPadding 的那个 Box 上
- composer — 发出去的字又回到输入框 —— 失焦那次回调把清空前的文本写回了草稿
- image — URL 画图全链路 —— 裸链接也出图、表格缩略图按格宽解码、清掉两个会伤到它的数字
- session — 点叉就必须退出 —— 「覆盖层开着」不再住在可以重放的状态里
- session — 点叉退出又被拉回会话树 —— 关掉覆盖层必须作废排队中的导航请求
- runtime — 客机时区指到了 Android 那份 zoneinfo,短名静默退化成 UTC
- session — 退出来又被拉回会话树 —— 「请求级」的状态不许比请求活得久
- viewer — 放大不清晰是三层叠加 —— 块预算比屏幕小、倍率本来就越过源 1:1、进 App 前已被压小
- runtime — 目录也纳入载荷所有权 —— prune 在同一趟里删掉旧载荷的空目录
- runtime — 收尾失败不许静默,也不许照样记账
- render — 网格写回源文本时前导缩进被 markdown 当成代码块,公式被切成两半
- render — 正文图片根本没有点按 —— URL 图渲染出来了,却没有任何手势通向查看器
- viewer — 放大后拖动只走了手指的 1/scale —— 手势位移按倍率换算回屏幕像素
- render — 网格的等宽段落不再对库的类型
copy—— CI 红在 Unresolved reference 'copy' - build — 两个提交把 main 弄红了 —— 缺一个实参、以及注释里的
/* - bridge — 生产者超预算时也把结论写进 lastFailure,别让它挂着别处的旧原因
- session — 会话树的「读取会话树 ↔ 就绪」来回刷、以及退出来又被拉回树
- viewer — 放大之后按源图坐标重解可见的一块 —— 8× 看到的是细节,不再是小图拉大
- render —
$的两条前缀判定按真实 prose 语料对齐,收尾那条保留并说明理由 - attachment — images.autoResize 关掉之后 App 照压 —— 接上这个开关的消费者
- render — 数学补两种定界符与 5 类解析缺口,表/行为改由 pi 夹具钉住
- attachment — 截图类源被压成有损 JPEG —— 候选序改成与 pi 一致的 PNG 优先
- viewer — 双击直接放到最大(1× ⇄ 8×),不再有第三个档位
- attachment — 竖拍照片被当成横图送出去 —— 先按 EXIF 摆正,再算尺寸
- composer — 命令面板塞进来的
/后面光标停在 0,再打字落在斜杠前面 - bridge — KDoc 里的「星号斜杠星号」提前关掉注释,两个 APK job 全挂
- workspace — 能看的地方就能改 —— 去掉刚加的那层"只读层"特例
- workspace — 认得 guest 的两种拼法;工作区屏改成能往上走的文件管理器
- ui — 「显示时间戳」真的管到气泡上的时间了,并且默认关
- boot — 「已是最新」不再占屏 —— 没有解包可做的启动不再闪一次启动页
- runtime — 覆盖解压前先清掉"占了目录位置"的旧节点 —— 升级不再必须重建
性能(7)
- render — 数学预处理每轮重扫串尾 ⇒ 平方;改成两个单调迭代器
- session — 打开会话的两处重复劳动 —— 为量一个长度序列化整棵 JSON、逐字符切行
- render — 行内 HTML 的认领钩子只在源文本含
<时才装 —— 其余消息代价归零 - session — 会话列表索引落盘 + 有界并行扫描,冷启动那一次不再逐行解析几百 MB
- bridge — 正文远端图片的请求策略 —— 进程级去重、45 s 负缓存、取字节并发上限
- render — 表格单元格不再无限折行,mermaid 滚回来不再重问 guest
- export — 保存会话导出不再绕一遍 base64 —— 少两份峰值内存
撤销 / 回退(2)
- image — 裸 URL 不再改写成图 ——
![]()才是图,链接就是链接 - blocks — 通用工具卡标题回到原样,去掉 MCP 的 server/tool 改写
检查与不变量(本机可跑)(6)
- pre-spawn — 三条源码不变量跟上
restartEngine的「两种钉法」 - session — replay-cost 的两条源码不变量跟上「argv 的两种钉法」
- runtime — 钉住"目录也在载荷所有权里"
- runtime — 钉住「收尾失败不许静默」的四处形状
- session-replay-cost — §3b 那一列量的是整份 readAll,列名别写成 entries
- session — 补一条断言 ——
-c(mostRecentForResume)走索引时也必须给出同一行
文档(15)
--session-id/--session的现状与列表合并规则(三处过期说法 + 一处登记)- session — TerminalScreen 的注释不再提已经删掉的
PiOverlay.Terminal - test — P19 与 fetch-runtime.mjs 必须成对改
- runtime — 耐久目录的保护来自 DurablePreserve,不是"在易失树之外"
- render — §A2 记下 NBSP 缩进保护(上一提交的实现)
- render — 行内 HTML 钩子的注释别再引用会漂的行号
- render — §A2 记下保行通道的实测结论 —— 候选 A 走不通,B 的作用域可收紧
- render — 收录渲染能力盘点(只读审计,424 行)
- check — EXIF 夹具条数在注释里写错(28 → 26)
- render — §A2 里的夹具条数 31 → 34
- attachment — AttachmentBudget 的四处过期引用(含 0.86.1 → 1.0.1)
- render — manifest 已开明文,改掉两处"没有 usesCleartextTraffic"的过期话
- 1.0.0 之后清一遍文档里"对当前版本已经不成立"的说法
- readme — 装上改为指向正式 release
- readme — 重排——能扩展成什么样提到最前,装上/架构/升级挪到底部
其他维护(8)
- session — 剩下三处注释不再提已删除的 setOverlayView/PiOverlay/overlayIndex
- session — 删掉没用的尾巴 setOverlayView,以及两处还提着旧名字的注释
- checks — 注册
guest-timezone—— 钉住客机的时钟来源 - checks — 注册
markdown-image-tap—— 正文图片点开查看器那条接线 - html — 生成器按惯例搬进
tools/,并把htmlharness 注册进 CI - checks —
sessions与session-identity两个闭包加进新文件PiSessionIndex.kt - pi — 钉住 1.0.1,并把引擎那棵依赖树自己锁起来
- pi — 钉住 pi 1.0.0,并修掉 node.tgz 的一个静默构建缺陷
PI 0.1.0(pi 1.0.0)
引擎 pi 1.0.0(上游 2026-10-01),App 版本 0.1.0。
下载哪个
| 文件 | targetSdk | 说明 |
|---|---|---|
pi-android-0.1.0-sideload28-arm64.apk |
28 | 默认选这个。 用 Android 10 以前那套沙箱规则,系统权限最少,行为最可预期 |
pi-android-0.1.0-modern36-arm64.apk |
36 | 面向新系统行为变更的版本,需要真机验证;只在 28 那个装不上或行为异常时才试 |
两个包用同一套密钥签名,可以互相覆盖安装,升级不丢数据。要求 Android 8.0 (API 26) 以上,arm64。
首次启动
会解包约 117 MiB 的运行时(APK 本身约 136 MB,大部分就是这部分),比之后每次启动慢。
之后只在载荷真的变化时才重新解包。/root/.pi/agent(会话、凭证、settings)与工作区不在载荷里,升级保留。
校验
0847f020c2688f7cedb0c0a7871d8e886555e7d352628e9eaaaa0e3f64dfff63 pi-android-0.1.0-sideload28-arm64.apk
a15133ade5bead70a1ace0638e48b704702eb724b8b9602cc2420f9044549509 pi-android-0.1.0-modern36-arm64.apk
这一版有什么
引擎从 pi 0.87.1 一路到 1.0.0(中途 0.99.0 是一次性大版本:codemode、MCP、虚拟模型、主题系统重做)。
1.0.0 这一步对 App 几乎没提要求:RPC 线、内置主题色值、会话格式、模型目录格式、工具结果文本、
设置键集合、依赖集全部零变化。逐条判据在仓库 tools/fetch-runtime.mjs 的 PI_VERSION 上方那段
审计结论;tools/pi-contract.mjs 是 CI 的 pi contract job,每次对着钉住的引擎重新核一遍。
App 侧这一轮还修了三个:
- 升级不再必须「重建运行时」。 覆盖解压时如果某个路径在新载荷里必须是目录、旧树里却是文件或
断掉的软链,mkdirs()会静默失败、写文件报ENOENT,重试永远停在同一处,只有重建能过(代价是
删掉你装在客体里的东西)。现在解压器会先把这类节点清掉,再写。装上这个包之后,那一次升级应该
直接过,不用再点重建。 - 没有解包可做的启动不再闪一块启动页。 以前「运行时已是最新」也会占满一屏,现在只有真要解包
或出错时才出现。 - 「显示时间戳」真的管事了,且默认关。 以前它只管日期分隔,气泡右下角的时间是无条件画的,
所以关了看着像没生效。现在它同时管气泡时间、通知行时间和日期分隔,默认关。