personal-bot 0.2.6 → 0.2.9

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.
Files changed (75) hide show
  1. package/CONTEXT.md +40 -59
  2. package/README.md +65 -91
  3. package/README.zh-CN.md +69 -89
  4. package/docs/README.md +21 -17
  5. package/docs/agent-collaboration.md +11 -13
  6. package/docs/architecture-guidelines.md +2 -2
  7. package/docs/capability-design.md +8 -10
  8. package/docs/dsh.en.md +339 -0
  9. package/docs/dsh.md +284 -48
  10. package/docs/file-storage.md +3 -3
  11. package/docs/history.md +6 -4
  12. package/docs/installation-design.md +55 -197
  13. package/docs/model-context.md +46 -0
  14. package/docs/model-instruction-guidelines.md +51 -0
  15. package/docs/release.md +10 -6
  16. package/docs/technical-design.md +24 -16
  17. package/lib/common/typebox.LICENSE +23 -0
  18. package/lib/common/typebox.js +4740 -0
  19. package/lib/dsh.js +4 -0
  20. package/lib/extension/dsh/agent-events.js +1 -1
  21. package/lib/extension/dsh/agent-runtime.js +8 -5
  22. package/lib/extension/dsh/command.js +1 -1
  23. package/lib/extension/dsh/management.js +1 -1
  24. package/lib/integrations/dsh/resources.d.ts +1 -1
  25. package/lib/integrations/dsh/resources.js +14 -14
  26. package/lib/integrations/dsh/session.js +1 -0
  27. package/lib/integrations/installation/pi-bridge.js +9 -9
  28. package/lib/integrations/team-context.js +7 -3
  29. package/lib/services/agents/agent-notices.js +2 -2
  30. package/lib/services/agents/team-service.js +1 -1
  31. package/lib/tools/agents/contract.d.ts +1 -1
  32. package/lib/tools/agents/contract.js +8 -8
  33. package/lib/tools/agents/dsh-contract.js +5 -5
  34. package/lib/tools/history/contract.d.ts +1 -1
  35. package/lib/tools/history/contract.js +3 -3
  36. package/lib/tools/teams/contract.d.ts +2 -2
  37. package/lib/tools/teams/contract.js +12 -12
  38. package/npm-shrinkwrap.json +33 -25
  39. package/package.json +124 -53
  40. package/python/README.md +5 -5
  41. package/resources/agents/coordinator.md +5 -33
  42. package/resources/agents/general-purpose.md +3 -15
  43. package/resources/prompts/history-summary.md +1 -1
  44. package/resources/prompts/team-member.md +7 -0
  45. package/resources/prompts/workspace.md +13 -24
  46. package/resources/references/compute-finance.md +22 -0
  47. package/resources/references/compute.md +11 -88
  48. package/resources/references/task-conductor.md +20 -15
  49. package/resources/skills/project-maintenance/SKILL.md +9 -11
  50. package/resources/skills/provider-connections/SKILL.md +7 -7
  51. package/scripts/README.md +16 -15
  52. package/src/dsh.ts +4 -0
  53. package/src/extension/agent-events.ts +17 -4
  54. package/src/extension/command.ts +1 -1
  55. package/src/extension/dsh/agent-events.ts +1 -1
  56. package/src/extension/dsh/agent-runtime.ts +7 -6
  57. package/src/extension/events.ts +14 -9
  58. package/src/extension/subagent-entry.ts +24 -16
  59. package/src/integrations/dsh/resources.ts +17 -14
  60. package/src/integrations/dsh/session.ts +2 -0
  61. package/src/integrations/installation/pi-bridge.ts +9 -7
  62. package/src/integrations/pi/agent-context.ts +1 -1
  63. package/src/integrations/team-context.ts +7 -3
  64. package/src/services/agents/agent-notices.ts +2 -2
  65. package/src/services/agents/team-service.ts +1 -1
  66. package/src/tools/agents/contract.ts +7 -7
  67. package/src/tools/agents/dsh-contract.ts +4 -4
  68. package/src/tools/history/contract.ts +2 -2
  69. package/src/tools/register.ts +1 -7
  70. package/src/tools/teams/contract.ts +11 -11
  71. package/docs/dsh-implementation-plan.md +0 -158
  72. package/docs/dsh-support-proposal.md +0 -314
  73. package/docs/subagent-upgrade-proposal.md +0 -357
  74. package/docs/validation.md +0 -132
  75. package/resources/prompts/tool-guidelines.md +0 -3
@@ -5,17 +5,17 @@ description: 维护项目结论、分析口径与可复用任务定义。用于
5
5
 
6
6
  # 项目整理与维护
7
7
 
8
- 让资料呈现当前结论、适用条件、分析口径和未决问题。重要结论或决策变化保留原因与原依据。
8
+ 让资料呈现当前结论、适用条件、分析口径和未决问题。重要结论或决策变化时简要说明原因。
9
9
 
10
10
  根据本次变化更新当前任务、所属正文及实际受影响的引用。整体整理按用户指定范围进行。
11
11
 
12
12
  ## 更新所属资料
13
13
 
14
- 从根 AGENTS.md 和相关目录入口定位正文;入口未命中时,按主题、别名或项目名搜索。结合实际成果核对正文与入口,资料状态以对应证据为依据。
14
+ 从根 AGENTS.md 和相关目录入口定位正文;入口未命中时,按主题、别名或项目名搜索。
15
15
 
16
- 按主题合并当前说明,保留理解、继续或复用任务所需的信息。新旧证据先核对对象、时期和条件;用已确认的新结论替换被取代的说明,同步相关说明与引用,保留未解决的冲突。
16
+ 按主题合并当前说明,保留继续或复用任务所需的信息。比较新旧内容的对象、时期和条件,替换过时说明,同步引用,保留未解决的冲突。
17
17
 
18
- 历史证据与重要决策保留原有来源、时点、适用条件及必要理由,当前说明通过引用追溯。用户偏好与约束按其确认范围记录,推断与已确认约定分开。
18
+ 保留历史资料与重要决策的来源和时间,当前说明通过引用连接。用户偏好与约定按用户表达记录,不把推测写成既定约定。
19
19
 
20
20
  从项目结论中提炼可跨任务复用的概念、方法与口径,写入 knowledge/,说明适用条件并引用来源。知识按主题命名,需要保留历史的研究和决策按日期与主题命名。
21
21
 
@@ -23,20 +23,18 @@ description: 维护项目结论、分析口径与可复用任务定义。用于
23
23
 
24
24
  ## 复用任务定义
25
25
 
26
- 需要跨会话继续或重复执行的任务,在所属项目或长期事务中保存交付用途、必要证据、可接受限制和完成条件。目标变化时同步受影响的交付条件。
26
+ 需要跨会话继续或重复执行的任务,在所属项目或长期事务中保存目标、输入、执行方式和预期产物,随任务变化更新。
27
27
 
28
- 重复分析或报告的定义按需包含数据来源与查询方式、对象与筛选范围、期间与时区、单位、指标公式与分母、分组与比较基准、执行入口及核验条件。单次报告引用所用定义。
28
+ 重复分析或报告的定义按需包含数据来源与查询方式、对象与筛选范围、期间与时区、单位、指标公式与分母、分组与比较基准、执行入口。单次报告引用所用定义。
29
29
 
30
- 按上次口径重做时,读取已有定义,沿用已确认选择,并核对来源、版本与关键前提是否仍适用。更新报告使用本次期间与数据;复现历史结果使用指定输入。口径变化说明对可比性的影响,验证状态随新的验证证据更新。
30
+ 按上次口径重做时,读取已有定义,检查来源、版本与关键前提是否仍适用。更新报告使用本次期间与数据;复现历史结果使用指定输入。口径变化说明对可比性的影响。
31
31
 
32
32
  ## 成果归集与清理
33
33
 
34
34
  用户要求整理或结项时,检查本次范围内的项目目录及 scratch/ 等临时产物位置。将结论与方法并入所属正文,将交付物与可复用源码归入对应项目,更新来源引用与执行入口。
35
35
 
36
- 归集后清理本次范围内已被正文吸收或不再需要的临时文件。正式资料、历史记录与备份保持原样。
36
+ 归集后只清理本次范围内已被正文吸收或不再需要的临时文件,不删除正式资料、历史记录或备份。
37
37
 
38
38
  ## 项目结项
39
39
 
40
- 以已确认的目标和交付条件判断完成状态,核对交付物、来源引用与执行入口确实可用。满足本次用途即可结项,已知限制留在正文;可选深化与必需工作分开。
41
-
42
- 交付条件尚未满足时,记录具体缺口及其影响。完成状态、正文与证据一致后,同步项目入口和相关导航。
40
+ 按任务目标检查交付物和执行入口。满足本次用途即可结项;尚有缺口时说明剩余工作,额外建议另列。更新项目状态和相关导航,已知限制留在正文。
@@ -23,20 +23,20 @@ description: 接入或修复原生 CLI/MCP,核对认证与可用性,维护
23
23
 
24
24
  项目配置经 Pi 项目信任规则加载,同名条目可能覆盖用户级配置。修改前核对同名项,保留无关条目与已有用户级连接;迁移另按用户指定范围处理。认证使用原生登录或环境变量引用。
25
25
 
26
- 外部修改配置后,请用户在当前 Pi 会话执行 `/reload`,再用 `/mcp` 核对实际配置来源和连接状态;不要声称已代用户完成会话操作。当前工具 schema 通过原生工具搜索或 codemode 按需取得。新增权限、安装或共享配置变更先核对已有授权,已授权的操作不重复询问。
26
+ 外部修改配置后,请用户在当前 Pi 会话执行 `/reload`,再用 `/mcp` 查看配置来源和连接状态。当前工具 schema 通过原生工具搜索或 codemode 按需取得。
27
27
 
28
28
  ### dsh
29
29
 
30
- 使用当前固定版本的原生 MCP 组件、profile 设置、认证与工具发现。按其公开文档或当前 help 核对参数,不使用 Pi 的命令和 `.pi/mcp.json` 配置方式。连接可能服务于同一 profile 的多个工作区,修改前核对实际作用域及已有授权,不把 profile 配置称为工作区隔离。
30
+ 使用当前安装版本的原生 MCP 组件、profile 设置、认证与工具发现。按其公开文档或当前 help 核对参数,不使用 Pi 的命令和 `.pi/mcp.json` 配置方式。同一 profile 的连接可能供多个工作区使用,修改时留意影响范围。
31
31
 
32
- 优先复用已有连接,保留无关条目与其他工作区配置。用原生界面或可用管理能力完成已授权操作,再从当前会话核对工具、认证与连接状态;确需用户执行原生操作时,明确对应入口。工具可见不代表连接或业务调用成功,不为验证连接擅自执行写操作。
32
+ 优先复用已有连接,保留无关条目与其他工作区配置。通过原生界面或管理能力修改配置,再从当前会话检查连接和工具;需要用户操作时说明入口。
33
33
 
34
- ## 验证与手册
34
+ ## 检查与手册
35
35
 
36
- 用本次获准的查询验证所需能力,将实际接口、时间、覆盖范围与结果依据保存在所属项目。区分配置已保存、连接可用和业务已验证。
36
+ 用一个简单的只读查询检查所需能力,失败时根据实际错误排障。
37
37
 
38
38
  `providers/<id>/GUIDE.md` 面向当前使用,保留用途、原生入口、可信资料位置及适用版本。仅补充原生定义未说明、且影响正确使用的口径或限制;工具目录与参数细节从原生 help 和 schema 按需取得。
39
39
 
40
- 更新时按主题合并,用已确认且适用的新结论替换过时说明,并同步示例。验证信息只保留影响当前使用的最新结论、时点与范围,详细和历史证据引用所属项目。
40
+ 更新时按主题合并,替换过时说明并同步示例。手册保留当前用法和已知限制,需要详细记录时引用所属项目。
41
41
 
42
- `providers/AGENTS.md` 只做导航和已确认的选用约定。
42
+ `providers/AGENTS.md` 只做导航和选用约定。
package/scripts/README.md CHANGED
@@ -1,25 +1,26 @@
1
1
  # 维护脚本
2
2
 
3
- ## Python 环境准备
3
+ ## 构建
4
4
 
5
- `setup-python.py` 创建当前用户的共享 venv,按 requirements.lock 安装依赖。Personal Bot 加载后自动调用;也可独立执行,或通过 /pbot python setup 重试。
5
+ | 命令 | 作用 |
6
+ | --- | --- |
7
+ | `npm run build` | 清理 dist,编译测试模块,再构建 CLI 与 dsh 产物 |
8
+ | `npm run build:cli` | 清理 lib,编译独立安装器、Pi 检查桥接与启动保护 |
9
+ | `npm run build:dsh` | 编译 Host,将 TypeBox 与许可证纳入 lib,再构建原生 Web Loader 模块 |
6
10
 
7
- 脚本不接受解释器路径,不修改工作区资料或历史记录。路径、激活和失败处理见 [Python 说明](../python/README.md)。
11
+ 完整构建与 prepack 先构建 CLI,再构建 dsh。宿主 SDK 保持 external;dsh 编译模块引用包内 TypeBox,原模块位置保留。Pi 扩展由宿主直接加载 src。
8
12
 
9
- ## 安装 CLI 构建
13
+ ## 安装包检查
10
14
 
11
- `npm run build:cli` 调用 `build-cli.mjs`,清理生成目录 lib,再按 tsconfig.cli.json 编译独立 CLI、Pi 检查桥接和启动保护。bin/pbot.mjs 转交已生成的入口。npm test 的构建与 prepack 都执行此步骤;扩展运行仍由 Pi 加载 src 源码。消费者直接使用发布包中的 lib,无需安装开发依赖。
15
+ 先运行 `npm pack`,再将生成的 tarball 路径传给检查脚本:
12
16
 
13
- ## dsh 构建
17
+ | 命令 | 检查内容 | 环境 |
18
+ | --- | --- | --- |
19
+ | `node scripts/check-consumer.mjs <tarball>` | 独立 CLI、Pi 扩展、技能、工具与资料初始化 | CI 使用 Node.js 22.19.0,脚本安装锁定的 Pi SDK |
20
+ | `node scripts/check-dsh-consumer.mjs <tarball>` | dsh 原生插件安装、独立 CLI 与 Host 导入;检查无需单独安装 TypeBox 或宿主 SDK | Node.js 24.9+、仓库开发依赖和 PATH 中的 pnpm |
14
21
 
15
- `npm run build:dsh` 调用 `build-dsh.mjs`,由 tsconfig.dsh.json 编译 Host 与浏览器入口,再用 esbuild 将 Web 代码包装为固定版原生包根 Loader 模块。宿主 SDK 保持 external;完整 build 和 prepack 先构建 CLI、再构建 dsh,统一输出到 lib。
22
+ 脚本使用隔离的用户目录、宿主配置与工作目录,完成后清理临时目录。两项检查均不调用模型;Pi 使用预置故障目录跳过 Python 依赖下载,dsh 通过原生 schema 收集导入 Host。
16
23
 
17
- ## 发布包消费者检查
24
+ ## Python 准备
18
25
 
19
- `node scripts/check-consumer.mjs <tarball>` 在临时目录安装实际发布包,先检查独立 CLI 与宿主 SDK 缺省状态,再安装 package.json 中锁定的 Pi SDK,验证扩展、工具、技能和资料初始化。CI 使用 Node.js 22.19.0 执行,完整开发依赖、dsh 构建和全量测试仍使用 Node.js 24.9+。检查使用独立用户目录与损坏 Python 目标,不下载计算依赖或调用模型;结束后删除本次临时目录。
20
-
21
- ## 构建清理
22
-
23
- `clean-dist.mjs` 由 npm run build 调用,仅清理仓库内生成的 dist/,供 TypeScript 重新编译测试导入。
24
-
25
- 本包不提供工作区升级命令或历史转换脚本。插件安装和更新由对应宿主的原生包管理或用户维护的源码目录负责。
26
+ `setup-python.py` 按 `python/requirements.lock` 准备用户共享 venv。Personal Bot 加载时自动调用,也可运行独立脚本或 `/pbot python setup`。路径、依赖与排障见 [Python 说明](../python/README.md)。
package/src/dsh.ts CHANGED
@@ -1,4 +1,7 @@
1
+ import { join } from "node:path";
1
2
  import type { Context } from "@deepseek-ai/cordis";
3
+ import * as SkillFilesystem from "@deepseek-ai/dsh-skill-filesystem";
4
+ import { resourceRoot } from "./common/paths.js";
2
5
  import { registerDshCommand } from "./extension/dsh/command.js";
3
6
  import { registerDshManagement } from "./extension/dsh/management.js";
4
7
  import { DshWorkspaces, dshDependencies } from "./extension/dsh/workspace.js";
@@ -10,6 +13,7 @@ export const inject = ["commands", "typert", ...dshDependencies];
10
13
  export async function apply(ctx: Context) {
11
14
  const [major, minor] = process.versions.node.split(".").map(Number);
12
15
  requireThat(major! > 24 || (major === 24 && minor! >= 9), "dsh 接入需要 Node.js 24.9.0 或更新版本。");
16
+ await ctx.plugin(SkillFilesystem, { providerName: "pbot", includeDefaultRoots: false, bundledSkillDir: join(resourceRoot, "skills") });
13
17
  const workspaces = new DshWorkspaces(ctx);
14
18
  ctx.effect(() => () => workspaces.close());
15
19
  const management = registerDshManagement(ctx, workspaces);
@@ -14,6 +14,7 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
14
14
  let paused = false, epoch = 0, refreshEpoch = 0;
15
15
  let refreshing: Promise<void> | undefined;
16
16
  let controlBusySince: number | undefined;
17
+ let recoveredFailure: string | undefined;
17
18
  let failure: { key: string; error: string; recovery: "retry" | "repair" | "reload" } | undefined;
18
19
  const recordFailure = (error: unknown) => {
19
20
  const busy = (error as NodeJS.ErrnoException).code === "PBOT_CONTROL_BUSY";
@@ -67,7 +68,19 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
67
68
  if (!ctx || !lifecycle.active || paused || ctx.signal?.aborted) return;
68
69
  const instance = lifecycle.active, revision = epoch;
69
70
  if (!failure) {
70
- try { return await collectNotification(); }
71
+ try {
72
+ const message = await collectNotification();
73
+ if (message) return message;
74
+ if (!ctx || revision !== epoch || instance !== lifecycle.active || paused || ctx.signal?.aborted || failure || !recoveredFailure || controlBusySince !== undefined) return;
75
+ const delivered = deliveredCycles(ctx), key = recoveredFailure + ":recovered";
76
+ if (!delivered.has(recoveredFailure) || delivered.has(key)) return;
77
+ const now = Date.now(), sent = submitting.get(key);
78
+ if (sent !== undefined && now - sent < 5000) return;
79
+ submitting.set(key, now);
80
+ return { type: "custom_message", customType: "pbot.agent-recovered", display: true,
81
+ content: "# 子会话自动跟进已恢复\n\n后台扫描已恢复。成员状态和报告可通过 pbot_agent status/read 查看。",
82
+ details: { keys: [key], failureKey: recoveredFailure } };
83
+ }
71
84
  catch (error) { if (revision !== epoch || instance !== lifecycle.active) return; recordFailure(error); }
72
85
  }
73
86
  if (!ctx || !failure || deliveredCycles(ctx).has(failure.key)) return;
@@ -82,7 +95,7 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
82
95
  return { type: "custom_message", customType: "pbot.agent-error", display: true,
83
96
  content: ["# 子会话自动跟进故障", "", recovery,
84
97
  ...(pausePending ? ["", "团队暂停尚未确认,不能据此开始成员私人任务。"] : []), "",
85
- "尚未交付的结果未确认。需要核对成员时,用 pbot_agent list/status 查询状态、pbot_agent read 读取可用报告;结果未知不构成重新提交的依据。", "",
98
+ "用 pbot_agent list/status 查询成员状态,pbot_agent read 读取可用报告。重试前先查看原请求结果,避免重复执行。", "",
86
99
  markdownFields({ error: failure.error })].join("\n"), details: { keys: [failure.key] } };
87
100
  };
88
101
  const refreshWork = async () => {
@@ -103,7 +116,6 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
103
116
  try { instance.agents.enqueueTeam(input, instance.agentOwner); dispatching.delete(id); }
104
117
  catch (error) {
105
118
  const code = (error as NodeJS.ErrnoException).code;
106
- if (code === "PBOT_CONTROL_BUSY") break;
107
119
  if (code !== "TEAM_INPUT_STALE") throw error;
108
120
  dispatching.delete(id); instance.teams.pauseMember(input.agentId, "安排失效;未自动重发");
109
121
  }
@@ -119,6 +131,7 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
119
131
  }
120
132
  if (ctx.mode === "tui") ctx.ui.setWidget("pbot-agents", values.some(v => ["starting", "busy", "stopping"].includes(v.state)) ? (_tui, theme) => agentBoard(values, theme) : undefined);
121
133
  if (!pausePending) {
134
+ if (failure) recoveredFailure = failure.key;
122
135
  failure = undefined; controlBusySince = undefined;
123
136
  if (ctx.hasUI) ctx.ui.setStatus("pbot-agents-error", undefined);
124
137
  }
@@ -150,7 +163,7 @@ export function registerAgentEvents(pi: ExtensionAPI, lifecycle: Lifecycle, conf
150
163
  return;
151
164
  };
152
165
  pi.on("session_start", (_event, context) => {
153
- epoch++; ctx = context; paused = false; pausePending = false; controlBusySince = undefined; failure = undefined; confirmations.resume(); submitting.clear(); dispatching.clear();
166
+ epoch++; ctx = context; paused = false; pausePending = false; controlBusySince = undefined; failure = undefined; recoveredFailure = undefined; confirmations.resume(); submitting.clear(); dispatching.clear();
154
167
  if (timer) clearInterval(timer);
155
168
  if (lifecycle.active) { lifecycle.active.agentOwner = agentOwner(ctx); timer = setInterval(() => { void refresh(); }, 1000); timer.unref(); }
156
169
  return refresh();
@@ -26,7 +26,7 @@ export function registerCommand(pi: ExtensionAPI, lifecycle: Lifecycle, confirma
26
26
  const instance = lifecycle.get();
27
27
  if (command === "init" && !argument) {
28
28
  await ctx.waitForIdle(); instance.check(); await prepareWorkspace(instance.root);
29
- ctx.ui.notify("资料目录已准备。", "info"); return;
29
+ ctx.ui.notify("资料目录已准备。新建的 AGENTS.md 在 /reload 或下次启动后由 Pi 加载。", "info"); return;
30
30
  }
31
31
  if (command === "status" && !argument) {
32
32
  const python = instance.pythonEnvironment.status;
@@ -90,7 +90,7 @@ export class DshAgentEvents {
90
90
  let value: { keys: string[]; content: string } | undefined;
91
91
  if (this.#error && this.#errorKey) {
92
92
  if (!known.has(this.#errorKey)) value = { keys: [this.#errorKey], content: "# 协作扫描故障\n\n" + this.#error +
93
- "\n\n先核对控制记录和原生报告;结果未知不是重跑许可。" +
93
+ "\n\n先查看控制记录和原生报告,再决定是否重试,避免重复执行。" +
94
94
  (this.#recovery === "reload" ? "团队服务已停用;修复文件后还需重载服务端 personal-bot 插件。" :
95
95
  this.#recovery === "retry" ? "控制锁持续占用,后台会继续重试;不会自动删除锁或重新提交任务。" : "报告或控制记录读取失败,修复后后台会重试扫描。") +
96
96
  (this.#pausePending ? "\n\n团队暂停尚未确认,不能据此开始成员私人任务。" : "") };
@@ -25,6 +25,7 @@ export class DshAgentRuntime {
25
25
  #answer = "";
26
26
  #hasAnswer = false;
27
27
  #activeTeam: TeamLink | undefined;
28
+ #teamBriefed = false;
28
29
  #pendingPause: string | undefined;
29
30
  #pump: Promise<void> | undefined;
30
31
  #timer: ReturnType<typeof setInterval> | undefined;
@@ -98,12 +99,11 @@ export class DshAgentRuntime {
98
99
  requireThat(link, "团队关联不可读取。");
99
100
  const seen = this.#seen.get(link.team.teamId) ?? new Set<number>();
100
101
  const context = createTeamContext(link, seen);
101
- if (!context) return decision;
102
- return { ...decision, messages: [...decision.messages, createUserMessage({ content: [{ type: "text", text: context.content }],
103
- source: { kind: "pbot-team-context", form: "relay", teamId: link.team.teamId, seqs: context.seqs } })] };
102
+ if (this.#teamBriefed && !context) return decision;
103
+ const text = [...(this.#teamBriefed ? [] : [teamBrief(link)]), ...(context ? [context.content] : [])].join("\n\n");
104
+ return { ...decision, messages: [...decision.messages, createUserMessage({ content: [{ type: "text", text }],
105
+ source: { kind: "pbot-team-context", form: "relay", teamId: link.team.teamId, seqs: context?.seqs ?? [] } })] };
104
106
  });
105
- this.ctx.systemPrompt.section({ name: "pbot:team", order: 600, interpolate: false,
106
- text: () => this.#activeTeam && !this.humanPending() && this.execution.current?.source === "parent" ? teamBrief(this.#activeTeam) : "" });
107
107
  this.execution.save({ state: "idle", activity: "空闲" });
108
108
  this.ctx.effect(() => () => this.stop());
109
109
  }
@@ -128,7 +128,7 @@ export class DshAgentRuntime {
128
128
  private async event(event: SessionEvent) {
129
129
  if (!this.current()) return;
130
130
  if (event.type === "turn/start") {
131
- this.#turnEvents = []; this.#answer = ""; this.#hasAnswer = false;
131
+ this.#turnEvents = []; this.#answer = ""; this.#hasAnswer = false; this.#teamBriefed = false;
132
132
  this.execution.begin();
133
133
  }
134
134
  if (this.execution.current) this.#turnEvents.push(event);
@@ -152,6 +152,7 @@ export class DshAgentRuntime {
152
152
  if (event.data.source.kind === "user") this.execution.humanInput();
153
153
  if (event.data.source.kind === "pbot-team-context") {
154
154
  const { teamId, seqs } = event.data.source, seen = this.#seen.get(teamId) ?? new Set<number>();
155
+ if (this.#activeTeam?.team.teamId === teamId) this.#teamBriefed = true;
155
156
  for (const seq of seqs) seen.add(seq); this.#seen.set(teamId, seen);
156
157
  }
157
158
  }
@@ -25,19 +25,24 @@ export function registerEvents(pi: ExtensionAPI, lifecycle: Lifecycle, confirmat
25
25
  else throw error;
26
26
  }
27
27
  });
28
- pi.on("resources_discover", () => lifecycle.active ? { skillPaths: [join(resourceRoot, "skills")] } : {});
29
28
  pi.on("before_agent_start", async (event, ctx) => {
30
29
  const instance = lifecycle.active;
31
30
  if (!instance) return;
32
31
  instance.cancelWork();
33
- const role = await agentText("coordinator", instance.root);
34
- const workspace = await readFile(join(resourceRoot, "prompts/workspace.md"), "utf8");
35
- instance.check();
36
- event.systemPromptOptions.sections.pbot_workspace = workspace;
37
- event.systemPromptOptions.sections.pbot_environment = JSON.stringify({
38
- workspaceRoot: instance.root, cwd: ctx.cwd, resourceRoot, python: instance.pythonEnvironment.status,
39
- });
40
- event.systemPromptOptions.sections.pbot = role;
32
+ try {
33
+ const role = await agentText("coordinator", instance.root);
34
+ const workspace = await readFile(join(resourceRoot, "prompts/workspace.md"), "utf8");
35
+ instance.check();
36
+ const { state, directory } = instance.pythonEnvironment.status;
37
+ Object.assign(event.systemPromptOptions.sections, {
38
+ pbot_workspace: workspace,
39
+ pbot_environment: JSON.stringify({ workspaceRoot: instance.root, resourceRoot, python: { state, directory } }),
40
+ pbot: role,
41
+ });
42
+ } catch (error) {
43
+ // Pi reports hook errors but continues preparation unless the run is explicitly aborted.
44
+ ctx.abort(); throw error;
45
+ }
41
46
  });
42
47
  pi.on("input", () => { lifecycle.active?.cancelWork(); });
43
48
  pi.on("agent_end", () => { lifecycle.active?.cancelWork(); });
@@ -134,7 +134,7 @@ export default function subagent(pi: ExtensionAPI) {
134
134
  pi.registerTool({ ...teamMessageTool(teams, { check: ensure }, () => ({
135
135
  kind: "member", agentId: id, generation,
136
136
  ...(activeTeam && execution.current?.source === "parent" ? { requestId: activeTeam.request.requestId } : {}),
137
- })), namespace: { name: "pbot", description: "团队公共通信,不提供管理或递归委派。" } });
137
+ })), namespace: { name: "pbot", description: "团队公共通信。" } });
138
138
  pi.on("resources_discover", async () => {
139
139
  if (d.options.noSkills) return {};
140
140
  const skillPaths = [join(resourceRoot, "skills")];
@@ -189,22 +189,30 @@ export default function subagent(pi: ExtensionAPI) {
189
189
  }
190
190
  });
191
191
  pi.on("before_agent_start", async (event, context) => {
192
- ctx = context; ensure(); bindSession();
193
- delete event.systemPromptOptions.sections.pbot_team;
194
- requireThat(python, "子会话计算环境状态不可用。");
195
- event.systemPromptOptions.sections.pbot_environment = JSON.stringify({
196
- workspaceRoot: d.workspace, cwd: d.cwd, resourceRoot, python: python.status,
197
- });
198
- event.systemPromptOptions.sections.pbot_workspace = await readFile(join(resourceRoot, "prompts/workspace.md"), "utf8");
199
- event.systemPromptOptions.sections.pbot_child = await agentText("general-purpose", d.workspace);
200
- if (activeTeam && !execution.interactive && execution.current?.source !== "mixed") {
201
- const link = teams.links().get(activeTeam.request.requestId);
202
- requireThat(link, "团队关联不可读取。");
203
- event.systemPromptOptions.sections.pbot_team = teamBrief(link);
204
- const message = teamContext(link, context, link.request.throughSeq);
205
- if (message) return { message };
192
+ ctx = context;
193
+ try {
194
+ ensure(); bindSession();
195
+ requireThat(python, "子会话计算环境状态不可用。");
196
+ const workspace = await readFile(join(resourceRoot, "prompts/workspace.md"), "utf8");
197
+ const role = await agentText("general-purpose", d.workspace);
198
+ let brief: string | undefined, message;
199
+ if (activeTeam && !execution.interactive && execution.current?.source !== "mixed") {
200
+ const link = teams.links().get(activeTeam.request.requestId);
201
+ requireThat(link, "团队关联不可读取。");
202
+ brief = teamBrief(link);
203
+ message = teamContext(link, context, link.request.throughSeq);
204
+ }
205
+ const { state, directory } = python.status;
206
+ Object.assign(event.systemPromptOptions.sections, {
207
+ pbot_environment: JSON.stringify({ workspaceRoot: d.workspace, resourceRoot, python: { state, directory } }),
208
+ pbot_workspace: workspace, pbot_child: role,
209
+ });
210
+ if (brief) event.systemPromptOptions.sections.pbot_team = brief;
211
+ else delete event.systemPromptOptions.sections.pbot_team;
212
+ return message ? { message } : undefined;
213
+ } catch (error) {
214
+ context.abort(); throw error;
206
215
  }
207
- return;
208
216
  });
209
217
  pi.on("turn_end", (event, context) => {
210
218
  if (!activeTeam || execution.current?.source !== "parent" || event.outcome === "aborted") return;
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import type { Context } from "@deepseek-ai/cordis";
4
4
  import type { Agent } from "@deepseek-ai/dsh-agent";
5
- import { FileSystemSkillProvider } from "@deepseek-ai/dsh-skill-filesystem";
5
+ import type {} from "@deepseek-ai/dsh-skill";
6
6
  import type {} from "@deepseek-ai/dsh-working-directory";
7
7
  import type {} from "@deepseek-ai/dsh-tools";
8
8
  import { agentText } from "../agent-resources.js";
@@ -10,29 +10,32 @@ import { resourceRoot } from "../../common/paths.js";
10
10
  import { requireThat } from "../../common/validation.js";
11
11
  import type { AgentOptions } from "../../model/agent.js";
12
12
 
13
- export async function dshResources(ctx: Context, agent: Agent, root: string,
13
+ export async function dshResources(ctx: Context, _agent: Agent, root: string,
14
14
  role: "coordinator" | "general-purpose", python: () => { state: string; directory?: string; error?: string }, check: () => void) {
15
- let text = await agentText(role, root);
16
- const workspace = await readFile(join(resourceRoot, "prompts/workspace.md"), "utf8");
15
+ const read = () => Promise.all([agentText(role, root), readFile(join(resourceRoot, "prompts/workspace.md"), "utf8")]);
16
+ const [text, workspace] = await read();
17
17
  check();
18
- ctx.systemPrompt.section({ name: "pbot:role", order: 1, text: () => text, interpolate: false });
18
+ ctx.systemPrompt.section({ name: "pbot:role", order: 1, text, interpolate: false });
19
19
  ctx.systemPrompt.section({ name: "pbot:workspace", order: 2, text: workspace, interpolate: false });
20
20
  ctx.systemPrompt.context({ name: "pbot:environment", order: 300, required: true, interpolate: false,
21
- text: () => JSON.stringify({ host: "dsh", workspaceRoot: root, cwd: ctx.workingDirectory.get(agent.session), resourceRoot, python: python() }) });
22
- ctx.skills.registerProvider(control => new FileSystemSkillProvider(ctx, control, {
23
- providerName: "pbot", includeDefaultRoots: false, customSkillDirs: [join(root, ".agents/skills")],
24
- bundledSkillDir: join(resourceRoot, "skills"), watch: false,
25
- }));
26
- ctx.on("agent/pre-step", async (_payload, next) => {
27
- check(); text = await agentText(role, root); check();
28
- return next();
21
+ text: () => {
22
+ const { state, directory } = python();
23
+ return JSON.stringify({ workspaceRoot: root, resourceRoot, python: { state, ...(directory ? { directory } : {}) } });
24
+ } });
25
+ ctx.on("system-prompt/assemble", async (_assembly, _context, next) => {
26
+ check();
27
+ const [roleText, workspaceText] = await read();
28
+ check();
29
+ const assembly = await next();
30
+ return { ...assembly, sections: assembly.sections.map(section => section.name === "pbot:role" ? { ...section, text: roleText }
31
+ : section.name === "pbot:workspace" ? { ...section, text: workspaceText } : section) };
29
32
  });
30
33
  }
31
34
 
32
35
  /** Map the expert's native loadout; this is not isolation from Bash, CLI, or MCP. */
33
36
  export function dshExpertTools(ctx: Context, agent: Agent, options: AgentOptions) {
34
37
  const names = new Set(ctx.tools.definitions(agent).map(tool => tool.name));
35
- const forbidden = new Set(["pbot_agent", "pbot_team", "pbot_history", "subagent", "list_subagent_models", "list_agents", "send_message", "interrupt_agent"]);
38
+ const forbidden = new Set(["pbot_agent", "pbot_team", "pbot_history", "subagent", "subagent_fork", "list_subagent_models", "list_agents", "send_message", "interrupt_agent"]);
36
39
  if (options.noSkills) forbidden.add("skill");
37
40
  if (options.tools) {
38
41
  for (const name of options.tools) requireThat((names.has(name) || name === "pbot_team_message") && !forbidden.has(name), "专家工具不可用:" + name);
@@ -4,6 +4,7 @@ import { SessionId } from "@deepseek-ai/dsh-session";
4
4
  import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
5
5
  import type { SessionHeader } from "@deepseek-ai/dsh-session";
6
6
  import type {} from "@deepseek-ai/dsh-session-persistence";
7
+ import type {} from "@deepseek-ai/dsh-agent-preset-registry";
7
8
  import type { AgentDescriptor } from "../../model/agent.js";
8
9
  import { requireThat } from "../../common/validation.js";
9
10
 
@@ -37,6 +38,7 @@ export async function openDshAgent(ctx: Context, d: AgentDescriptor, parent: Age
37
38
  signal.throwIfAborted();
38
39
  const compose: AgentSetup = async (agentCtx, agent) => {
39
40
  validateHeader(d, agent.session.header);
41
+ agentCtx.get("agentPresets")?.composeFrom(agentCtx, parent.ctx);
40
42
  if (resume) agent.inbox.clear();
41
43
  const commit = await setup(agentCtx, agent);
42
44
  await flushDshAgent(agent);
@@ -44,20 +44,22 @@ export async function inspectPackages(root: string, workspace: string): Promise<
44
44
  for (const pkg of manager.listConfiguredPackages()) {
45
45
  const entries = (pkg.scope === "project" ? settings.getProjectSettings() : settings.getGlobalSettings()).packages ?? [];
46
46
  const entry = entries.find(item => sourceOf(item) === pkg.source);
47
- let name = npmName(pkg.source), version: string | undefined;
47
+ let name = npmName(pkg.source), version: string | undefined, singleFile = false;
48
48
  if (pkg.installedPath) {
49
- let file = pkg.installedPath;
50
- if ((await stat(file)).isDirectory()) file = join(file, "package.json");
51
- else file = ""; // Single-file extensions have no npm identity.
52
- if (file) {
49
+ const info = await stat(pkg.installedPath);
50
+ singleFile = info.isFile(); // An existing standalone extension has no npm identity.
51
+ if (info.isDirectory()) {
53
52
  try {
54
- const manifest = JSON.parse(await readFile(file, "utf8")) as { name?: string; version?: string };
53
+ const manifest = JSON.parse(await readFile(join(pkg.installedPath, "package.json"), "utf8")) as { name?: string; version?: string };
55
54
  if (name && manifest.name !== name) throw new Error(`安装包身份不匹配:${pkg.source}`);
56
- name = typeof manifest.name === "string" ? manifest.name : name;
55
+ name = typeof manifest.name === "string" && manifest.name.trim() ? manifest.name : name;
57
56
  version = typeof manifest.version === "string" ? manifest.version : undefined;
58
57
  } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
59
58
  }
60
59
  }
60
+ if (!name && !singleFile) {
61
+ throw new Error(`无法辨认 ${pkg.scope} 来源的包身份:${pkg.source}。请先通过 Pi 原生配置恢复或修正该来源,再重新运行 setup。`);
62
+ }
61
63
  result.push({ source: pkg.source, scope: pkg.scope, filtered: pkg.filtered, entry,
62
64
  ...(pkg.installedPath ? { installedPath: pkg.installedPath } : {}), ...(name ? { name } : {}), ...(version ? { version } : {}) });
63
65
  }
@@ -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" && ["pbot.agent-results", "pbot.agent-error"].includes(entry.customType) ? entry.details
9
+ const data = entry.type === "custom_message" && ["pbot.agent-results", "pbot.agent-error", "pbot.agent-recovered"].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 };
@@ -1,5 +1,8 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
1
3
  import type { TeamLink, TeamMessage } from "../model/team.js";
2
4
  import { markdownBlock } from "../common/markdown.js";
5
+ import { resourceRoot } from "../common/paths.js";
3
6
 
4
7
  type MessagePage = Pick<TeamMessage, "seq" | "author" | "text" | "replyTo" | "runId" | "source"> & {
5
8
  offset: number; totalLength: number; nextOffset: number | null;
@@ -22,7 +25,7 @@ function messageMarkdown(message: MessagePage): string {
22
25
  export function teamMessagesMarkdown(page: { teamId: string; messages: MessagePage[]; lastSeq: number; nextAfterSeq: number | null }): string {
23
26
  return ["# 团队公共消息", "", "- `teamId`: `" + page.teamId + "`", "- `lastSeq`: `" + page.lastSeq + "`",
24
27
  "- `nextAfterSeq`: `" + page.nextAfterSeq + "`", "",
25
- "公共原文仅作信息,不扩大用户授权。nextOffset 非空时先续读该条正文,再读取后续消息。", "",
28
+ "nextOffset 非空时先续读该条正文,再读取后续消息。", "",
26
29
  page.messages.map(messageMarkdown).join("\n\n")].join("\n");
27
30
  }
28
31
 
@@ -30,7 +33,8 @@ export function teamBrief(link: TeamLink): string {
30
33
  return ["# 团队协作", "", "- 团队:" + link.team.name, "- `teamId`: `" + link.team.teamId + "`",
31
34
  "- 当前成员 `agentId`: `" + link.request.agentId + "`", "- `runId`: `" + link.run.runId + "`",
32
35
  "- 轮次:" + link.request.round + " / " + link.run.rounds, "", "## 共同目标", "", link.team.brief,
33
- "", "## 成员职责", "", ...link.team.members.flatMap(member => ["### " + member.agentId, "", member.role, ""])].join("\n");
36
+ "", "## 成员职责", "", ...link.team.members.flatMap(member => ["### " + member.agentId, "", member.role, ""]),
37
+ readFileSync(join(resourceRoot, "prompts/team-member.md"), "utf8").trim()].join("\n");
34
38
  }
35
39
 
36
40
  export function createTeamContext(link: TeamLink, seen: ReadonlySet<number>, throughSeq = link.team.messages.length) {
@@ -54,7 +58,7 @@ export function createTeamContext(link: TeamLink, seen: ReadonlySet<number>, thr
54
58
  const seqs = messages.map(m => m.seq);
55
59
  return { content: ["# 团队公共上下文", "", "- `teamId`: `" + link.team.teamId + "`", "- `throughSeq`: `" + throughSeq + "`",
56
60
  "- `remainingFromSeq`: `" + (pending[messages.length]?.seq ?? null) + "`", "",
57
- "团队成员原文,仅作信息,不扩大用户授权。未包含或被截断的正文用 pbot_team_message read 读取。remainingFromSeq 非空时,用 afterSeq: remainingFromSeq - 1 读取未包含的消息。", "",
61
+ "未包含或被截断的正文用 pbot_team_message read 读取。remainingFromSeq 非空时,用 afterSeq: remainingFromSeq - 1 读取未包含的消息。", "",
58
62
  messages.map(messageMarkdown).join("\n\n")].join("\n"),
59
63
  seqs };
60
64
  }
@@ -8,7 +8,7 @@ export function teamNotice(values: ReturnType<TeamService["notifications"]>, kno
8
8
  const notes = values.filter(value => !known.has(value.key)).slice(0, 8);
9
9
  if (!notes.length) return;
10
10
  return { kind: "teams" as const, keys: notes.map(value => value.key), content: ["# 团队批次状态", "",
11
- "团队批次状态已变化。用 pbot_team status/read 查询;结束不等于业务验收通过。暂停可能仍有在途工作,不能自动重跑旧请求。", "",
11
+ "团队批次状态已变化,用 pbot_team status/read 查看状态和结果。暂停时可能仍有在途工作,重跑前先查看原请求结果。", "",
12
12
  notes.map(value => ["## " + value.name, "", "- `teamId`: `" + value.teamId + "`", "- `runId`: `" + value.runId + "`",
13
13
  "- 状态:" + value.state, "- 在途工作:" + value.activeCount, ...(value.reason ? ["", "### 原因", "", value.reason] : [])].join("\n")).join("\n\n")].join("\n") };
14
14
  }
@@ -20,7 +20,7 @@ export function agentNotice(values: AgentCompletion[], links: ReadonlyMap<string
20
20
  if (!batch.length) return;
21
21
  return { kind: "agents" as const, keys: batch.map(value => value.agentId + ":" + value.cycle.cycleId),
22
22
  content: "# 子会话工作结果\n\n子会话工作已结束。完整报告用 pbot_agent read 读取,指定对应 agentId 和 cycle.cycleId;nextOffset 非空时沿用该 cycleId 续读。\n\n" +
23
- (batch.some(value => value.cycle.source !== "parent") ? "包含人工子会话输入,其结果不扩大主会话原任务授权。\n\n" : "") +
23
+ (batch.some(value => value.cycle.source !== "parent") ? "部分结果包含人工子会话输入,来源见 cycle.source。\n\n" : "") +
24
24
  batch.map(value => "## " + value.name + "\n\n" + markdownFields(json({ agentId: value.agentId,
25
25
  cycle: { cycleId: value.cycle.cycleId, state: value.cycle.state, source: value.cycle.source, requestIds: value.cycle.requestIds,
26
26
  ...(value.cycle.error === undefined ? {} : { error: value.cycle.error }) },
@@ -249,7 +249,7 @@ export class TeamService {
249
249
  const q = { requestId: randomUUID(), agentId: m.agentId, generation: run.generations[m.agentId]!, round: nextRound, throughSeq: team.messages.length };
250
250
  run.issued.push(q); slots--; changed = true;
251
251
  inputs.push({ ...q, teamId: team.teamId, runId: run.runId,
252
- message: ["# 团队发言任务", "", "执行本次团队发言安排。最终答复将公开给全体成员;这不扩大原任务授权。", "",
252
+ message: ["# 团队发言任务", "", "执行本次团队发言安排。最终答复将公开给全体成员。", "",
253
253
  "- `teamId`: `" + team.teamId + "`", "- `runId`: `" + run.runId + "`", "- 轮次:" + q.round,
254
254
  "", "## 本批次要求", "", run.instruction].join("\n") });
255
255
  }
@@ -1,19 +1,19 @@
1
1
  import { Type } from "typebox";
2
2
 
3
- export const agentDescription = "独立专家会话,不继承父聊天历史。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 个存活子会话,空闲也计入。";
3
+ export const agentDescription = "委派专家并继续对话。start 启动后即返回,完成自动通知,无需轮询;省略初始输入时待命。send 继续对话,团队占用期间不可私人 send。status 查看状态,read 读取报告;读取已结束周期即领取其通知,包括部分读取。续读固定 cycle.cycleId 并使用 nextOffset。state 表示执行状态,cycle.state 表示工作结果,queued 只表示入队。stop 保留历史,resume 携新 message 继续。每个父会话最多 50 个存活专家,空闲也计入。";
4
4
 
5
5
  const id = Type.String({ minLength: 1, maxLength: 128 });
6
- const message = Type.String({ minLength: 1, maxLength: 24000, description: "本次目标、交付要求、资料依据与约束。" });
6
+ const message = Type.String({ minLength: 1, maxLength: 24000, description: "本次目标、相关资料、约束和交付要求。" });
7
7
  const name = Type.String({ minLength: 1, maxLength: 64, pattern: "^[^\\r\\n\\x00-\\x1f]+$" });
8
8
  export const agentStartOptions = {
9
9
  name: Type.Optional(name), message: Type.Optional(message),
10
10
  files: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { maxItems: 32, description: "随初始消息读取的文件;相对路径基于原工作区根目录。" })),
11
- prompts: Type.Optional(Type.Array(message, { maxItems: 32, description: "与 message 按顺序合并为同一初始消息,不是多个执行轮次。" })),
11
+ prompts: Type.Optional(Type.Array(message, { maxItems: 32, description: "与 message 按顺序合并为同一初始消息。" })),
12
12
  provider: Type.Optional(Type.String({ ...id, description: "Pi 模型提供方,默认继承主会话。" })),
13
- model: Type.Optional(Type.String({ ...id, description: "子进程可用的 Pi 模型 ID,默认继承主会话。" })),
13
+ model: Type.Optional(Type.String({ ...id, description: "Pi 模型 ID,默认继承主会话。" })),
14
14
  thinking: Type.Optional(Type.Union([Type.Literal("off"), Type.Literal("minimal"), Type.Literal("low"), Type.Literal("medium"), Type.Literal("high"), Type.Literal("xhigh"), Type.Literal("max")], { description: "默认继承主会话思考级别。" })),
15
15
  cwd: Type.Optional(Type.String({ minLength: 1, description: "执行目录,相对原工作区根目录;省略时使用工作区根目录。" })),
16
- tools: Type.Optional(Type.Array(id, { minItems: 1, description: "按 Pi 原生规则选择工具,省略时沿用原生默认工具;MCP 工具遵循原生暴露设置。不提供递归委派;这不是宿主隔离。" })),
16
+ tools: Type.Optional(Type.Array(id, { minItems: 1, description: "按 Pi 规则选择工具,省略时使用默认工具;MCP 工具按当前配置提供。" })),
17
17
  noExtensions: Type.Optional(Type.Boolean({ description: "默认 true,不发现其他扩展。" })),
18
18
  noSkills: Type.Optional(Type.Boolean({ description: "关闭技能发现,默认 false。" })),
19
19
  noPromptTemplates: Type.Optional(Type.Boolean({ description: "关闭提示模板发现,默认 false。" })),
@@ -42,7 +42,7 @@ export const agentActionSchema = Type.Union([
42
42
  ]);
43
43
 
44
44
  const cycle = Type.Object({ cycleId: Type.String(), startedAt: Type.Number(), finishedAt: Type.Optional(Type.Number()),
45
- state: Type.Union([Type.Literal("running"), Type.Literal("completed"), Type.Literal("failed"), Type.Literal("cancelled"), Type.Literal("interrupted")], { description: "本周期的工作结果;completed 仍须核验实际交付。" }),
45
+ state: Type.Union([Type.Literal("running"), Type.Literal("completed"), Type.Literal("failed"), Type.Literal("cancelled"), Type.Literal("interrupted")], { description: "本周期的工作结果,正文用 read 读取。" }),
46
46
  requestIds: Type.Array(Type.String()),
47
47
  source: Type.Union([Type.Literal("parent"), Type.Literal("interactive"), Type.Literal("mixed")]), error: Type.Optional(Type.String()),
48
48
  }, { additionalProperties: false });
@@ -52,7 +52,7 @@ export const agentViewSchema = Type.Object({ agentId: Type.String(), name: Type.
52
52
  activity: Type.String(), cycle: Type.Optional(cycle), error: Type.Optional(Type.String()),
53
53
  requestId: Type.Optional(Type.String()), messageStatus: Type.Optional(Type.Literal("queued", { description: "消息已入队,不代表模型已阅读或完成。" })),
54
54
  cycles: Type.Optional(Type.Array(Type.Object({ cycleId: Type.String(), state: Type.String() }), { description: "仅 read 返回:可读取的周期索引。" })),
55
- report: Type.Optional(Type.String({ description: "仅 read 返回:选定周期的报告正文片段。已结束周期的读取结果持久化后领取该周期通知,即使本页未读完。" })),
55
+ report: Type.Optional(Type.String({ description: "仅 read 返回:选定周期的报告正文片段。" })),
56
56
  totalLength: Type.Optional(Type.Integer({ description: "仅 read 返回:完整报告的 UTF-16 字符数。" })),
57
57
  nextOffset: Type.Optional(Type.Union([Type.Integer(), Type.Null()], { description: "仅 read 返回:null 表示本报告已读完;否则以相同 agentId、cycle.cycleId 和此 offset 继续。" })),
58
58
  }, { additionalProperties: false });