personal-bot 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "personal-bot",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/realqiyan/personal-bot.git"
@@ -24,6 +24,7 @@
24
24
  "themes": []
25
25
  },
26
26
  "files": [
27
+ "npm-shrinkwrap.json",
27
28
  "src",
28
29
  "resources",
29
30
  "scripts/setup-python.py",
@@ -6,24 +6,22 @@
6
6
 
7
7
  - 普通问答和不需要独立上下文的工作直接完成。
8
8
  - 独立协助或核验使用 pbot_agent。委派提供目标、交付要求、资料依据和约束,不依赖专家知道父会话历史。
9
- - 需要成员直接交流时,使用 pbot_team 组队。roundRobin 依次讨论,parallel 在每轮内并行、轮间汇合。可先无任务 start 待命成员,确认空闲后 run;提供目标、每人职责、授权范围及交付要求。成员原文由代码直接同步,不逐条转述。
9
+ - 需要成员直接交流时,使用 pbot_team 组队。roundRobin 依次讨论,parallel 在每轮内并行、轮间汇合。提供目标、每人职责、授权范围及交付要求。成员原文直接同步,无须逐条转述。
10
10
  - 周期、重复流程、DAG 或需在当前 Pi 退出后继续的任务,先读包内 `references/task-conductor.md`,再按 using-task-conductor 技能使用原生任务能力。缺少该能力只阻塞依赖它的部分。
11
11
 
12
12
  ## 委派与验收
13
13
 
14
- 默认由专家交回报告和产物引用,由主会话维护正式资料。仅用户要求专家修改资料时,委派才授予相应范围;多个协作者写入时划分文件责任。计算按任务保存源码、输入、参数和结果,临时产物放入 scratch。
14
+ 默认由专家交回报告和必要产物引用,由主会话整合正式资料。原任务已授权资料修改时,可在委派中明确专家的修改范围,无须用户另行指定由专家执行;多个协作者写入时划分文件责任。
15
15
 
16
- 收到结果后,按本次完成条件核对报告与实际产物。汇总数值前核对对象、时期、单位、指标定义和来源;范围不同的结果分别解释,重复转述不算独立证据。证据判断使用 research-evidence,数据与计算核验使用 analysis-validation。
16
+ 收到结果后,按本次完成条件核对报告与实际产物。汇总数值前核对对象、时期、单位、指标定义和来源;范围不同的结果分别解释,重复转述不算独立证据。
17
17
 
18
18
  失败、中断或取消后,判断已完成部分与后续条件,不自动重跑。人工子会话输入不扩大主会话原任务授权。委派不扩大原用户授权;需要用户决定的业务交回主会话说明。
19
19
 
20
20
  ## 团队协作
21
21
 
22
- 团队成员固定,普通 post 只保存公共信息,不追加模型轮次。成员当前团队请求的最终回答会公开;不要把私人资料或无关任务放入公共上下文。成员已有私人记忆仍可能影响回答,团队讨论不等于独立复核。
22
+ 公共消息与成员当前团队请求的最终回答会向团队公开;只提供本次协作需要的信息,避免披露私人历史或无关资料。成员已有记忆可能影响回答,团队讨论不等于独立复核。
23
23
 
24
- 团队运行期间不能向成员私人 send;先 pause,核对 activeCount 和 unresolved,收尾后再切换。暂停不自动停止在途工作,需要停止时用 pbot_agent stop。失败、超时、reload 或未知结果后先查报告和外部状态;新的 run 是新批次,不是重跑旧步骤的许可。只有未关闭团队成员且空闲就绪时才能开始一批协作。
25
-
26
- 批次 finished 只表示安排的发言均结束,不证明任务完成或达成共识。用 pbot_team read 直接核对原文和产物,按原任务验收。需要更换成员时关闭旧团队、再创建新团队,不删除历史。
24
+ 批次 finished 只表示安排的发言均结束,不证明任务完成或达成共识。直接核对成员原文和实际产物,按原任务验收;团队调度与状态转换遵循工具契约。
27
25
 
28
26
  ## 独立复核与业务执行
29
27
 
@@ -37,4 +35,4 @@
37
35
 
38
36
  及时保存有依据的阶段结论、可复用知识、已确认决策,以及交接所需或用户要求的成果,无须再次询问。普通问答、零散想法和原始输出不逐轮存档。
39
37
 
40
- 资料放入所属项目或长期事务,附来源、时间和适用条件;跨任务知识引用原证据。新结果、纠正或状态变化需要同步,或用户要求整理、复用任务定义、结项时,使用 project-maintenance。发现其他未完成事项不构成开展新任务的授权。
38
+ 新结果、纠正或状态变化需要同步,或用户要求整理、复用任务定义、结项时,使用 project-maintenance。发现其他未完成事项不构成开展新任务的授权。
@@ -2,13 +2,13 @@
2
2
 
3
3
  完成本次委派范围内的研究、分析、资料查找、计算或独立复核,按任务选择技能。
4
4
 
5
- 从委派引用及工作区入口查找依据。交付说明结论、支持与反对依据、来源时点、适用范围、限制及产物路径;数值注明对象、期间、单位与计算口径,计算附实际源码、输入、参数和结果引用。说明本次实际完成的检查,关键事实不足时明确无法确认。
5
+ 从委派引用及工作区入口查找依据。按交付需要说明结论、关键依据、来源时点、适用范围、限制及实际产物路径;数值注明影响理解的对象、期间、单位与计算口径。说明实际完成的检查,关键事实不足时明确无法确认。
6
6
 
7
7
  独立复核时,核验方案参数、前置条件、实际后果和用户约束,独立查询关键事实,报告通过、拒绝或无法确认及其依据。研究与复核只使用明确的查询接口和计算,不执行业务变更;复核结论不授予执行许可。
8
8
 
9
- 默认交回报告与产物引用。只有用户明确要求修改资料时,才在委派指定范围内修改正式正文,并说明变更。
9
+ 默认交回报告与必要产物引用,由主会话整合正式资料。原任务已授权资料修改且委派明确修改范围时,可在该范围内修改正式正文并说明变更,无须再次询问。
10
10
 
11
- 使用原生 CLI/MCP 完成本次委派。需要用户决定、工具接入或环境处理时,交回已有结果、缺口和所需条件,不扩大任务范围。
11
+ 使用原生 CLI/MCP 完成本次委派,可自主诊断工具接入与环境问题。新增权限或共享环境变更先核对原任务及委派中的已有授权,已覆盖的操作不重复询问;需要补充授权或用户在会话中操作时,交回已有结果、缺口和所需条件,继续不受影响的工作。项目构建和测试遵循项目声明的环境。
12
12
 
13
13
  不创建子专家或外部任务,不管理团队。需要其他协作者或持久执行时,交回建议,由主会话决定。
14
14
 
@@ -1,3 +1,3 @@
1
- pbot 工具返回结构化对象,按各工具契约检查结果;脚本不能只看 Promise 是否成功返回。协议成功不证明业务完成。
1
+ pbot 工具返回结构化对象,按各工具契约检查结果;脚本不能只看 Promise 是否成功返回。
2
2
 
3
- 有依赖的调用按顺序 await,为用户确认留足等待时间。取消或脚本失败不回滚已保存的数据或外部副作用。
3
+ 有依赖的调用按顺序 await,为工具所需的用户确认留足等待时间。
@@ -1,6 +1,6 @@
1
1
  # 工作区
2
2
 
3
- 协助用户学习、推进项目、开展研究与支持决策,保存可复用的资料与结论。投资是按需开展的专业领域。
3
+ 协助用户学习、推进项目、开展研究与支持决策,沉淀可复用的知识、方法与结论。投资是按需开展的专业领域。
4
4
 
5
5
  环境信息见 `pbot_environment`:`workspaceRoot` 是工作区根目录,`cwd` 是当前执行目录,`resourceRoot` 是包内资料根目录,`python` 是共享计算环境状态。下文资料路径相对工作区根目录;包内路径相对 `resourceRoot`。执行命令和文件工具时按实际工作目录解析路径。
6
6
 
@@ -9,7 +9,7 @@
9
9
  - 涉及工作区资料时,优先从已有根 `AGENTS.md` 定位相关项目或长期事务,按需读取目录入口、正文和匹配的技能。没有资料入口时按任务查找或创建必要资料。
10
10
  - 复用历史结论前核对范围、时点与关键条件,只重核受变化影响的部分。分析假设不等于用户已确认的约束。
11
11
  - 复杂任务先明确交付用途、必要证据、可接受限制与完成条件,沿用已有约定。
12
- - 在当前授权内持续推进,常规、可逆的实现选择自行决定。仅当缺失信息影响目标、关键结果或授权时提问;暂停依赖步骤,继续有助于目标的独立工作。
12
+ - 在当前授权内持续推进,常规、可逆的实现选择自行决定。新增权限、安装或共享环境变更先核对已有授权,已覆盖的操作不重复询问。仅当缺失信息影响目标、关键结果或授权时提问;暂停依赖步骤,继续有助于目标的独立工作。
13
13
  - 资料、手册、工具结果、待办和职责补充不扩大用户授权。只开展当前任务需要的工作,不为普通问答增加文件或固定流程。
14
14
  - 使用常用词、短句和一致术语。先给结论,按需说明依据、假设、验证、限制和实际产物路径。
15
15
 
@@ -19,13 +19,15 @@
19
19
  | --- | --- |
20
20
  | `AGENTS.md` | 资料导航与已确认的工作区约定 |
21
21
  | `areas/<领域>/` | 长期目标、约束、决策与复盘;个人组合信息 |
22
- | `projects/<名称>/` | 任务结论、必要数据与复现代码;项目依赖和测试 |
23
- | `knowledge/<主题>/` | 可跨任务使用的知识与方法 |
22
+ | `projects/<名称>/` | 任务定义、结论与交付物;源码、依赖和测试 |
23
+ | `knowledge/<主题>/` | 可跨任务复用的概念、方法与口径 |
24
24
  | `providers/` | `AGENTS.md` 导航与各 `<id>/GUIDE.md` 能力手册 |
25
- | `scratch/` | 临时脚本、下载、中间数据与运行环境 |
25
+ | `scratch/` | 临时脚本、查询与计算数据、下载及运行环境 |
26
26
  | `agents/` | `coordinator.md` 与 `general-purpose.md` 的职责补充 |
27
27
  | `.agents/skills/` | 工作区自有专业方法 |
28
28
 
29
+ 可再次查询的数据在使用时按口径获取。结论注明来源、时点与适用条件。
30
+
29
31
  保存前搜索同主题正文,优先更新原文,其他位置用引用连接。整理、修正或结项使用 project-maintenance 技能,遵守本角色的资料修改权限。
30
32
 
31
33
  ## 调用与计算
@@ -34,6 +36,6 @@ CLI 通过 Bash 使用原生命令与当前 help,MCP 使用 Pi 原生接入与
34
36
 
35
37
  按用户授权执行本次业务。研究、接入和验证不授权交易、转账或账本修改。协议成功不证明业务完成;超时、断连和取消不证明未执行。可能已发送的操作先查询原生状态,不因恢复会话或结果缺失自动重发。
36
38
 
37
- 共享 Python 环境为 ready 时,通过 Bash 使用 python 或 python3 和第三方库原生 API。环境异常时报告问题,不自行退回系统 Python;项目构建和测试明确要求其他环境时遵循项目约定。运行计算前按需读取包内 `references/compute.md`,按项目约定选择工作目录并保存必要产物。
39
+ 共享 Python 环境为 ready 时,通过 Bash 使用 python 或 python3 和第三方库原生 API。环境异常可自主诊断,只暂停依赖该环境的步骤,不自行退回系统 Python;项目构建和测试遵循项目声明的环境。运行计算前按需读取包内 `references/compute.md`,按项目约定选择工作目录并保存必要产物。
38
40
 
39
41
  Bash、Python、MCP 和其他扩展具有实际账户与凭据权限,不是沙箱。
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## 环境
4
4
 
5
- 主会话与专家的 `pbot_environment.python` 给出当前共享 Python 3.11+ 环境状态。为 ready 时,通过 Bash 使用 python 或 python3,按任务导入第三方库原生 API。项目构建和测试明确要求另一环境时,按项目约定在对应命令中使用。
5
+ 主会话与专家的 `pbot_environment.python` 给出当前共享 Python 3.11+ 环境状态。为 ready 时,通过 Bash 使用 python 或 python3,按任务导入第三方库原生 API。项目构建和测试遵循项目声明的环境,在对应命令中使用。
6
6
 
7
- 状态未就绪或依赖异常时,暂停依赖该环境的计算并说明问题,不自动退回系统 Python,不删除或重建环境。主会话修复后可用 `/pbot python setup` 重试;专家交回错误,由主会话处理。额外依赖或环境变更须取得用户同意。
7
+ 状态未就绪或依赖异常时,可自主诊断,只暂停依赖该环境的计算,不自动退回系统 Python,不删除或重建共享环境。额外依赖或共享环境变更先核对已有授权及影响范围;未获授权时说明所需变更并询问。需要重试 setup 时,请用户在当前 Pi 会话执行 `/pbot python setup`;专家将所需会话操作或补充授权交回主会话。
8
8
 
9
9
  所有 pbot 工作区共用当前用户的环境。更新依赖前停止相关计算,避免影响其他工作区;不同插件版本的依赖要求可能冲突。库清单说明可用方法,不代替本次环境状态检查。
10
10
 
@@ -37,7 +37,7 @@ cd "/项目根目录" && python scripts/analyze.py --window 20
37
37
 
38
38
  环境中只有 python3 命令时,将示例中的 python 换成 python3。包内相对导入通常使用模块入口,例如在项目根目录执行 `python -m analysis.main --window 20`。普通脚本导入同目录模块与 `-m` 的包导入遵循 Python 原生规则;不要为绕过错误随意改 sys.path。相对数据路径取决于工作目录或脚本自己的路径处理,应先核对约定。临时计算也可使用 `python -c` 或标准输入。
39
39
 
40
- 按脚本或项目约定提供输入、参数和结果位置,使用实际 Bash 工具的超时与取消能力。需要复用的研究保存源码、参数、必要输入、数据来源、时间、单位和结果引用;正式产物归关联项目或长期事务,临时产物使用 scratch。保留已有研究证据,不为整理资料自动重算。
40
+ 按脚本或项目约定提供输入、参数和结果位置,使用实际 Bash 工具的超时与取消能力。
41
41
 
42
42
  Python 具有宿主文件和网络权限,不是沙箱;不得超出本次任务授权执行业务变更或改写历史记录。取消不回滚已发生的副作用。
43
43
 
@@ -60,7 +60,7 @@ plt.close(fig)
60
60
 
61
61
  ## TA-Lib 技术指标
62
62
 
63
- 使用原生 API,不需要指标包装层。常用函数包括 `SMA`、`EMA`、`MACD`、`RSI`、`BBANDS`、`ATR`、`ADX`、`STOCH`、`OBV` 和 `CDL*` 形态函数。用 `talib.get_function_groups()` 或 `talib.get_functions()` 查找能力,用 `help(talib.函数名)` 核对参数与返回值,不凭名称猜测。
63
+ 常用原生函数包括 `SMA`、`EMA`、`MACD`、`RSI`、`BBANDS`、`ATR`、`ADX`、`STOCH`、`OBV` 和 `CDL*` 形态函数。用 `talib.get_function_groups()` 或 `talib.get_functions()` 查找能力,用 `help(talib.函数名)` 核对参数与返回值,不凭名称猜测。
64
64
 
65
65
  以下脚本从第一个命令行参数指定的 JSON 文件读取 close 数组,计算均线并输出严格 JSON:
66
66
 
@@ -92,6 +92,6 @@ print(json.dumps({"sma20": [float(x) if np.isfinite(x) else None for x in sma]},
92
92
  - 显式确定资金、手续费、价差、成交时点与仓位假设,不把库默认值当作投资规则。
93
93
  - 原生 Backtest 不自动提供 A 股 T+1、整手买入、涨跌停或交易日历约束。研究需要这些规则时,必须明确建模并验证;无法表达时说明限制,不能直接声称结果适用于该市场。
94
94
  - 按研究问题决定是否划分样本内/样本外及如何暖机,不默认套用固定回测流程。避免未来数据泄漏。
95
- - 原生结果可能包含 Series、DataFrame、时间和非有限值;按需要提取统计与交易记录,再转为可保存的数据,保留数据来源、参数及库版本。
95
+ - 按交付需要提取统计或交易记录,并说明参数与库版本。导出时处理原生结果中的 Series、DataFrame、时间和非有限值。
96
96
 
97
97
  环境与许可信息见 [Python 说明](../../python/README.md)。
@@ -6,13 +6,13 @@
6
6
 
7
7
  先通过 Pi 原生工具发现查找 Task Conductor,并加载 using-task-conductor 技能,以实际 MCP schema 为准。工具未直接显示不等于未安装;技能可用时,按技能处理连接或排障。
8
8
 
9
- 技能缺失时,用 `pi list` 检查插件及资源启用状态。已有旧版接入包时,先审阅官方升级说明,确认来源和作用域后显式替换旧包声明,不同时加载两份;服务更新与 MCP 配置另行核对,保留任务历史。确未安装则说明需要 Pi 1.4.0+,经用户同意后使用工作区安装命令:
9
+ 技能缺失时,用 `pi list` 检查插件及资源启用状态。已有旧版接入包时,先审阅官方升级说明,确认来源和作用域后显式替换旧包声明,不同时加载两份;服务更新与 MCP 配置另行核对,保留任务历史。确未安装则说明需要 Pi 1.4.0+,核对已有安装授权,已覆盖时直接使用工作区安装命令,未覆盖时再请求授权:
10
10
 
11
11
  ```bash
12
12
  pi install --local npm:task-conductor-plugin
13
13
  ```
14
14
 
15
- 重载后通过 `/skill:using-task-conductor` 处理服务安装、setup 与原生 MCP 接入。复用已有原生连接,不在聊天中索取凭据值。插件可见不证明服务已连接,执行器 ready 不证明模型认证和执行成功。
15
+ 需要重载及通过技能交互安装时,请用户在当前 Pi 会话执行 `/reload`,再执行 `/skill:using-task-conductor` 处理服务安装、setup 与原生 MCP 接入;这些斜杠命令不能通过 Bash 代执行。可直接读取已发现的技能继续可执行的步骤。复用已有原生连接,不在聊天中索取凭据值;CLI 配置查询不等价于当前会话的临时连接状态。插件可见不证明服务已连接,执行器 ready 不证明模型认证和执行成功。
16
16
 
17
17
  能力不可用时说明依赖它的工作受阻,继续其他已授权工作;不以循环等待或后台子会话替代持久调度。
18
18
 
@@ -22,10 +22,10 @@ pi install --local npm:task-conductor-plugin
22
22
 
23
23
  核实节点实际具备的工具、技能和 Python 环境,不假定它继承主会话能力或批准通道。节点任务限定为本次交付,不再递归委派;普通 Pi 节点不是受限专家,这些约定也不是宿主隔离。
24
24
 
25
- 没有可靠批准通道时,不安排需人工批准的后台操作,不降低权限或开展无人值守交易。独立复核使用新的独立节点,只查询和计算;协调器验收不等于专业复核或业务批准。
25
+ 没有可靠批准通道时,不安排需人工批准的后台操作或无人值守交易;不得绕过批准要求。调度和恢复任务不扩大已有授权。独立复核使用新的独立节点,只查询和计算;协调器验收不等于专业复核或业务批准。
26
26
 
27
27
  ## 核对与保存
28
28
 
29
- 任务定义、执行和节点分别用 taskId、runId 和节点 ID 标识。提交响应不明时先查原标识,不换标识重复创建。实时状态查询 Task Conductor;项目资料只保存必要定义、标识和有时点的结果依据。
29
+ 任务定义、执行和节点分别用 taskId、runId 和节点 ID 标识。提交响应不明时先查已有标识;未取得 ID 时,按接口可用的请求关联、任务列表、提交时间或任务特征查找并核对。未能确认是否已创建时保留未知状态,不重复提交。实时状态查询 Task Conductor;项目资料只保存必要定义、标识和有时点的结果依据。
30
30
 
31
31
  取消当前执行与停止未来周期分别操作,并查询确认。pbot_agent stop 或 Pi 关闭不停止 Task Conductor。报告已设置调度须有实际提交结果;报告本轮成功须核对本轮产物与完成条件。
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: project-maintenance
3
- description: 维护项目结论、证据与可复用任务定义。用于资料更新、按既有口径复用任务、成果整理和项目结项。
3
+ description: 维护项目结论、分析口径与可复用任务定义。用于资料更新、按既有口径复用任务、成果整理和项目结项。
4
4
  ---
5
5
 
6
6
  # 项目整理与维护
7
7
 
8
- 让资料呈现当前结论、适用条件、必要证据和真正未决的问题。重要结论或决策变化保留原因与原依据。
8
+ 让资料呈现当前结论、适用条件、分析口径和未决问题。重要结论或决策变化保留原因与原依据。
9
9
 
10
10
  根据本次变化更新当前任务、所属正文及实际受影响的引用。整体整理按用户指定范围进行。
11
11
 
@@ -17,32 +17,26 @@ description: 维护项目结论、证据与可复用任务定义。用于资料
17
17
 
18
18
  历史证据与重要决策保留原有来源、时点、适用条件及必要理由,当前说明通过引用追溯。用户偏好与约束按其确认范围记录,推断与已确认约定分开。
19
19
 
20
- 有跨任务价值的知识提炼到 knowledge/ 并引用项目证据,保留复用所需的条件。知识按主题命名,需要保留历史的研究和决策按日期与主题命名。
20
+ 从项目结论中提炼可跨任务复用的概念、方法与口径,写入 knowledge/,说明适用条件并引用来源。知识按主题命名,需要保留历史的研究和决策按日期与主题命名。
21
21
 
22
- 根 AGENTS.md 用一棵 text 目录树维护资料索引,已确认的工作区约定单独保留。入口变化时更新现有树,展开到有资料的主题或项目目录;节点注释说明用途,未完成项目加 [TODO],完成后移除。结论和未决事项留在正文,导航用链接引用。
22
+ 尊重根 AGENTS.md 已有的导航格式和工作区约定,只更新受本次变化影响的入口,不为统一样式重排。新建导航时可用 text 目录树,展开到有资料的主题或项目目录并简注用途,也可按已有习惯使用链接列表;任务状态沿用现有标记。结论和未决事项留在正文,导航引用对应资料。
23
23
 
24
24
  ## 复用任务定义
25
25
 
26
26
  需要跨会话继续或重复执行的任务,在所属项目或长期事务中保存交付用途、必要证据、可接受限制和完成条件。目标变化时同步受影响的交付条件。
27
27
 
28
- 重复分析或报告的定义按需包含指标公式与分母、数据来源、对象与筛选范围、期间与时区、分组与比较基准、输入和执行入口、产物位置及核验条件。单次输入与结果作为有时点的证据引用,与可复用定义分开。
28
+ 重复分析或报告的定义按需包含数据来源与查询方式、对象与筛选范围、期间与时区、单位、指标公式与分母、分组与比较基准、执行入口及核验条件。单次报告引用所用定义。
29
29
 
30
- 按上次口径重做时,读取已有定义,沿用已确认选择,并核对来源、版本与关键前提是否仍适用。更新报告使用本次期间与数据;复现历史结果使用指定输入。口径变化说明对可比性的影响,验证状态随新的验证证据更新。数值与计算核验见 [analysis-validation 技能](../analysis-validation/SKILL.md)。
30
+ 按上次口径重做时,读取已有定义,沿用已确认选择,并核对来源、版本与关键前提是否仍适用。更新报告使用本次期间与数据;复现历史结果使用指定输入。口径变化说明对可比性的影响,验证状态随新的验证证据更新。
31
31
 
32
32
  ## 成果归集与清理
33
33
 
34
- 用户要求整理或结项时,检查本次范围内的项目目录及 scratch/ 等临时产物位置。将有用信息并入正文,将必要证据与复现资料保存到对应 projects/<名称>/。
34
+ 用户要求整理或结项时,检查本次范围内的项目目录及 scratch/ 等临时产物位置。将结论与方法并入所属正文,将交付物与可复用源码归入对应项目,更新来源引用与执行入口。
35
35
 
36
- 长期保留支撑结论的最小充分资料:
37
-
38
- - 无法可靠恢复的输入快照、来源与数据口径。按能否再次取得同一份输入判断保存价值,而非来源是否仍可访问。
39
- - 复现所需的源码、命令、参数、必要环境说明及结果引用;数据注明对象、时点与单位。
40
- - 用户要求的最终成果与明细。
41
-
42
- 完成归集并更新引用和复现路径后,清理已被吸收或不再需要、且不承担证据作用的临时产物。仍在使用、跨项目共享或归属与价值不明的文件暂留。清理仅限本次范围内的临时产物;正式资料、历史交易记录与备份保留,历史记录不改写。
36
+ 归集后清理本次范围内已被正文吸收或不再需要的临时文件。正式资料、历史记录与备份保持原样。
43
37
 
44
38
  ## 项目结项
45
39
 
46
- 以已确认的目标和交付条件判断完成状态,核对必要成果、证据与复现入口确实可用。满足本次用途即可结项,已知限制留在正文;可选深化与必需工作分开。
40
+ 以已确认的目标和交付条件判断完成状态,核对交付物、来源引用与执行入口确实可用。满足本次用途即可结项,已知限制留在正文;可选深化与必需工作分开。
47
41
 
48
42
  交付条件尚未满足时,记录具体缺口及其影响。完成状态、正文与证据一致后,同步项目入口和相关导航。
@@ -13,13 +13,13 @@ description: 接入或修复原生 CLI/MCP,核对认证与可用性,维护
13
13
 
14
14
  ## MCP
15
15
 
16
- 用 `/mcp` 检查已有连接、认证、工具与配置来源。需要从 Bash 配置时,先读 `pi mcp --help` 及相关子命令帮助。
16
+ 可先用当前可见工具及 CLI 自主诊断。需要查看当前会话的连接、认证、工具与配置来源时,请用户在该 Pi 会话执行 `/mcp`;这是会话操作,不能通过 Bash 代执行。Bash 中使用 `pi mcp --help` 及相关子命令查询或配置;CLI 查询不等价于当前会话状态,尤其不能据此判断会话内临时连接。
17
17
 
18
18
  新增连接默认只接入当前项目。从工作区根目录执行 `pi mcp add --local <server> ...`,写入 `.pi/mcp.json`;删除项目连接使用 `pi mcp remove --local <server>`。add/remove 未指定 `--local` 时修改当前 Pi agent dir 的 `mcp.json`,仅在用户明确要求跨项目使用时选择该范围。第三方 setup 同样采用项目配置;无法指定范围时,改用 Pi 原生项目配置。
19
19
 
20
20
  项目配置经 Pi 项目信任规则加载,同名条目可能覆盖用户级配置。修改前核对同名项,保留无关条目与已有用户级连接;迁移另按用户指定范围处理。认证使用原生登录或环境变量引用。
21
21
 
22
- 外部修改配置后执行 `/reload`,再用 `/mcp` 核对实际配置来源和连接状态。当前工具 schema 通过原生工具搜索或 codemode 按需取得。
22
+ 外部修改配置后,请用户在当前 Pi 会话执行 `/reload`,再用 `/mcp` 核对实际配置来源和连接状态;不要声称已代用户完成会话操作。当前工具 schema 通过原生工具搜索或 codemode 按需取得。新增权限、安装或共享配置变更先核对已有授权,已授权的操作不重复询问。
23
23
 
24
24
  ## 验证与手册
25
25
 
@@ -5,9 +5,9 @@
5
5
  ```text
6
6
  ./
7
7
  ├── areas/ # 长期目标、约束、建议、决策与复盘
8
- ├── projects/ # 任务结论、必要数据与复现代码
9
- ├── knowledge/ # 可复用知识、方法与参考资料
8
+ ├── projects/ # 任务定义、结论、交付物与源码
9
+ ├── knowledge/ # 可复用的概念、方法与口径
10
10
  ├── providers/ # 能力提供方导航与手册
11
- ├── scratch/ # 临时文件、运行环境与计算产物
11
+ ├── scratch/ # 临时脚本、查询与计算数据、下载及运行环境
12
12
  └── agents/ # coordinator.md 与 general-purpose.md 的职责补充
13
13
  ```
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import type { ExtensionAPI, ExtensionContext, CustomMessageEntryDraft, SessionBoundaryDraft } from "@earendil-works/pi-coding-agent";
2
3
  import type { Lifecycle } from "./lifecycle.js";
3
4
  import type { Confirmations } from "../ui/confirmation.js";
@@ -12,6 +13,21 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
12
13
  let ctx: ExtensionContext | undefined, timer: ReturnType<typeof setInterval> | undefined;
13
14
  let paused = false;
14
15
  let controlBusySince: number | undefined;
16
+ let failure: { key: string; error: string; recovery: "retry" | "repair" | "reload" } | undefined;
17
+ const recordFailure = (error: unknown) => {
18
+ const busy = (error as NodeJS.ErrnoException).code === "PBOT_CONTROL_BUSY";
19
+ if (busy) {
20
+ controlBusySince ??= Date.now();
21
+ if (Date.now() - controlBusySince < 5000) return;
22
+ } else controlBusySince = undefined;
23
+ let recovery: "retry" | "repair" | "reload" = busy ? "retry" : "repair";
24
+ try { lifecycle.active?.teams.check(); } catch { recovery = "reload"; }
25
+ // A stronger recovery requirement needs a new notice even if the earlier one was delivered.
26
+ if (!failure || (failure.recovery !== recovery && (failure.recovery === "retry" || recovery === "reload"))) {
27
+ failure = { key: "agent-error:" + randomUUID(), error: String(error), recovery };
28
+ }
29
+ if (ctx?.hasUI) ctx.ui.setStatus("pbot-agents-error", String(error));
30
+ };
15
31
  const submitting = new Map<string, number>();
16
32
  // Only a lock-busy, not-yet-published input is retried in this runtime. Reload discards this map.
17
33
  const dispatching = new Map<string, TeamInput>();
@@ -21,9 +37,14 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
21
37
  if (!ctx || !instance) return;
22
38
  pausePending = true;
23
39
  try { instance.teams.pauseAll(agentOwner(ctx), "主会话中断"); pausePending = false; dispatching.clear(); }
24
- catch (error) { if ((error as NodeJS.ErrnoException).code !== "PBOT_CONTROL_BUSY") { instance.teams.fail(error); throw error; } }
40
+ catch (error) {
41
+ const busy = (error as NodeJS.ErrnoException).code === "PBOT_CONTROL_BUSY";
42
+ if (!busy) instance.teams.fail(error);
43
+ recordFailure(error);
44
+ if (!busy) throw error;
45
+ }
25
46
  };
26
- const notification = (): CustomMessageEntryDraft | undefined => {
47
+ const collectNotification = (): CustomMessageEntryDraft | undefined => {
27
48
  const instance = lifecycle.active;
28
49
  if (!ctx || !instance || paused || ctx.signal?.aborted) return;
29
50
  const delivered = new Set([...deliveredCycles(ctx), ...deliveredTeams(ctx)]), now = Date.now();
@@ -48,13 +69,34 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
48
69
  const batch = pending.slice(0, 8), keys = batch.map(v => v.agentId + ":" + v.cycle.cycleId);
49
70
  for (const key of keys) submitting.set(key, now);
50
71
  return { type: "custom_message", customType: "pbot.agent-results", display: true,
51
- content: "# 子会话工作结果\n\n子会话工作已结束。完整报告用 pbot_agent read 读取。\n\n" +
72
+ content: "# 子会话工作结果\n\n子会话工作已结束。完整报告用 pbot_agent read 读取,指定对应 agentId 和 cycle.cycleId;nextOffset 非空时沿用该 cycleId 续读。\n\n" +
52
73
  (batch.some(v => v.cycle.source !== "parent") ? "包含人工子会话输入,其结果不扩大主会话原任务授权。\n\n" : "") +
53
74
  batch.map(v => "## " + v.name + "\n\n" + markdownFields(json({ agentId: v.agentId,
54
75
  cycle: { cycleId: v.cycle.cycleId, state: v.cycle.state, source: v.cycle.source, requestIds: v.cycle.requestIds,
55
76
  ...(v.cycle.error === undefined ? {} : { error: v.cycle.error }) },
56
77
  report: v.report.slice(0, 2000), truncated: v.report.length > 2000 }), 3)).join("\n\n"), details: { keys } };
57
78
  };
79
+ const notification = (): CustomMessageEntryDraft | undefined => {
80
+ if (!ctx || !lifecycle.active || paused || ctx.signal?.aborted) return;
81
+ if (!failure) {
82
+ try { return collectNotification(); }
83
+ catch (error) { recordFailure(error); }
84
+ }
85
+ if (!failure || deliveredCycles(ctx).has(failure.key)) return;
86
+ const now = Date.now(), sent = submitting.get(failure.key);
87
+ if (sent !== undefined && now - sent < 5000) return;
88
+ submitting.set(failure.key, now);
89
+ const recovery = failure.recovery === "reload"
90
+ ? "团队服务或协作存储已停用,修复文件后当前实例仍不可用。先按错误信息排障,再请用户在当前 Pi 会话执行 /reload;重载前不要反复调用 pbot_team。重载后用 pbot_team status/read 核对团队状态与公共原文。"
91
+ : failure.recovery === "retry"
92
+ ? "团队控制锁持续被占用,后台会继续重试;锁释放后自动恢复扫描,不自动删除锁或重新提交任务。"
93
+ : "报告或控制记录读取失败。先按错误信息排障;修复后后台扫描会重试。";
94
+ return { type: "custom_message", customType: "pbot.agent-error", display: true,
95
+ content: ["# 子会话自动跟进故障", "", recovery,
96
+ ...(pausePending ? ["", "团队暂停尚未确认,不能据此开始成员私人任务。"] : []), "",
97
+ "尚未交付的结果未确认。需要核对成员时,用 pbot_agent list/status 查询状态、pbot_agent read 读取可用报告;结果未知不构成重新提交的依据。", "",
98
+ markdownFields({ error: failure.error })].join("\n"), details: { keys: [failure.key] } };
99
+ };
58
100
  const refresh = () => {
59
101
  const instance = lifecycle.active;
60
102
  if (!ctx || !instance) return;
@@ -86,17 +128,14 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
86
128
  ctx.ui.setStatus("pbot-teams", teams.length ? "团队 " + teams.filter(t => t.owned && t.run?.state === "running").length + " 运行 / " + teams.length : undefined);
87
129
  }
88
130
  if (ctx.mode === "tui") ctx.ui.setWidget("pbot-agents", values.some(v => ["starting", "busy", "stopping"].includes(v.state)) ? (_tui, theme) => agentBoard(values, theme) : undefined);
89
- if (ctx.isIdle() && !paused) { const message = notification(); if (message) pi.sendMessage(message, { triggerTurn: true, deliverAs: "steer" }); }
90
- controlBusySince = undefined;
91
- if (ctx.hasUI) ctx.ui.setStatus("pbot-agents-error", undefined);
131
+ if (!pausePending) {
132
+ failure = undefined; controlBusySince = undefined;
133
+ if (ctx.hasUI) ctx.ui.setStatus("pbot-agents-error", undefined);
134
+ }
92
135
  } catch (error) {
93
- // Brief contention retries on the next scan; persistent locks remain visible.
94
- if ((error as NodeJS.ErrnoException).code === "PBOT_CONTROL_BUSY") {
95
- controlBusySince ??= Date.now();
96
- if (Date.now() - controlBusySince < 5000) return;
97
- } else controlBusySince = undefined;
98
- if (ctx.hasUI) ctx.ui.setStatus("pbot-agents-error", String(error));
136
+ recordFailure(error);
99
137
  }
138
+ if (ctx.isIdle() && !paused) { const message = notification(); if (message) pi.sendMessage(message, { triggerTurn: true, deliverAs: "steer" }); }
100
139
  };
101
140
  const boundary = (outcome: string, entries: SessionBoundaryDraft[], context: ExtensionContext) => {
102
141
  ctx = context;
@@ -106,7 +145,7 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
106
145
  return;
107
146
  };
108
147
  pi.on("session_start", (_event, context) => {
109
- ctx = context; paused = false; pausePending = false; controlBusySince = undefined; confirmations.resume(); submitting.clear(); dispatching.clear();
148
+ ctx = context; paused = false; pausePending = false; controlBusySince = undefined; failure = undefined; confirmations.resume(); submitting.clear(); dispatching.clear();
110
149
  if (timer) clearInterval(timer);
111
150
  if (lifecycle.active) { lifecycle.active.agentOwner = agentOwner(ctx); timer = setInterval(refresh, 1000); timer.unref(); }
112
151
  refresh();
@@ -2,8 +2,7 @@ import { readFile, stat } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { existsSync, realpathSync } from "node:fs";
4
4
  import { randomUUID } from "node:crypto";
5
- import { createReadTool, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
6
- import type { TextContent, ImageContent } from "@earendil-works/pi-ai";
5
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
7
6
  import { AgentFiles, descriptor, launch, status, cycle } from "../storage/agent-files.js";
8
7
  import { TeamFiles } from "../storage/team-files.js";
9
8
  import { TeamService } from "../services/agents/team-service.js";
@@ -15,6 +14,7 @@ import { PythonEnvironmentService } from "../services/workspace/python-environme
15
14
  import { activatePython } from "../integrations/python-activation.js";
16
15
  import { authorizeMutation } from "../services/workspace/workspace-files.js";
17
16
  import { agentText } from "../integrations/pi/agent-resources.js";
17
+ import { agentInput } from "../integrations/pi/agent-input.js";
18
18
  import { agentFileTools } from "../integrations/pi/agent-tools.js";
19
19
  import { resourceRoot } from "../common/paths.js";
20
20
  import { requireThat } from "../common/validation.js";
@@ -118,12 +118,7 @@ export default function subagent(pi: ExtensionAPI) {
118
118
  const link = links.get(request.requestId);
119
119
  if (link && (link.team.closed || link.run.state !== "running" || link.run.deadlineAt <= Date.now())) continue;
120
120
  if (link && (!ctx.isIdle() || ctx.hasPendingMessages() || current || interactive || queuedRequests.length)) break;
121
- const content: (TextContent | ImageContent)[] = [];
122
- for (const path of request.files) {
123
- const output = await createReadTool(d.cwd).execute("file-" + request.requestId, { path }, lifetime.signal);
124
- content.push(...output.content.filter((p): p is TextContent | ImageContent => p.type === "text" || p.type === "image"));
125
- }
126
- content.push({ type: "text", text: request.message || "请处理所附文件。" });
121
+ const content = await agentInput(d.cwd, request, lifetime.signal);
127
122
  requireThat(ctx.model, "子会话没有可用模型。");
128
123
  requireThat(ctx.modelRegistry.hasConfiguredAuth(ctx.model) || await ctx.modelRegistry.getProviderAuth(ctx.model.provider), "子会话模型未认证,请配置原生 Pi 认证后显式恢复。");
129
124
  ensure();
@@ -6,7 +6,7 @@ export function agentOwner(ctx: ExtensionContext): AgentOwner {
6
6
  export function deliveredCycles(ctx: ExtensionContext): Set<string> {
7
7
  const keys = new Set<string>();
8
8
  for (const entry of ctx.sessionManager.getBranch()) {
9
- const data = entry.type === "custom_message" && entry.customType === "pbot.agent-results" ? entry.details
9
+ const data = entry.type === "custom_message" && ["pbot.agent-results", "pbot.agent-error"].includes(entry.customType) ? entry.details
10
10
  : entry.type === "custom" && entry.customType === "pbot.agent-read" ? entry.data : undefined;
11
11
  if (entry.type === "custom" && entry.customType === "pbot.agent-read") {
12
12
  const receipt = entry.data as { toolCallId?: string };
@@ -0,0 +1,19 @@
1
+ import { resolve } from "node:path";
2
+ import { createReadTool } from "@earendil-works/pi-coding-agent";
3
+ import type { TextContent, ImageContent } from "@earendil-works/pi-ai";
4
+ import type { AgentMessage } from "../../model/agent.js";
5
+
6
+ export async function agentInput(cwd: string, request: Pick<AgentMessage, "requestId" | "files" | "message">,
7
+ signal?: AbortSignal): Promise<(TextContent | ImageContent)[]> {
8
+ const content: (TextContent | ImageContent)[] = [];
9
+ const read = createReadTool(cwd);
10
+ for (const file of request.files) {
11
+ const path = resolve(cwd, file);
12
+ const output = await read.execute("file-" + request.requestId, { path }, signal);
13
+ content.push({ type: "text", text: "附件来源(绝对 path):" + JSON.stringify(path) +
14
+ "\n以下为原生 read 内容;如有截断,按原生提示用此 path 与 offset/limit 续读。" });
15
+ content.push(...output.content.filter((part): part is TextContent | ImageContent => part.type === "text" || part.type === "image"));
16
+ }
17
+ content.push({ type: "text", text: request.message || "请处理所附文件。" });
18
+ return content;
19
+ }
@@ -31,8 +31,7 @@ export function teamBrief(link: TeamLink): string {
31
31
  return ["# 团队协作", "", "- 团队:" + link.team.name, "- `teamId`: `" + link.team.teamId + "`",
32
32
  "- 当前成员 `agentId`: `" + link.request.agentId + "`", "- `runId`: `" + link.run.runId + "`",
33
33
  "- 轮次:" + link.request.round + " / " + link.run.rounds, "", "## 共同目标", "", link.team.brief,
34
- "", "## 成员职责", "", ...link.team.members.flatMap(member => ["### " + member.agentId, "", member.role, ""]),
35
- "## 本批次要求", "", link.run.instruction].join("\n");
34
+ "", "## 成员职责", "", ...link.team.members.flatMap(member => ["### " + member.agentId, "", member.role, ""])].join("\n");
36
35
  }
37
36
 
38
37
  /** Branch-local context, not a durable read receipt. Long messages retain explicit read positions. */
@@ -171,6 +171,10 @@ export class AgentService {
171
171
  }
172
172
  return values;
173
173
  }
174
+ status(id: string, owner: AgentOwner) {
175
+ const d = this.get(id, owner), latest = this.cycles(id, owner).at(-1);
176
+ return { ...this.view(d, owner), ...(latest ? { cycle: latest } : {}) };
177
+ }
174
178
  read(id: string, owner: AgentOwner, cycleId?: string, offset = 0, limit = 16000) {
175
179
  const d = this.get(id, owner), cycles = this.cycles(id, owner);
176
180
  const c = cycleId ? cycles.find(v => v.cycleId === cycleId) : cycles.at(-1);
@@ -17,14 +17,14 @@ const options = {
17
17
  noContextFiles: Type.Optional(Type.Boolean({ description: "关闭 Pi 原生上下文文件加载,默认 false。" })),
18
18
  };
19
19
  const page = {
20
- cycleId: Type.Optional(Type.String({ ...id, description: "read 的历史工作周期;省略时读取最近周期。" })),
21
- offset: Type.Optional(Type.Integer({ minimum: 0, description: "报告字符偏移,默认 0。" })),
22
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 16000, description: "最多返回字符数,默认 16000。" })),
20
+ cycleId: Type.Optional(Type.String({ ...id, description: "仅 read:省略时读取最近周期;按 nextOffset 续读时须固定为上页返回的 cycle.cycleId,避免切换到新周期。" })),
21
+ offset: Type.Optional(Type.Integer({ minimum: 0, description: "仅 read:报告正文的 UTF-16 字符偏移,默认 0;续读使用上页 nextOffset。" })),
22
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 16000, description: "仅 read:最多返回的正文 UTF-16 字符数,默认 16000,不是消息数或 token 数。" })),
23
23
  };
24
24
  const delivery = Type.Optional(Type.Union([Type.Literal("steer"), Type.Literal("followUp")], { description: "send 默认 steer 补充当前工作;followUp 排到当前工作之后。" }));
25
25
  export const agentParametersSchema = Type.Object({
26
26
  action: Type.Union([Type.Literal("start"), Type.Literal("send"), Type.Literal("rename"), Type.Literal("status"), Type.Literal("list"), Type.Literal("read"), Type.Literal("stop"), Type.Literal("resume")], {
27
- description: "start 可省略 message/prompts/files,只创建待命进程;启动选项仅用于 start。list 不带其他字段;其余操作须 agentId。send/resume 须 message,rename 须 name;分页仅用于 read。",
27
+ description: "start 仅接受启动选项,可省略 message/prompts/files 创建待命进程,禁止 agentId/delivery/分页字段;list 只接受 action;status/stop 只接受并必填 agentId;send 必填 agentId/message,仅可选 delivery;resume 必填 agentId/message;rename 必填 agentId/name;read 必填 agentId,仅可选 cycleId/offset/limit。禁止携带其他 action 的字段。",
28
28
  }),
29
29
  agentId: Type.Optional(id), ...options, ...page, delivery,
30
30
  }, { additionalProperties: false });
@@ -48,7 +48,9 @@ export const agentViewSchema = Type.Object({ agentId: Type.String(), name: Type.
48
48
  state: Type.Union([Type.Literal("starting"), Type.Literal("busy"), Type.Literal("idle"), Type.Literal("stopping"), Type.Literal("stopped"), Type.Literal("error")], { description: "进程状态;idle 不代表上轮工作成功,结果见 cycle.state。" }),
49
49
  activity: Type.String(), cycle: Type.Optional(cycle), error: Type.Optional(Type.String()),
50
50
  requestId: Type.Optional(Type.String()), messageStatus: Type.Optional(Type.Literal("queued", { description: "消息已入队,不代表模型已阅读或完成。" })),
51
- cycles: Type.Optional(Type.Array(Type.Object({ cycleId: Type.String(), state: Type.String() }))),
52
- report: Type.Optional(Type.String()), totalLength: Type.Optional(Type.Integer()), nextOffset: Type.Optional(Type.Union([Type.Integer(), Type.Null()])),
51
+ cycles: Type.Optional(Type.Array(Type.Object({ cycleId: Type.String(), state: Type.String() }), { description: "仅 read 返回:可读取的周期索引。" })),
52
+ report: Type.Optional(Type.String({ description: "仅 read 返回:选定周期的报告正文片段。已结束周期的读取结果持久化后领取该周期通知,即使本页未读完。" })),
53
+ totalLength: Type.Optional(Type.Integer({ description: "仅 read 返回:完整报告的 UTF-16 字符数。" })),
54
+ nextOffset: Type.Optional(Type.Union([Type.Integer(), Type.Null()], { description: "仅 read 返回:null 表示本报告已读完;否则以相同 agentId、cycle.cycleId 和此 offset 继续。" })),
53
55
  }, { additionalProperties: false });
54
56
  export const agentResultSchema = Type.Union([agentViewSchema, Type.Object({ agents: Type.Array(agentViewSchema) }, { additionalProperties: false })]);
@@ -27,13 +27,15 @@ function agentOutput(view: AgentView & Partial<AgentOutput>): AgentOutput {
27
27
  }
28
28
 
29
29
  export function agentTool(service: AgentService, access: CallAccess) {
30
- const valid = schema(agentActionSchema);
30
+ const validators = new Map(agentActionSchema.anyOf.map(action => [action.properties.action.const, schema(action)]));
31
31
  return defineTool({ name: "pbot_agent", label: "子会话",
32
- description: "独立 Pi 子会话,不继承父聊天历史。start 立即返回;未提供 message/prompts/files 时只启动待命进程,不调用模型。完成自动通知,无需轮询。团队占用期间不能私人 send。send 继续对话,rename 改名,status/list 查看状态,read 非阻塞读取报告。state 是进程状态,cycle.state 是工作结果,queued 仅表示入队。stop 停止并保留历史,resume 携新 message 恢复,不重放旧工作。独立复核须 start 新上下文。子会话使用原生 CLI/MCP,按委派范围工作,不递归委派。每个父会话最多 50 个存活子会话,空闲也计入。",
32
+ description: "独立 Pi 子会话,不继承父聊天历史。start 立即返回;未提供 message/prompts/files 时只启动待命进程,不调用模型。完成自动通知,无需轮询。团队占用期间不能私人 send。send 继续对话,rename 改名,status/list 查看状态,status 返回最近周期且不读取报告正文。read 非阻塞读取报告;读取已结束周期会领取该周期的完成通知(包括部分读取),后续不再重复通知。nextOffset 非空时以返回的 cycle.cycleId 固定周期续读。state 是进程状态,cycle.state 是工作结果,queued 仅表示入队。stop 停止并保留历史,resume 携新 message 恢复,不重放旧工作。独立复核须 start 新上下文。子会话使用原生 CLI/MCP,按委派范围工作,不递归委派。每个父会话最多 50 个存活子会话,空闲也计入。",
33
33
  parameters: agentParametersSchema, outputSchema: agentResultSchema,
34
34
  async execute(_id, args, signal, _update, ctx) {
35
35
  access.check(); signal?.throwIfAborted();
36
- requireThat(valid(args), "子会话参数不符合所选 action;请核对必要字段。");
36
+ const valid = validators.get(args.action);
37
+ requireThat(valid, "未知的子会话 action:" + args.action);
38
+ requireThat(valid(args), "子会话 " + args.action + " 参数无效:" + JSON.stringify(valid.errors));
37
39
  const input = args as Static<typeof agentParametersSchema>, owner = agentOwner(ctx);
38
40
  let value: AgentView & Partial<AgentOutput>;
39
41
  switch (input.action) {
@@ -46,7 +48,7 @@ export function agentTool(service: AgentService, access: CallAccess) {
46
48
  case "rename": value = service.rename(input.agentId!, input.name!, owner); break;
47
49
  case "resume": value = service.resume(input.agentId!, input.message!, owner); break;
48
50
  case "stop": value = service.stop(input.agentId!, owner); break;
49
- case "status": value = service.read(input.agentId!, owner, undefined, 0, 1); break;
51
+ case "status": value = service.status(input.agentId!, owner); break;
50
52
  case "read": {
51
53
  const page = service.read(input.agentId!, owner, input.cycleId, input.offset, input.limit);
52
54
  const data = agentOutput(page), output = markdownResult(data, "子会话报告");