npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wolido/async-subagent-isolation

v1.14.4

Published

Fix context rot and context pollution in long AI agent sessions. Subagents run in isolated processes; the main agent stays read-only and context stays clean.

Readme

async-subagent-isolation

TypeScript Pi Package npm version License: MIT

你的 AI agent 在长会话后开始“失忆”、输出质量下降,甚至擅自修改文件?这是上下文爆炸(context explosion)、上下文腐烂(context rot)与上下文污染(context pollution)的典型症状。async-subagent-isolation 是 Pi Agent 的扩展,也是 subagent-isolation(同步版)的异步演进,用子 agent 进程隔离解决这些问题。

核心约束不变:主 agent 不能碰代码。没有 write、没有 edit、没有 bash,只有 read、grep、find、ls 四个只读工具,外加一个 subagent 委派工具;修改文件、跑命令、执行逻辑的工作全部交给子 agent。两个核心卖点由此成立。一是 skill 级的提示词隔离:每个子 agent 跑在独立的 pi 进程里,有自己的 agent 定义文件(如 coder.md)和 skill 白名单,不继承主 agent 的提示词与 skills,主 agent 的 skill 一个都进不来。二是分工模型:主 agent 只做拆分、调度与验收,coder 写代码、writer 写文档、reviewer 评审,每个子 agent 只拿自己领域的那段上下文。关键区别在异步:TUI 模式下派发后立即返回回执(Dispatched <agent>. taskId: <taskId>),子 agent 在后台运行,完成后结果以 [subagent-result] 系统通知推回对话;主 agent 不被阻塞,可以并行派发多个任务、继续做其他工作。


军团模式:把「谁能派谁」写成一张 DAG 名单

军团模式(Legion Mode)的核心只有一件事:把「谁能派谁」写进名单,让整张图长成一张有向无环图(DAG)。

  • 层级是名单的结果,不是前提。 谁在谁下面,由名单里的边决定;新增一个角色,只需在名单里加一行。
  • 上下文完全用于决策,而非控制。 主 agent 只保留「要做什么」与「结果是什么」;过程与细节留在各自子 agent 的进程里。省下的宝贵上下文,全部留给判断与决策。
  • 纪律写进结构。 管事者必须只读(决策与执行分离,没有 write/edit);名单之外的派发会被拒绝;配置写错时启动即阻断(fail-closed)。
  • DAG 不是树: 同一个 agent 可以被多行引用——一个节点、一份能力。下面范例中,executor 被两个军团共用,writer 同时被主 agent 和两个军团引用。
  • 异步与它正好互补: TUI 下派发即返回、结果以系统通知回传——主链路不阻塞;非 TUI 模式(print/json)自动降级为同步,结果在调用处直接返回——脚本场景随调随取。这套设计在多层委派的军团模式下非常好用。
  • 看得到: 整张名单的运行状况可以搬进浏览器——/subagent-web 在本机起一个只监听 127.0.0.1 的页面,实时看每个子 agent 的转录,并可按面包屑从主 agent 一路下钻到任意一层(详见下文《浏览器视图(/subagent-web)》)。

范例:一套真实配置——16 个角色、19 条派发边(executor、writer 为多父共用节点)。

详细机制见下文《派发名单制:dispatch》与《同步版 vs 异步版》。


你的 agent 是否出现了这些症状

下面五种症状都可以追溯到结构性根因,也都有结构性的解法:

| 症状 | 根因 | 本项目的解法 | |------|------|--------------| | 长会话后输出质量下降,忘记早期约定 | 上下文腐烂(又称上下文退化、上下文腐蚀):上下文随会话不断膨胀,早期信息被淹没 | 上下文切割:主 agent 只保留“要做什么”和“结果是什么”,执行痕迹留在子 agent 的进程里 | | 上下文里充斥无关的工具输出 | 上下文污染:子任务冗长的执行输出回流主 agent | 上下文隔离:子 agent 只拿到委派的那句话,看不到主 agent 的执行痕迹,只把结果送回 | | agent 擅自修改文件、执行未授权命令 | 主 agent 权限过大,write/edit/bash 全在手上 | 最小权限:主 agent 被剥夺 write/edit/bash,只剩四个只读工具加委派 | | 多个子任务互相干扰 | 缺乏进程隔离,子 agent 复用主 agent 的提示词和 skills | 进程隔离:每个子 agent 跑在独立的 pi 进程,提示词、skills、执行能力各自独立 | | 等待子任务时主 agent 被阻塞,无法并行 | 同步委派语义:每次调用都阻塞到子 agent 完成 | 异步子 agent 委派:派发即返回回执,子 agent 后台运行,结果以系统通知推回 |


为什么现有方案不够

应对上下文腐烂通常走三条路:压缩、检索、加长窗口。它们都能缓解一时,但都没有改变腐烂发生的机制。

  • /compact 类上下文压缩是事后补救。 等上下文烂了再压缩,压缩本身就在丢信息:早期约定、决策理由,往往正是后来要用的东西。压缩完上下文继续膨胀,下一次压缩接着丢。腐烂的节奏没变,只是从上一个压缩点重新计时。
  • RAG / 检索式记忆把问题换成了调参。 历史存进向量库、按需取回,思路本身没错,但“取什么、取多少、什么时候取”成了新的调参负担。取回错误的片段比不取更糟:看似相关实则无关的上下文,比干净的上下文更容易带偏主 agent 的判断。
  • 更长的上下文窗口只是把墙推远。 窗口翻倍,塞满只是时间问题;每轮对话都带着全部历史发给模型,成本随长度先涨。窗口也治不了腐烂:Chroma 的 Context Rot 研究量化了这一点,输入变长后,模型性能在窗口塞满之前就开始退化。

三条路背后是同一个默认前提:一个 agent 扛下所有上下文。前提不动,解法就只能是给这个 agent 更多:更长的窗口、更大的记忆、更多的工具。async-subagent-isolation 换掉的是前提本身:把上下文切开,每个子 agent 管自己的一段,主 agent 只保留“要做什么”和“结果是什么”。


适用人群

本项目适合以下使用者:

  • 长会话重度使用的独立开发者,想防止上下文腐烂和上下文膨胀,让主 agent 长期保持清醒
  • 并行跑多类任务的重度用户,需要子 agent 上下文隔离,避免子任务输出造成上下文污染
  • 对权限纪律有要求的团队技术负责人(tech lead),希望主 agent 保持最小权限(无 write/edit/bash),改不了文件也跑不了命令
  • 构建多 agent 系统的架构师,需要 agent 进程隔离来搭建可靠工作流
  • 追求并行吞吐的开发者,想要异步子 agent 委派,不想被同步阻塞卡住

前置条件:安装 Pi Agent

先安装 Pi Agent(需 Node.js >= 20):

curl -fsSL https://pi.dev/install.sh | sh
# 或通过 npm:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent

快速开始

1. 安装扩展

pi install npm:@wolido/async-subagent-isolation

2. 复制示例 agent 与 skill

cp examples/pi/agent/agents/*.md ~/.pi/agent/agents/
cp examples/pi/agent/master.md ~/.pi/agent/master.md
cp -r examples/pi/agent/skills/* ~/.pi/agent/skills/

3. 启动主 agent

pi --tools read,grep,find,ls,subagent \
   --no-skills \
   --append-system-prompt ~/.pi/agent/master.md \
   --skill ~/.pi/agent/skills/brainstorming/

这条命令把主 agent 限制为只读工具 + subagent 委派(剥夺 write/edit/bash),并加载主 agent 提示词和 brainstorming skill。日常使用可设 alias:

alias pp='pi --tools read,grep,find,ls,subagent --no-skills --append-system-prompt ~/.pi/agent/master.md --skill ~/.pi/agent/skills/brainstorming/'

启动后直接说需求,例如“把认证中间件重构为 async/await”,主 agent 会自动派 coder 子 agent。子 agent(coder、writer)通过 frontmatter 的 skills: 字段自动加载各自 skill,无需命令行指定;项目级 agent 放 .pi/agents/ 即可。


和常规子 agent 的区别:为什么上下文隔离比提示词隔离更彻底

很多子 agent 实现只是“在主 agent 内部开一个工具调用”:子 agent 仍复用主 agent 的提示词和 skills,主 agent 也仍保留写文件、跑命令的能力——隔离是可选的、不彻底的。

async-subagent-isolation 做的是强制且完全的隔离:

  • 进程完全隔离:每个子 agent 启动独立的 pi 进程。
  • 提示词完全隔离:子 agent 有自己的 agent 定义文件(如 coder.md),不继承主 agent 的 master.md。
  • Skills 完全隔离:主 agent 和每个子 agent 各自加载自己的 skill,互不干扰。
  • 执行能力完全隔离(最小权限):主 agent 被剥夺 write/edit/bash,只能委派,无法自己执行。
  • 独立可配置:每个 agent 单独定义自己的 tools 和 skills,精确控制它能做什么、不能做什么。

此外,子 agent 只拿到委派的那一句话、看不到主 agent 的执行痕迹(上下文隔离)。委派能力按模式区分:legacy 模式(两级配置都没有 dispatch 字段)下子 agent 不可再委派,递归深度限制为 1;名单模式下子 agent 只要自己名单非空,就可以继续嵌套派发(见「派发名单制:dispatch」)。

与彻底隔离配套的,是两条互为支撑的设计决策。

默认异步。 派发即投递任务:调用即返回回执,任务在独立进程后台运行,结果由 [subagent-result] 系统通知推回。异步是默认语义,没有可选开关;主 agent 不被阻塞,可并行派发、继续规划,用户面对的始终只有调度者一个。

排他性 skill 隔离。 子 agent 的 skill 按白名单加载:全局一律关闭,只有逐条指定的 skill 能进入它的上下文。隔离发生在进程层,不靠提示词约束:每个子 agent 是独立 pi 进程,主 agent 的 skill 一个都进不来。隔离因此成为构造事实:子 agent 只有被允许的知识,专注域精确可控。

意义:上下文切割。 主 agent 只保留“要做什么”与“结果是什么”,子 agent 冗长的执行痕迹留在自己的进程与 session 里,不回流主 agent。上下文被切成小块、各管一段,主 agent 得以长期保持清醒,规划与验收不被细节淹没。异步与隔离都是默认值,可靠的分工由结构保证,不依赖纪律。

常规子 agent 是“分工”;async-subagent-isolation 是“彻底分家”。


同步版 vs 异步版

本项目是 subagent-isolation 的异步演进,两者目标一致——把执行能力从主 agent 剥离、放进隔离的 pi 进程;区别只在委派语义:

| | 同步版(原项目) | 异步版(本项目) | |---|---|---| | 派发后 | 阻塞等待子 agent 完成 | 立即返回回执(含 taskId) | | 结果呈现 | 在工具返回值处直接内联 | 以 [subagent-result] 系统通知到达 | | 并行 | 每次调用阻塞,只能串行 | 可并行派发多个任务 | | 等待期 | 主 agent 回合被占用 | 等待期间继续其他工作 | | 结果是否阻塞主 agent 回合 | 阻塞 | 不阻塞 |

原项目继续作为同步版维护。 需要同步阻塞语义(结果就在调用处返回)用原项目;需要异步并行、后台执行、派发即返回用本项目。


直接面对调度者

同步版里,每次委派都阻塞等待,体验上你面对的是一个“智能体集群”:主 agent 派完活就沉默,等子 agent 干完才回来,中间是一段段接力执行的空白。异步版把这一点翻了过来,你的对话对象始终只有主 agent 一个。

主 agent 是调度者:理解需求、拆任务、派发、汇总结果。子 agent 是幕后工人,每个都在后台独立进程里跑,完成后用 [subagent-result] 通知把结果送回主 agent。你不直接和子 agent 对话,也不需要;查看结果用 /subagent-result,实时观察运行中的任务用 /subagent-watch,在浏览器里看实时转录用 /subagent-web,取消任务用 /subagent-cancel,中间过程交给调度者。

更关键的是派发之后的自由。任务在后台跑的时候,你可以继续和主 agent 聊天:细化需求、调整规划、商量下一步,或提出新任务。主 agent 不必干等,可以继续规划,甚至并行派发更多任务。前台对话与后台工作并行推进。

最后是结果回来再验收。子 agent 完成,通知到达,主 agent 处理并向你汇报。等待期间你可以随时查看进度 widget,但不必盯着。

一句话:同步版让你陷在“集群执行”的阻塞感里;异步版让你只面对调度者,后台工作与你自己的节奏并行。


异步工作流

这是与同步版最大的不同,也是核心使用方式(TUI 模式)。

1. 派发(subagent 工具)

主 agent 调用 subagent,为子 agent 启动独立的 pi 进程。非 TUI 模式(print/json)自动降级为同步——等待完成后直接返回结果,无通知。

2. 回执(立即返回)

TUI 模式下 subagent 立即返回派发回执,不阻塞:

Dispatched coder. taskId: 01912345-6789-7abc-8def-0123456789ab

taskId 就是 session ID,之后可复用来继续同一任务。回执不是结果——不要臆造结果。

3. 后台执行 + 进度 widget

子 agent 在后台独立进程运行。TUI 编辑器上方显示进度 widget,实时列出所有在飞任务(taskId、agent、当前阶段、耗时):

● 01912345-abcd... coder    ⚡ read...         01:23

点击底部面板中的 agent 名字即打开该任务的浏览器视图(仅 fullscreen TUI;非 TUI 仍用命令)。

4. 结果通知([subagent-result])

子 agent 完成后,结果以 [subagent-result] 系统通知推送到对话(系统消息,不是用户请求):

  • 主 agent 空闲时,通知直接触发新的对话回合,立即处理。
  • 主 agent 忙碌时,通知进入队列,在当前回合的工具调用执行完后、下一次 LLM 调用前送达(steer 投递),不等整个回合结束。
  • 无论哪种送达方式,信封标题行下都带一条固定的触发行:提醒主 agent 这是任务完成通知而非用户新指令,消化前先锚定当前主线任务与进度(信封格式详见“通知信封与卡片”一节)。

结果自动到达,无需轮询。在途任务信息由 [subagent-result] 通知信封的“在途任务”块直接提供;action="status" 已在 v1.2.0 清理移除。

5. 查看全文(/subagent-result)

通知卡片只显示摘要。用 /subagent-result <taskId> 在全屏查看器中阅读完整返回:↑↓/jk 滚动、Space/b 翻页、g/G 首尾、Enter/Esc/q 关闭。查看器底部常驻按键栏,文本逐字为 ↑↓/jk line · b/PgUp & Space/PgDn page · g/G top/bottom · Enter/Esc/q close;Home/End 同样可用(跳首/跳尾)但故意不列入按键栏,Shift+Q 与 q 一样可以关闭查看器。不带参数时(TUI 模式)弹出选择列表,列出最近 5 个已结束的任务,Enter 打开所选任务;全屏查看器与无参选择列表仅 TUI 可用,非 TUI 下直接把完整结果 console.log 到终端。

6. 实时观察(/subagent-watch)

子 agent 长时间不返回时,不必干等到结束才知道它在做什么。用 /subagent-watch <taskId> 打开全屏实时查看器,每 1 秒刷新:正文顶部是 Original task 节(主 agent 下发的任务原文),其下 Conversation log 节按 /subagent-result 同款格式展示已完成内容([assistant] 文本、→ 工具调用),正在生成的文本以 [streaming] 追加在末尾;按键习惯一致(↑↓/jk 滚动、Space/b 翻页、g/G 首尾、Enter/Esc/q 关闭),底部常驻按键栏文本逐字为 ↑↓/jk line · b/PgUp & Space/PgDn page · g/G top/bottom · Enter/Esc/q close(Home/End 可用但故意不列入按键栏,Shift+Q 同样可关闭查看器),默认锚定最新输出。实时查看器仅在 TUI 可用。

查看器只服务仍在运行的任务:传入已结束或不存在的 taskId 会被拒绝并提示改用 /subagent-result;不带参数时(TUI)弹出选择列表,只列运行中任务,非 TUI 且无参时打印用法提示 Usage: /subagent-watch <taskId> — watch a running subagent task live.。观看过程中任务结束,刷新自动停止,底部固定提示 Task finished — live updates stopped. Final result: /subagent-result <taskId>,查看器保持打开直到你手动关闭。

7. 浏览器视图(/subagent-web)

/subagent-watch 与 /subagent-result 是全屏 TUI 查看器;/subagent-web 把同一批子 agent 会话搬进浏览器。命令注册描述逐字为 Open the live subagent web view in the browser (usage: /subagent-web [taskId] [--stop] [--no-open])。

  • /subagent-web:启动本地服务并打开浏览器,落在本次会话直接派发任务的列表。
  • /subagent-web <taskId>:直接打开该任务,运行中或已结束都可以。
  • /subagent-web --stop:关闭服务。
  • /subagent-web --no-open:只启动服务并给出 URL,不拉起浏览器。

侧栏只列本次会话直接派发的任务,按最旧在上、最新在下的日志顺序排列。每行依次显示状态点、状态词、agent 名、taskId 前 8 位、耗时与任务文本。任务文本超过 200 个码点时截断。行尾是子任务聚合徽章,形如 3 children · 2 running · 1 failed;子节点尚未扫描时留空。打开页面时自动滚到最新的在跑任务;没有在跑任务就滚到最后一行;用户手动滚动后不再自动滚;深链进入时以选中行为准。顶层条目被丢弃时显示 显示最新 N / 共 M。侧栏可收起、可展开,也可拖拽调宽(最小 200px);窗口宽度小于 700px 时自动收起。

状态分六种:running、recent、pending、rejected、failed、finished。判定优先级固定:进程内注册表 running > 指针且无更晚同步收据 > 同步收据 > 完成记录 > 子会话最后条目时间戳(10 秒窗口)。异步收据只表示已派发,不作为完成信号;pending 表示尚未拿到收据或指针;rejected / failed 按最新事件胜。

转录里每次 subagent 派发调用都是可点导航项,显示形如 → open <agent> (<status>)。点击后在同页切换到该子任务的转录(#/t/<taskId>)。子任务 id 先查父会话里的收据,再查该任务的 children.jsonl 指针(agent 与任务原文精确匹配);两者都没有时显示 waiting for receipt,收据落盘后自动变为可点。深层页面顶部有面包屑 祖先 › 当前(每节可点、带状态点,当前节标记 aria-current="page");← Back 仅在 depth ≥ 2(有祖先)时显示,回直接上级,depth ≤1 不显示、Alt+← 无动作。当 #/t/<taskId> 指向不存在或不可达的任务时,页面显示「未找到该任务」并提供返回任务列表的入口(#/)。转录区右上角有吸顶的状态 pill,显示 status | duration。侧栏行支持键盘导航:↑/↓ 移动焦点、Home/End 跳首/尾行、Enter(或 Space)打开所选任务(行带拒绝/失败原因时展开/收起原因面板)、Alt+← 返回上一级;详见 ADVANCED.md 的「浏览器视图(/subagent-web)」。

取数上,顶层列表每次请求重建。在跑与刚结束的节点即时扫子节点;已完结节点只回计数(childCount / childSummary),打开时用 ?expand= 按需拉取并缓存。转录中 assistant 文本按 Markdown 渲染、工具结果按纯文本显示(保留空白与换行,不解析 Markdown 结构;反引号与 ``` 围栏仍按代码片段呈现),marked(MIT)与 highlight.js(BSD-3-Clause)两份随包文件内联进页面,页面不发起外部请求。thinking 与工具结果折叠;代码块带语法高亮;mermaid 代码块按普通代码块显示。图片按需加载(/api/image);外部图片不自动加载,显示占位与可点链接;正文内嵌的 data:image/...;base64,... 会变成 [图片已省略:…] 占位,base64 不下发。Markdown 里的原始 HTML 按文本显示;非 http(s) / # 的链接目标改写为纯文本。转录不截断:首屏用服务端尾窗口,随后客户端自动向前分片补齐到会话开头。

服务只监听 127.0.0.1,端口随机,每次启动生成随机 token 放进 URL 的 t 参数。请求按 方法 → Host → Origin → token 的顺序校验;taskId 必须落在 subagent 会话根目录内,先做词法校验再做 realpath 校验。非 TUI(print/json)模式不拉浏览器,打印一行 [subagent-web] http://127.0.0.1:<port>/?t=<token>。接口 apiVersion 与页面不一致时,顶部横幅提示刷新(⌘⇧R);取数失败时右上角以红字写明原因(健康时不显示该文本)。安全模型、生命周期、状态判定与取数细节见 ADVANCED.md 的「浏览器视图(/subagent-web)」。

完整流程一览

主 agent 派发 subagent
      │ 立即返回回执(Dispatched <agent>. taskId: <id>)
      ▼
子 agent 在后台独立进程运行(进度 widget 实时显示)
      │
      ▼
完成后推 [subagent-result] 系统通知 ──► 主 agent 空闲直接处理 / 忙碌排队
      │
      ▼
用户 /subagent-result <taskId> 查看完整返回

工具与命令面

工具(主 agent 使用)

| 工具 | 作用 | 关键约束 | |------|------|----------| | subagent | 单入口工具(action 参数);action="dispatch"(默认)异步派发任务(TUI 模式),非 TUI 自动降级同步 | 回执≠结果;结果以通知到达,勿轮询;名单模式下只能派本进程名单内的 agent(启动期名单为空时不注册 subagent 工具;运行期收紧到空则工具保留、每次派发都被拒绝),legacy 模式无名单限制 | | subagent action="cancel" | 主 agent 取消单个在途任务(两步确认:首次调用返回质询,confirm:true + 非空 reason 才执行) | 仅当任务明显错误或不再需要,勿因耗时久而取消 |

v1.2.0 提示:subagent 工具的 action="status" 已作为 cleanup 移除。在途任务信息改由 [subagent-result] 通知信封的“在途任务”块提供,不再提供主动查询入口。

命令(用户使用)

| 命令 | 作用 | |------|------| | /subagent-cancel <taskId> | 取消单个运行中的后台任务(不带参数时弹出运行中任务的交互选择列表,Enter 取消所选;交互选择器仅 TUI,非 TUI 无参时改为通知列出运行中任务) | | /subagent-cancel-all | 一键取消全部运行中的后台任务 | | /subagent-result <taskId> | 全屏查看某任务的完整返回(不带参数时弹出最近 5 个已结束任务的交互选择列表;全屏查看器与无参选择列表仅 TUI,非 TUI 下直接把完整结果 console.log 到终端) | | /subagent-watch <taskId> | 实时查看运行中任务的输出(全屏,1 秒刷新;不带参数时弹出运行中任务的交互选择列表;实时查看器仅 TUI 可用,非 TUI 且无参时打印用法提示) | | /subagent-web [taskId] [--stop] [--no-open] | 在浏览器里查看实时子 agent 会话:无参数打开本次会话直接派发任务的列表,<taskId> 直接打开该任务(运行中或已结束),--stop 关闭服务,--no-open 只启动服务并给出 URL;非 TUI 模式打印一行 [subagent-web] <url>,不拉浏览器;页面提供日志式侧栏列表、面包屑下钻、→ open <agent> (<status>) 转录导航、assistant Markdown 渲染、图片按需加载与转录自动补齐全量 | | /subagent-dispatch | 打印 subagent-isolation.json 的有效派发名单(S × C 合成交集)与校验发现;启动校验 fail-closed 时该命令仍可用,用于排查 | | /subagent-config [agent] | 唯一的交互式配置入口:agent 选择菜单直接标注每个 agent 的生效 model/thinking,可编辑 description/tools/skills/body/model & thinking 五字段(name 只读)并管理可用 model 列表(带参数直进指定 agent) |


示例 agents

仓库 examples/pi/agent/agents/ 提供三个可直接参考的 agent:

| Agent | 作用 | 可用工具 | 加载的 skill | |-------|------|----------|-------------| | coder | 写代码、改代码、跑验证 | read, write, edit, bash, grep, find, ls | systematic-debugging | | reviewer | 只读评审,输出可操作的反馈 | read, grep, find, ls | (无) | | writer | 写文档、改 README、生成 commit message | read, write, edit, grep, find, ls | writing-clearly-and-concisely |

复制到 ~/.pi/agent/agents/(用户级)或 .pi/agents/(项目级,同名时 project 覆盖 user)即可使用,可按需修改或新建。修改或新建 agent 文件后,legacy 模式运行 /reload 刷新注入主 agent 提示词的子 agent 清单(名单模式下清单每轮重建,无需 /reload;改 dispatch 字段并非一律需要 /reload:在 C 仍保留该字段时,收紧(删条目)即时生效,放宽(加条目)仅当条目已在启动快照 S 的同一行中才即时生效、否则需 /reload,见“配置管理”一节)。


为子 agent 指定模型

模型有三个配置来源:agent 文件 frontmatter 的 model: / thinking: 字段、subagent-isolation.json(用户级/项目级),以及进程内存级临时覆盖(仅当前 pi 窗口进程生效)。前两者都支持,更推荐 json:所有 agent 的模型配置集中在一个文件里,不用逐个翻 agent 文件;/subagent-config 可交互编辑并写回;json 覆盖优先于 frontmatter,json 中配置的字段会遮蔽 frontmatter 同名值,frontmatter 里的配置被遮蔽时不生效、也不易察觉。进程内存层优先级最高,适合多窗口共享同一配置文件时只在本窗口临时调整(见下)。

可用 subagent-isolation.json 为每个子 agent 单独指定模型与 thinking level(配置文件名沿用同步版,两者可共享):

{
  "$models": ["deepseek/deepseek-v4-pro", "deepseek/deepseek-v4-flash"],
  "coder": { "model": "deepseek/deepseek-v4-pro", "thinking": "high" },
  "writer": "deepseek/deepseek-v4-flash"
}

文件放在 ~/.pi/agent/subagent-isolation.json(用户级)或 .pi/subagent-isolation.json(项目级,覆盖用户级同名 key)。三种覆盖格式的完整示例见 examples/pi/agent/subagent-isolation.json。

进程内存级临时覆盖。 多个 pi 窗口共享同一份 subagent-isolation.json 时,某窗口工作过程中可以用 /subagent-config 把某个 subagent 的 model/thinking 临时写入 this process:覆盖只存在当前进程的内存里,不落盘、不写文件,进程退出或 /reload 后消失,其它窗口不受影响。优先级链为进程内存层 > 项目级 json > 用户级 json > frontmatter,整 key 遮蔽语义与文件层级一致——内存层 entry 存在时整体遮蔽低层同 key entry。进程覆盖生效时,/subagent-config 的菜单标注会在总览行尾附 [saved: ...] 片段显示配置文件里的原值(见下节)。$models 列表不受影响,仍是文件级(写入目标只有 user/project)。

顶层 $models 数组是可选的可用 model 列表($ 前缀避免与 agent 名冲突):配置 model 覆盖时从列表中选择,列表为空或未配置时回退自由输入。项目级 $models 是合法数组时整体遮蔽用户级列表,写 "$models": [] 可显式清空。无需手写 JSON:/subagent-config 提供列表管理入口(见下节)。

thinking 等级、优先级与合并规则详见 ADVANCED.md。


派发名单制:dispatch

为什么用名单(Why a roster):旧深度锁只能表达「几层」,名单能表达「A 能派 B、不能派 C」;名单里没有「层/深度」概念——层级是名单的结果、不是前提,新增角色只需在对应名单行里加名字,不用改全局配置结构。管事(中间层)必须是只读人设:决策与执行分离,能改文件的执行者不配派发权,只有只读且 tools 声明 subagent 才允许派。能力从零构建:没有名单行 = 叶子(不注册 subagent 工具);配置写错时启动即阻断、不注册工具,绝不退回更宽松的旧行为(fail-closed);未进任何名单的 agent 只提示、不报错。结构是名单(DAG)而不是树:同一 agent 可被多行引用,指向同一节点、能力只有单份,删一行只影响该行。S×C 交集的安全动机:启动快照 S 与运行时配置 C 任一来源收紧都生效——运行时读不到 dispatch 时绝不退回 legacy 放行。兼容性上,两级都没有 dispatch 字段 = 与改造前完全一致(零迁移成本)。

subagent-isolation.json 顶层的 dispatch 字段把「谁能派谁」显式写成一张名单表,形如 { "管事的": ["可派子 agent", ...] },值为字符串数组:

{
  "dispatch": {
    "main": ["coder", "reviewer"],
    "coder": ["reviewer"]
  }
}

字段语义:

  • main 是入口行:主 agent 的派发名单就是 dispatch.main。
  • 缺行 = 叶子:没有出现在任何一行左边的 agent 是叶子,不可再派。
  • 同名 agent 可被多行引用:指向的是同一节点,能力只有单份(不是复制多份)。
  • 形状规范:dispatch 必须是对象,每一行的值必须是数组,元素必须是非空白字符串。

配置查找与模型配置一致:用户级 ~/.pi/agent/subagent-isolation.json + 从 cwd 向上最近的项目级 .pi/subagent-isolation.json。但替换语义不同:项目级 dispatch 整体替换用户级(不是按 key 合并);项目级文件没有该字段则沿用用户级。

S×C 合成:S = 启动时(或 /reload)读到的快照,C = 每次派发/注入时按当前 cwd 读到的运行时配置。某个 agent(或 main)的生效名单 = 存在的来源各按该行求交集(来源存在但缺该行时按空行参与;整份配置缺席的来源不参与),顺序在 S 存在时取 S(S 不存在取 C),结果去重。故主进程里改 dispatch 字段是:在 C 仍保留该字段时,收紧(删条目)即时生效(C 每次派发/注入都重读,交集立即变小);放宽(加条目)仅当该条目已在启动快照 S 的同一行中才即时生效,否则需 /reload 才重算(S 固定于启动时)。整份字段/文件被删除或改坏、dispatch 形状非法、启动即 fail-closed、以及子进程按 PI_SUBAGENT_ALLOWED 的边界,见 ADVANCED.md 的「reload 语义」。

空名单 = fail-closed:注册门在 factory 期判定——启动期生效名单为空的进程不注册 subagent 工具,派不了任何 agent;若名单在运行期才收紧到空,工具保留但每次派发都被调用门禁拒绝。名单外的派发被拒绝,报错文案逐字为 Cannot dispatch "X". Allowed subagents: a, b.(X 为目标 agent 名,允许列表按逗号+空格分隔;名单为空时列表为空,即 Cannot dispatch "X". Allowed subagents: .)。

legacy 回退:两级都没有 dispatch 字段 = legacy 模式,行为与改造前完全一致(仍由深度锁限制)。

启动校验(阻断项 ①-⑦ 与非阻断提示)、fail-closed 后果、嵌套派发与 /subagent-dispatch 排查命令见「安全与权限纪律」一节;S×C 合成算法、子进程 PI_SUBAGENT_ALLOWED 传递等完整细节见 ADVANCED.md 的「派发名单制(dispatch)」。


配置管理:/subagent-config

TUI 模式下用 /subagent-config 统一管理子 agent 配置,全程交互,不用手动编辑文件:

  1. 选择 agent:列表逐项带来源标记 (user) / (project),并直接标注生效 model/thinking(格式 <name> (<source>) — <model> (<thinking>),未配置显示 not set;该 agent 存在进程级覆盖时行尾追加 (process) 标识并附 [saved: <model> (<source>) / <thinking> (<source>)] 片段(显示配置文件中的低层原值,槽位无值显示 not set);生效值按整 key 合并语义计算——进程内存 entry 遮蔽项目级/用户级同 key entry,项目级 entry 遮蔽用户级同 key entry,entry 内未配字段回退 frontmatter,与派发实际使用一致);末尾固定一项 Manage available model list ($models),进入可用 model 列表管理(查看当前列表及来源、添加、删除,写入目标可选用户级/项目级)。一个 agent 都没有时列表只剩该入口,$models 照常可管理。
  2. 选择字段编辑:选中 agent 后直接进入字段选择,字段选项自带当前值标注(无详情通知,信息获取靠菜单标注)。可编辑 description、tools、skills、body、model & thinking 五个字段(model 与 thinking 合并为一个编辑项,一次写入两字段);name 是只读身份标识,不在其中。

各字段的编辑方式:

| 字段 | 编辑方式 | |------|----------| | description | 单行输入,输入框预填当前值;改后需 /reload 才刷新注入清单(legacy 模式的清单启动时缓存;名单模式清单每轮重建,不需 reload) | | tools / skills | 逗号分隔输入;输入空串即从 frontmatter 移除该 key | | body | 在外部编辑器中编辑($EDITOR,未设置回退 $VISUAL,再回退 vi);取消、未改动、改完全空白都不写盘 | | model & thinking | 合并为一个编辑项:进入子流程先选动作——edit model & thinking(标注当前生效值与各自来源)/ clear model & thinking (reset to frontmatter);edit 分支依次为 model 值步($models 列表非空时从列表选择、为空时自由输入并预填生效值)→ thinking 值步(官方 7 级 + not set 选项,当前生效标 (current))→ 写入目标(this process / user / project)→ 一次写入两字段;clear 分支选写入目标后整条覆盖清除,反馈 model 与 thinking 各自回退值 |

name 是只读身份标识,不可编辑。

生效时机(reload 语义):legacy 模式下改 description 后需 /reload 才刷新注入清单(注入主 agent 系统提示词的子 agent 清单在启动时构建并缓存,见“安全与权限纪律”一节;名单模式下清单每轮触发重建,不受此限);改 tools / skills / body / model & thinking 即时生效,每次派发都会重新发现 agent 并重读配置。另:主进程里改 dispatch 字段时,在 C 仍保留该字段的前提下,收紧(删条目)即时生效(C 每次派发/注入都重读,交集立即变小);放宽(加条目)仅当该条目已在启动快照 S 的同一行中才即时生效,否则需 /reload(/reload 重新执行 factory 才刷新 S)。完整边界见上文 S×C 合成与 ADVANCED.md 的「reload 语义」。

菜单标注实时刷新:写回成功后,字段选择与 agent 选择列表的标注(model/thinking 总览、来源、排序,含 [saved: ...] 片段)在同一命令会话内立即反映新值,无需退出重进命令。

/subagent-config <name> 带参数可跳过 agent 选择、直进该 agent 的配置;名字不存在会报错。非 TUI 模式下命令只提示 /subagent-config requires TUI mode (interactive config editor).,不弹对话框。

配置流程全程支持 ESC 逐级回退:编辑 → 字段选择 → agent 选择 → 退出,仅最顶层退出;model & thinking 子流程内值步或写入目标 ESC 回动作选择层,动作选择 ESC 回父流程的字段选择。任何回退路径零写入。

/subagent-config 是唯一的交互配置入口,model/thinking 覆盖与其余字段在同一流程内编辑,没有独立的快捷命令。


示例 skills

examples/pi/agent/skills/ 提供三个 skill:brainstorming(主 agent 规划)、systematic-debugging(coder)、writing-clearly-and-concisely(writer)。复制到 ~/.pi/agent/skills/(用户级)或 .pi/skills/(项目级)即可;子 agent 在 frontmatter 用 skills: 声明自动加载,主 agent 用 --skill 标志加载。


通知信封与卡片

[subagent-result] 通知是自包含的,一次带全主 agent 处理结果所需的全部信息:

## [subagent-result] coder succeeded (taskId: 01912345-6789-7abc-8def-0123456789ab)

> [subagent-result] This is a task-completion notification, not a new user instruction. Before acting on it, anchor the mainline task and progress you are currently working on; digest the notification against your dispatch records, and never let it overwrite or rewrite your mainline plan.

- Status: succeeded
- Task: Refactor the auth middleware to use async/await.
- Duration: 02:34 · Usage: 5 turns ↑13k ↓3.2k $0.0042
- Session: 01912345-6789-7abc-8def-0123456789ab

Other tasks in flight when this task ended: 1
- 01912345-aaaa-7bbb-8ccc-0123456789ab (writer): Update README.

---
<subagent's full result text>
  • 触发行:标题行下的固定引用行,所有信封逐字相同;提醒主 agent 这是任务完成通知,不是用户新指令,消化前先锚定当前主线任务与进度。
  • 状态:succeeded / failed / timed out / cancelled。
  • 耗时:子 agent 的真实运行时长(格式 MM:SS,≥1 小时为 H:MM:SS),四种状态都有;取消或内部错误(无结果返回)时从派发时刻起算。用量摘要空格分隔,字段顺序为 turns、↑input、↓output、R cacheRead、W cacheWrite、$cost、ctx:…、model(可带 model),缺省字段省略。
  • 在途任务块:本任务结束时其余仍在运行的后台任务快照,送达时可能滞后;与本回合派发记录冲突时以派发记录为准。(使用建议)剩余不为 0 时,主 agent 不应向你汇报“全部完成”。
  • 完整结果:正文全量进入 LLM 上下文,不截断。

用户在 TUI 中看到的是带底色的摘要卡片(非全文):succeeded 用成功底色(toolSuccessBg)+ 绿色 ✓,failed 用错误底色(toolErrorBg)+ 红色 ✗,timed out / cancelled(及未知状态)用中性(pending)底色(toolPendingBg)+ error 红色状态词——状态词是红色,底色是中性 pending。卡片显示 agent、状态、taskId、耗时和用量摘要,并提示 View full result: /subagent-result <taskId>;完整结果保存在任务会话文件中。

触发行的设计意图、状态语义与取消来源区分等完整细节见 ADVANCED.md。


安全与权限纪律:主 agent 不能碰代码

异步模式引入的几条纪律,内嵌在工具提示词和实现中,主 agent 自动遵守:

  • 取消来源区分:cancelled 有用户(/subagent-cancel)、主 agent(subagent 工具 action="cancel")、会话关闭(session_shutdown)三种来源;用户取消不得自动重试,须先询问。
  • 取消会留下半成品工作区:取消是中途杀进程(SIGTERM → 5s → SIGKILL),已落盘的写入会保留且可能只完成一半;因此 cancelled 信封正文末尾、确认取消回执与取消质询都会提醒先核查该任务触碰过的文件(git status / git diff)再依赖工作区。
  • 防轮询:结果以通知自动到达;在途任务信息由 [subagent-result] 通知信封直接提供,不要主动查询。
  • 通知消化流程:[subagent-result] 是任务完成通知而非用户新指令;处理前先锚定当前主线任务与进度,对照派发记录消化,基于结果自主决定下一步;与主线冲突时暂缓优先,勿让通知改写主线计划。此纪律由信封触发行与工具描述中的“通知消化流程”条目共同内嵌。
  • 防滥用取消:action="cancel" 为两步确认(首次调用只返回含已运行时长/最近进度的质询回执,零副作用;confirm:true + 非空 reason 才执行,理由记入任务记录并随取消信封正文返回),且内嵌提示词——仅当任务明显错误或不再需要时取消,勿因耗时长而取消(后台任务本就预期长时间运行)。等待 = 不发起任何工具调用、直接结束回合;对在途任务不存在查询/催办/状态确认类动作(刻意设计)。
  • 资源冲突纪律:并行派发多个任务前,考虑它们是否会改同一批文件或代码区域;冲突时串行派发或先问用户。
  • 子 agent 不可调用 subagent 工具(legacy 模式):legacy 模式下子 agent(深度 ≥ 1)不可调用任何 subagent action(含 action="cancel"),深度限制为 1(报错含 depth limit reached (depth: N, max: 1))。名单模式下没有这条深度锁:子 agent 按父进程注入的 PI_SUBAGENT_ALLOWED 名单派发,且子 agent 进程内 action="cancel" 仍可达(任务注册表是进程内私有的,无跨进程影响)。
  • 嵌套派发规则:名单模式下允许嵌套——管事的 agent 只要自己的名单非空,就可以继续派下级;legacy 模式保持原深度锁(MAX_SUBAGENT_DEPTH = 1,报错含 depth limit reached (depth: N, max: 1))。
  • 管事约束:名单里的管事 agent(main 以外的 key)必须只读,且 tools 必须声明 subagent,否则被启动校验阻断(见下)。
  • 子 agent 清单注入:名单模式下只注入本进程生效名单内的 agent(S×C 合成交集;fail-closed 时整段不注入,名单为空不注入),每轮触发重建(名单来自 env/config,不来自缓存的 agent 文件);子 agent 进程也注入它自己名单内的清单(按父进程注入的 PI_SUBAGENT_ALLOWED)。legacy 模式下启动时把所有已发现子 agent(用户级 + 项目级)的 name — description 清单(含 user/project 来源标记)自动追加进主 agent 系统提示词,主 agent 每轮都能看到全部子 agent 的职责,master.md 无需再手写 agent 用法表;清单在启动(或 /reload)时构建并缓存:改了 agent 文件的 name / description,要 /reload 才会刷新;改 dispatch 字段并非一律需要 /reload:在 C 仍保留该字段时,收紧(删条目)即时生效,放宽(加条目)仅当条目已在启动快照 S 的同一行中才即时生效、否则需 /reload 才重算生效名单(S 快照在启动时固定;完整边界见 ADVANCED.md 的「reload 语义」)。legacy 模式子 agent 进程内不注入(子 agent 没有 subagent 工具面,注入纯属污染)。
  • 启动校验与 fail-closed:配置了 dispatch 字段时,启动即校验,阻断项:① dispatch 形状非法(非对象 / 某行不是数组 / 某元素非字符串或空白);② 缺 main 行(入口);③ 表中某个名字找不到对应 agent .md;④ 管事的 agent 不是只读人设(未声明 tools,或 tools 含 write/edit;bash 不算 write/edit);⑤ 管事的 agent 的 tools 已声明且非空、但未声明 subagent(--tools 白名单同时管辖扩展工具,缺它则无法派发;tools 缺失或为空由 ④ 覆盖);⑥ 表中存在环(含自环);⑦ 某名字从 main 沿名单边不可达。非阻断提示:发现到的 agent 不在任何名单里(仅提示)。fail-closed 后果:任一阻断项 → 启动逐行 console.warn("[async-subagent-isolation] …")、不注册 subagent 工具、系统提示词的清单注入整段跳过;/subagent-dispatch 保持可用,用于排查。
  • TUI 异步 / 非 TUI 同步降级:只在 TUI 模式走异步路径;print/json 等非 TUI 模式降级为同步阻塞。

进阶用法

手写 subagent 调用、复用 sessionId、信封与在途任务块细节、action="cancel" 取消任务、清单注入缓存、配置写回保证、环境变量等见 ADVANCED.md。

  • 活性心跳:同步派发时父进程在等待下级期间经工具进度通道上报活性 + 静默期周期心跳,防上游活动超时误杀;心跳周期 H = clamp(活动超时/3, 5s, 30s)。完整描述见 ADVANCED.md 的「超时与终止」。
  • 超时终止顺序:超时/取消杀进程一律 SIGTERM 优先——先 SIGTERM,再经 5000ms 固定宽限(源码常量 SIGTERM_GRACE_MS,不受环境变量影响)补 SIGKILL。环境变量 PI_SUBAGENT_SHUTDOWN_KILL_GRACE_MS(默认 5000,上界 24 小时)只控制 session_shutdown 时 detached 辅助进程补 SIGKILL 的宽限期。详见 ADVANCED.md 的「超时与终止」。

项目结构

  • src/index.ts — 扩展主源码(命令接线与生命周期)
  • src/web/ — /subagent-web 的本地服务与页面(server.ts 服务与路由、tail.ts 增量读取、tree.ts 会话树、tree-view.ts 侧栏视图辅助、session-source.ts 解析缓存、children.ts 派发指针、images.ts 图片按需加载、markdown.ts Markdown 渲染、page.ts 自包含页面、api-version.ts 接口版本号、vendor.ts 随包浏览器文件)
  • examples/pi/agent/ — 示例 agent 和 skill 定义(master.md、agents/、skills/)
  • package.json — npm 包清单
  • tsconfig.json — TypeScript 配置
  • README.md / README.en.md — 说明文档
  • ADVANCED.md / ADVANCED.en.md — 进阶参考
  • LICENSE — MIT 许可证

License

MIT