personal-bot 0.2.8 → 0.2.10

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 +70 -119
  3. package/README.zh-CN.md +73 -116
  4. package/docs/README.md +22 -19
  5. package/docs/agent-collaboration.md +10 -12
  6. package/docs/architecture-guidelines.md +2 -2
  7. package/docs/capability-design.md +4 -12
  8. package/docs/dsh.md +32 -96
  9. package/docs/file-storage.md +8 -5
  10. package/docs/history.md +5 -12
  11. package/docs/installation-design.md +56 -198
  12. package/docs/model-context.md +23 -37
  13. package/docs/model-instruction-guidelines.md +2 -2
  14. package/docs/release.md +5 -5
  15. package/docs/technical-design.md +16 -12
  16. package/docs/workspace-configuration.md +55 -0
  17. package/lib/client.js +28 -3
  18. package/lib/common/typebox.LICENSE +23 -0
  19. package/lib/common/typebox.js +4740 -0
  20. package/lib/dsh.js +0 -5
  21. package/lib/extension/dsh/command.js +15 -11
  22. package/lib/extension/dsh/history.js +1 -1
  23. package/lib/extension/dsh/management.d.ts +4 -0
  24. package/lib/extension/dsh/management.js +21 -6
  25. package/lib/extension/dsh/workspace.d.ts +7 -1
  26. package/lib/extension/dsh/workspace.js +68 -1
  27. package/lib/extension/setup-cli.js +1 -1
  28. package/lib/integrations/dsh/skills.d.ts +4 -0
  29. package/lib/integrations/dsh/skills.js +27 -0
  30. package/lib/model/history.d.ts +1 -0
  31. package/lib/model/workspace-config.d.ts +9 -0
  32. package/lib/model/workspace-config.js +1 -0
  33. package/lib/services/history/history-service.d.ts +1 -2
  34. package/lib/services/history/history-service.js +27 -40
  35. package/lib/services/workspace/workspace-config.d.ts +7 -0
  36. package/lib/services/workspace/workspace-config.js +50 -0
  37. package/lib/services/workspace/workspace-preparation.d.ts +1 -0
  38. package/lib/services/workspace/workspace-preparation.js +8 -0
  39. package/lib/storage/history-files.d.ts +5 -1
  40. package/lib/storage/history-files.js +68 -15
  41. package/lib/tools/agents/contract.js +1 -1
  42. package/lib/tools/agents/dsh-contract.js +1 -1
  43. package/lib/tools/history/contract.js +1 -1
  44. package/lib/tools/teams/contract.js +1 -1
  45. package/lib/ui/dsh/management.js +27 -3
  46. package/npm-shrinkwrap.json +10 -3
  47. package/package.json +102 -31
  48. package/python/README.md +6 -6
  49. package/resources/prompts/workspace.md +3 -3
  50. package/scripts/README.md +16 -15
  51. package/src/dsh.ts +0 -5
  52. package/src/extension/command.ts +25 -8
  53. package/src/extension/dsh/command.ts +13 -8
  54. package/src/extension/dsh/history.ts +2 -2
  55. package/src/extension/dsh/management.ts +21 -5
  56. package/src/extension/dsh/workspace.ts +62 -5
  57. package/src/extension/events.ts +18 -1
  58. package/src/extension/lifecycle.ts +9 -2
  59. package/src/extension/setup-cli.ts +1 -1
  60. package/src/extension/subagent-entry.ts +4 -1
  61. package/src/integrations/dsh/skills.ts +30 -0
  62. package/src/integrations/pi/skills.ts +52 -0
  63. package/src/model/history.ts +1 -0
  64. package/src/model/workspace-config.ts +10 -0
  65. package/src/pbot.ts +2 -3
  66. package/src/services/history/history-service.ts +15 -25
  67. package/src/services/workspace/workspace-config.ts +49 -0
  68. package/src/services/workspace/workspace-preparation.ts +8 -0
  69. package/src/storage/history-files.ts +53 -15
  70. package/src/ui/dsh/management.ts +20 -3
  71. package/docs/dsh-implementation-plan.md +0 -160
  72. package/docs/dsh-support-proposal.md +0 -298
  73. package/docs/dsh.en.md +0 -333
  74. package/docs/subagent-upgrade-proposal.md +0 -357
  75. package/docs/validation.md +0 -189
@@ -1,12 +1,13 @@
1
- import { existsSync, mkdirSync, readdirSync, rmdirSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { object } from "../common/json.js";
1
+ import { existsSync, mkdirSync, readdirSync, rmdirSync, unlinkSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { canonical, object } from "../common/json.js";
4
4
  import { requireThat } from "../common/validation.js";
5
- import { readJsonFile, writeJsonFile } from "./atomic-json.js";
5
+ import { internalPath, readJsonFile, syncDirectory, writeJsonFile } from "./atomic-json.js";
6
6
  import { fileId } from "./file-access.js";
7
7
  const strings = (value) => Array.isArray(value) && value.every(item => typeof item === "string");
8
8
  function record(value) {
9
- requireThat(object(value) && typeof value.id === "string" && typeof value.sessionId === "string" &&
9
+ requireThat(object(value) && (value.host === "pi" || value.host === "dsh") &&
10
+ typeof value.id === "string" && typeof value.sessionId === "string" &&
10
11
  typeof value.name === "string" && Array.isArray(value.nodes), "历史索引无效。");
11
12
  for (const node of value.nodes)
12
13
  requireThat(object(node) && typeof node.id === "string" &&
@@ -21,25 +22,77 @@ function record(value) {
21
22
  export class HistoryFiles {
22
23
  directory;
23
24
  host;
25
+ #warnings = [];
24
26
  constructor(directory, host = "pi") {
25
27
  this.directory = directory;
26
28
  this.host = host;
27
29
  }
28
- list(workspace) {
30
+ get warnings() { return this.#warnings; }
31
+ list() {
32
+ this.#warnings = [];
29
33
  if (!existsSync(this.directory))
30
34
  return [];
31
- return readdirSync(this.directory).filter(name => name.endsWith(".json"))
32
- .map(name => readJsonFile(this.directory, name, value => {
33
- const result = record(value);
34
- requireThat(this.host !== "dsh" || result.id.startsWith("dsh-"), "dsh 历史根 ID 缺少来源前缀。");
35
- return result;
36
- }))
37
- .filter(value => workspace === undefined || ("workspace" in value && value.workspace === workspace));
35
+ const records = [];
36
+ for (const name of readdirSync(this.directory).filter(name => name.endsWith(".json"))) {
37
+ try {
38
+ const value = readJsonFile(this.directory, name, record);
39
+ requireThat(name === fileId(value.id) + ".json", "历史文件名与根 ID 不匹配。");
40
+ if (value.host === this.host)
41
+ records.push(value);
42
+ }
43
+ catch (error) {
44
+ this.#warnings.push(join(this.directory, name) + ": " + String(error));
45
+ }
46
+ }
47
+ return records;
48
+ }
49
+ /** Publish each legacy archive with its directory's host before removing the old copy. */
50
+ migrate(root) {
51
+ const directory = join(root, "." + this.host, "pbot/history");
52
+ if (!existsSync(directory))
53
+ return;
54
+ this.control(() => {
55
+ if (!existsSync(directory))
56
+ return;
57
+ const legacyLock = join(directory, ".write");
58
+ mkdirSync(legacyLock, { mode: 0o700 });
59
+ try {
60
+ for (const name of readdirSync(directory).filter(name => name.endsWith(".json"))) {
61
+ const source = internalPath(directory, name), target = internalPath(this.directory, name);
62
+ requireThat(source !== target, "新旧历史目录指向同一位置。");
63
+ const value = readJsonFile(directory, name, value => {
64
+ requireThat(object(value) && (value.host === undefined || value.host === this.host), "旧历史归档与来源目录不匹配。");
65
+ return record({ ...value, host: this.host });
66
+ });
67
+ requireThat(name === fileId(value.id) + ".json", "历史文件名与根 ID 不匹配。");
68
+ if (!writeJsonFile(this.directory, name, value, record, true)) {
69
+ requireThat(canonical(readJsonFile(this.directory, name, record)) === canonical(value), "历史归档迁移冲突,请核对新旧文件:" + name);
70
+ }
71
+ syncDirectory(this.directory);
72
+ unlinkSync(source);
73
+ syncDirectory(directory);
74
+ }
75
+ }
76
+ finally {
77
+ rmdirSync(legacyLock);
78
+ }
79
+ rmdirSync(directory);
80
+ const parent = dirname(directory);
81
+ syncDirectory(parent);
82
+ try {
83
+ rmdirSync(parent);
84
+ syncDirectory(dirname(parent));
85
+ }
86
+ catch (error) {
87
+ if (!["ENOTEMPTY", "EEXIST"].includes(error.code ?? ""))
88
+ throw error;
89
+ }
90
+ });
38
91
  }
39
92
  /** The callback is synchronous: no SDK reads or model work can hold this source's write lock. */
40
93
  control(operation) {
41
94
  mkdirSync(this.directory, { recursive: true, mode: 0o700 });
42
- const lock = join(this.directory, ".write");
95
+ const lock = join(this.directory, ".write-" + this.host);
43
96
  try {
44
97
  mkdirSync(lock, { mode: 0o700 });
45
98
  }
@@ -56,7 +109,7 @@ export class HistoryFiles {
56
109
  }
57
110
  }
58
111
  save(value) {
59
- requireThat(this.host !== "dsh" || value.id.startsWith("dsh-"), "dsh 历史根 ID 缺少来源前缀。");
112
+ requireThat(value.host === this.host, "历史归档与来源不匹配。");
60
113
  mkdirSync(this.directory, { recursive: true, mode: 0o700 });
61
114
  writeJsonFile(this.directory, fileId(value.id) + ".json", value, record);
62
115
  }
@@ -1,4 +1,4 @@
1
- import { Type } from "typebox";
1
+ import { Type } from "../../common/typebox.js";
2
2
  export const agentDescription = "委派专家并继续对话。start 启动后即返回,完成自动通知,无需轮询;省略初始输入时待命。send 继续对话,团队占用期间不可私人 send。status 查看状态,read 读取报告;读取已结束周期即领取其通知,包括部分读取。续读固定 cycle.cycleId 并使用 nextOffset。state 表示执行状态,cycle.state 表示工作结果,queued 只表示入队。stop 保留历史,resume 携新 message 继续。每个父会话最多 50 个存活专家,空闲也计入。";
3
3
  const id = Type.String({ minLength: 1, maxLength: 128 });
4
4
  const message = Type.String({ minLength: 1, maxLength: 24000, description: "本次目标、相关资料、约束和交付要求。" });
@@ -1,4 +1,4 @@
1
- import { Type } from "typebox";
1
+ import { Type } from "../../common/typebox.js";
2
2
  import { agentActionSchema, agentParametersSchema, agentStartOptions } from "./contract.js";
3
3
  const options = Type.Omit(Type.Object(agentStartOptions), ["provider", "model", "thinking", "files", "tools", "noExtensions", "noPromptTemplates", "noContextFiles", "noSkills"]).properties;
4
4
  const native = {
@@ -1,4 +1,4 @@
1
- import { Type } from "typebox";
1
+ import { Type } from "../../common/typebox.js";
2
2
  import { requireThat, schema } from "../../common/validation.js";
3
3
  const host = Type.Union([Type.Literal("pi"), Type.Literal("dsh")]);
4
4
  const nullableOffset = Type.Union([Type.Integer(), Type.Null()]);
@@ -1,4 +1,4 @@
1
- import { Type } from "typebox";
1
+ import { Type } from "../../common/typebox.js";
2
2
  export const teamDescription = "组织固定成员协作。create 建立团队,run 开始一批讨论。运行时占用成员;pause 停止新安排,允许在途工作收尾,之后可私人 send;中止成员用 pbot_agent stop。close 保留成员和历史;换成员须关闭旧团队再创建。继续讨论用 run 提交新要求。成员最终答复向团队公开。";
3
3
  export const teamMessageDescription = "读写团队公共原文。post 向全体成员发布信息,需要安排回答时由主会话调用 pbot_team run;成员只能在自己的团队工作中 post。read 用 afterSeq 翻页,长正文按 seq/offset 续读,先补完 nextOffset 非空的消息。";
4
4
  const id = Type.String({ minLength: 1, maxLength: 128 });
@@ -78,6 +78,8 @@ export function showManagement(ctx, parentSessionId) {
78
78
  }
79
79
  function form(parent) { const node = element("form"); parent.append(node); return node; }
80
80
  function activate(id) {
81
+ if (snapshot?.enabled === false && id !== "status")
82
+ return;
81
83
  tab = id;
82
84
  for (const [key, section] of sections)
83
85
  section.hidden = key !== id;
@@ -157,7 +159,7 @@ export function showManagement(ctx, parentSessionId) {
157
159
  counts.classList.add("pbot-counts");
158
160
  const materials = group(status, "pbot-setting");
159
161
  materials.append(element("h3", "资料目录"));
160
- button(materials, "准备目录", async () => { await command("init", undefined, "资料目录已准备。"); });
162
+ const initializeButton = button(materials, "准备目录", async () => { await command("init", undefined, "工作区已初始化并启用。"); });
161
163
  const pythonCard = group(status, "pbot-setting"), pythonInfo = group(pythonCard);
162
164
  const pythonButton = button(pythonCard, "准备 Python", async () => { await command("python setup", undefined, "Python 准备完成。"); });
163
165
  const diagnostic = details(status, "环境详情"), statusBody = block("");
@@ -625,13 +627,35 @@ export function showManagement(ctx, parentSessionId) {
625
627
  return;
626
628
  location.textContent = text(snapshot.workspaceRoot).split(/[\\/]/).filter(Boolean).at(-1) ?? "";
627
629
  location.title = text(snapshot.workspaceRoot);
630
+ if (snapshot.enabled === false) {
631
+ const configuration = record(snapshot.configuration);
632
+ counts.textContent = configuration.error ? "Personal Bot 配置错误" : "Personal Bot 未启用";
633
+ workspaceInfo.replaceChildren(facts([["状态", configuration.error ? "配置错误" : "未启用"], ["工作区", snapshot.workspaceRoot], ["配置文件", configuration.path]]), note(configuration.error ? "修复配置文件后执行 /pbot init。" : "执行 /pbot init,或点击下方按钮初始化并启用。"));
634
+ pythonInfo.replaceChildren(note("启用工作区后自动准备共享 Python 环境。"));
635
+ materials.hidden = false;
636
+ initializeButton.textContent = "初始化并启用";
637
+ enabled(initializeButton, !configuration.error);
638
+ for (const [id, control] of tabs)
639
+ enabled(control, id === "status");
640
+ enabled(pythonButton, false);
641
+ activate("status");
642
+ statusBody.textContent = JSON.stringify(snapshot, null, 2);
643
+ if (configuration.error)
644
+ inform(text(configuration.error), "error");
645
+ return;
646
+ }
647
+ for (const control of tabs.values())
648
+ enabled(control, true);
649
+ materials.hidden = false;
650
+ initializeButton.textContent = "准备目录";
651
+ enabled(initializeButton, true);
628
652
  const agents = rows(snapshot.agents), teams = rows(snapshot.teams), python = record(snapshot.python), history = record(snapshot.history);
629
653
  tabCounts.get("experts").textContent = text(agents.length);
630
654
  tabCounts.get("teams").textContent = text(teams.length);
631
655
  const active = agents.filter(agent => ["starting", "busy", "stopping"].includes(text(agent.state))).length;
632
- counts.textContent = `专家 ${agents.length} · 执行中 ${active} · 团队 ${teams.length}`;
656
+ counts.textContent = `Personal Bot 已启动 · 专家 ${agents.length} · 执行中 ${active} · 团队 ${teams.length}`;
633
657
  const model = object(snapshot.model) ? snapshot.model : {};
634
- workspaceInfo.replaceChildren(facts([["工作区", snapshot.workspaceRoot], ["模型", model.model || "尚未选择"], ["Provider", model.provider || "—"],
658
+ workspaceInfo.replaceChildren(facts([["状态", "已启动"], ["工作区", snapshot.workspaceRoot], ["配置文件", record(snapshot.configuration).path], ["模型", model.model || "尚未选择"], ["Provider", model.provider || "—"],
635
659
  ["历史", history.organizing ? "整理中" : history.pending === null ? "查询时更新索引" : `${history.pending} 项待整理`]]));
636
660
  pythonInfo.replaceChildren(element("h3", "Python"), badge(python.state));
637
661
  if (python.error)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "personal-bot",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "personal-bot",
9
- "version": "0.2.8",
9
+ "version": "0.2.10",
10
10
  "dependencies": {
11
11
  "ajv": "8.17.1",
12
12
  "ajv-formats": "3.0.1",
@@ -29,6 +29,7 @@
29
29
  "@deepseek-ai/dsh-fs-patch": "0.2.2-rc.1",
30
30
  "@deepseek-ai/dsh-home-paths": "0.2.2-rc.1",
31
31
  "@deepseek-ai/dsh-llm": "0.2.2-rc.1",
32
+ "@deepseek-ai/dsh-scope": "0.2.2-rc.1",
32
33
  "@deepseek-ai/dsh-session": "0.2.2-rc.1",
33
34
  "@deepseek-ai/dsh-session-persistence": "0.2.2-rc.1",
34
35
  "@deepseek-ai/dsh-session-query": "0.2.2-rc.1",
@@ -68,6 +69,7 @@
68
69
  "@deepseek-ai/dsh-fs-patch": "*",
69
70
  "@deepseek-ai/dsh-home-paths": "*",
70
71
  "@deepseek-ai/dsh-llm": "*",
72
+ "@deepseek-ai/dsh-scope": "*",
71
73
  "@deepseek-ai/dsh-session": "*",
72
74
  "@deepseek-ai/dsh-session-persistence": "*",
73
75
  "@deepseek-ai/dsh-session-query": "*",
@@ -122,6 +124,9 @@
122
124
  "@deepseek-ai/dsh-llm": {
123
125
  "optional": true
124
126
  },
127
+ "@deepseek-ai/dsh-scope": {
128
+ "optional": true
129
+ },
125
130
  "@deepseek-ai/dsh-session": {
126
131
  "optional": true
127
132
  },
@@ -166,6 +171,9 @@
166
171
  },
167
172
  "@earendil-works/pi-tui": {
168
173
  "optional": true
174
+ },
175
+ "typebox": {
176
+ "optional": true
169
177
  }
170
178
  }
171
179
  },
@@ -4684,7 +4692,6 @@
4684
4692
  "integrity": "sha512-FOJbXYvrskIcrb/4eVMMQfp0kaReTSBtIry2KsGNURb0OJfelqeSKaB1MsQQq/sOxbt3nPJIN9HY00SwmrTk3Q==",
4685
4693
  "dev": true,
4686
4694
  "license": "MIT",
4687
- "peer": true,
4688
4695
  "peerDependencies": {
4689
4696
  "@deepseek-ai/cordis": "~4.1.0"
4690
4697
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "personal-bot",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/realqiyan/personal-bot.git"
@@ -22,21 +22,36 @@
22
22
  "extensions": [
23
23
  "./src/pbot.ts"
24
24
  ],
25
- "skills": ["./resources/skills"],
25
+ "skills": [],
26
26
  "prompts": [],
27
27
  "themes": []
28
28
  },
29
29
  "dsh": {
30
- "bundle": { "patch": "./resources/dsh/cordis.patch.yml" },
30
+ "bundle": {
31
+ "patch": "./resources/dsh/cordis.patch.yml"
32
+ },
31
33
  "client": {
32
34
  "platform": "web",
33
- "inject": ["@deepseek-ai/dsh-api-remotes", "@deepseek-ai/dsh-client-ui-commands", "@deepseek-ai/dsh-client-file-upload"]
35
+ "inject": [
36
+ "@deepseek-ai/dsh-api-remotes",
37
+ "@deepseek-ai/dsh-client-ui-commands",
38
+ "@deepseek-ai/dsh-client-file-upload"
39
+ ]
34
40
  }
35
41
  },
36
42
  "exports": {
37
- ".": { "types": "./lib/dsh.d.ts", "default": "./lib/dsh.js" },
38
- "./dsh": { "types": "./lib/dsh.d.ts", "default": "./lib/dsh.js" },
39
- "./client": { "types": "./lib/ui/dsh/index.d.ts", "default": "./lib/client.js" },
43
+ ".": {
44
+ "types": "./lib/dsh.d.ts",
45
+ "default": "./lib/dsh.js"
46
+ },
47
+ "./dsh": {
48
+ "types": "./lib/dsh.d.ts",
49
+ "default": "./lib/dsh.js"
50
+ },
51
+ "./client": {
52
+ "types": "./lib/ui/dsh/index.d.ts",
53
+ "default": "./lib/client.js"
54
+ },
40
55
  "./*": "./*"
41
56
  },
42
57
  "files": [
@@ -84,6 +99,7 @@
84
99
  "@deepseek-ai/dsh-tools": "*",
85
100
  "@deepseek-ai/dsh-home-paths": "*",
86
101
  "@deepseek-ai/dsh-system-prompt": "*",
102
+ "@deepseek-ai/dsh-scope": "*",
87
103
  "@deepseek-ai/dsh-skill": "*",
88
104
  "@deepseek-ai/dsh-skill-filesystem": "*",
89
105
  "@deepseek-ai/dsh-working-directory": "*",
@@ -100,30 +116,81 @@
100
116
  "typebox": "*"
101
117
  },
102
118
  "peerDependenciesMeta": {
103
- "@deepseek-ai/cordis": { "optional": true },
104
- "@deepseek-ai/dsh-agent": { "optional": true },
105
- "@deepseek-ai/dsh-agent-preset-registry": { "optional": true },
106
- "@deepseek-ai/dsh-session": { "optional": true },
107
- "@deepseek-ai/dsh-session-persistence": { "optional": true },
108
- "@deepseek-ai/dsh-session-query": { "optional": true },
109
- "@deepseek-ai/dsh-llm": { "optional": true },
110
- "@deepseek-ai/dsh-commands": { "optional": true },
111
- "@deepseek-ai/dsh-api-remotes": { "optional": true },
112
- "@deepseek-ai/dsh-client-ui-commands": { "optional": true },
113
- "@deepseek-ai/dsh-client-file-upload": { "optional": true },
114
- "@deepseek-ai/dsh-tools": { "optional": true },
115
- "@deepseek-ai/dsh-home-paths": { "optional": true },
116
- "@deepseek-ai/dsh-system-prompt": { "optional": true },
117
- "@deepseek-ai/dsh-skill": { "optional": true },
118
- "@deepseek-ai/dsh-skill-filesystem": { "optional": true },
119
- "@deepseek-ai/dsh-working-directory": { "optional": true },
120
- "@deepseek-ai/dsh-fs": { "optional": true },
121
- "@deepseek-ai/dsh-attachment": { "optional": true },
122
- "@deepseek-ai/dsh-fs-patch": { "optional": true },
123
- "@deepseek-ai/dsh-user-approval": { "optional": true },
124
- "@deepseek-ai/dsh-tool-executor-ptc": { "optional": true },
125
- "@deepseek-ai/dsh-typert-protocol": { "optional": true },
126
- "@deepseek-ai/dsh-typert-registry": { "optional": true },
119
+ "@deepseek-ai/cordis": {
120
+ "optional": true
121
+ },
122
+ "@deepseek-ai/dsh-agent": {
123
+ "optional": true
124
+ },
125
+ "@deepseek-ai/dsh-agent-preset-registry": {
126
+ "optional": true
127
+ },
128
+ "@deepseek-ai/dsh-session": {
129
+ "optional": true
130
+ },
131
+ "@deepseek-ai/dsh-session-persistence": {
132
+ "optional": true
133
+ },
134
+ "@deepseek-ai/dsh-session-query": {
135
+ "optional": true
136
+ },
137
+ "@deepseek-ai/dsh-llm": {
138
+ "optional": true
139
+ },
140
+ "@deepseek-ai/dsh-commands": {
141
+ "optional": true
142
+ },
143
+ "@deepseek-ai/dsh-api-remotes": {
144
+ "optional": true
145
+ },
146
+ "@deepseek-ai/dsh-client-ui-commands": {
147
+ "optional": true
148
+ },
149
+ "@deepseek-ai/dsh-client-file-upload": {
150
+ "optional": true
151
+ },
152
+ "@deepseek-ai/dsh-tools": {
153
+ "optional": true
154
+ },
155
+ "@deepseek-ai/dsh-home-paths": {
156
+ "optional": true
157
+ },
158
+ "@deepseek-ai/dsh-system-prompt": {
159
+ "optional": true
160
+ },
161
+ "@deepseek-ai/dsh-scope": {
162
+ "optional": true
163
+ },
164
+ "@deepseek-ai/dsh-skill": {
165
+ "optional": true
166
+ },
167
+ "@deepseek-ai/dsh-skill-filesystem": {
168
+ "optional": true
169
+ },
170
+ "@deepseek-ai/dsh-working-directory": {
171
+ "optional": true
172
+ },
173
+ "@deepseek-ai/dsh-fs": {
174
+ "optional": true
175
+ },
176
+ "@deepseek-ai/dsh-attachment": {
177
+ "optional": true
178
+ },
179
+ "@deepseek-ai/dsh-fs-patch": {
180
+ "optional": true
181
+ },
182
+ "@deepseek-ai/dsh-user-approval": {
183
+ "optional": true
184
+ },
185
+ "@deepseek-ai/dsh-tool-executor-ptc": {
186
+ "optional": true
187
+ },
188
+ "@deepseek-ai/dsh-typert-protocol": {
189
+ "optional": true
190
+ },
191
+ "@deepseek-ai/dsh-typert-registry": {
192
+ "optional": true
193
+ },
127
194
  "@earendil-works/pi-ai": {
128
195
  "optional": true
129
196
  },
@@ -132,6 +199,9 @@
132
199
  },
133
200
  "@earendil-works/pi-tui": {
134
201
  "optional": true
202
+ },
203
+ "typebox": {
204
+ "optional": true
135
205
  }
136
206
  },
137
207
  "dependencies": {
@@ -155,6 +225,7 @@
155
225
  "@deepseek-ai/dsh-tools": "0.2.2-rc.1",
156
226
  "@deepseek-ai/dsh-home-paths": "0.2.2-rc.1",
157
227
  "@deepseek-ai/dsh-system-prompt": "0.2.2-rc.1",
228
+ "@deepseek-ai/dsh-scope": "0.2.2-rc.1",
158
229
  "@deepseek-ai/dsh-skill": "0.2.2-rc.1",
159
230
  "@deepseek-ai/dsh-skill-filesystem": "0.2.2-rc.1",
160
231
  "@deepseek-ai/dsh-working-directory": "0.2.2-rc.1",
package/python/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 自动准备与激活
6
6
 
7
- Pi 加载 Personal Bot、原生 `/reload`,或 dsh 加载 pbot Host 组件时自动执行 setup,无需另行确认或指定路径。同一系统用户的两种宿主共用该环境:
7
+ 工作区的 `.pbot/config.json` 设置 `enabled: true` 后,Pi 加载或原生 `/reload` 时自动执行 setup;dsh 在首个已启用工作区加载时准备环境。无需另行确认或指定 Python 路径。同一系统用户的两种宿主共用该环境:
8
8
 
9
9
  1. 共享 venv 不存在时创建,不使用系统 site-packages。
10
10
  2. 按 `requirements.lock` 补齐或对齐依赖,允许升级或降级;已匹配时不重新安装。
@@ -18,11 +18,11 @@ Pi 加载 Personal Bot、原生 `/reload`,或 dsh 加载 pbot Host 组件时
18
18
 
19
19
  首次准备从当前 PATH 选择 python3,无该命令时使用 python;需要 Python 3.11+ 和 venv/ensurepip。系统 Python 只用于创建独立 venv,不在系统环境中安装依赖。现有环境不自动替换基础解释器,也不删除额外安装的包。
20
20
 
21
- 首次安装可能需要数分钟和网络。环境准备不依赖 `/pbot init` 或工作区资料;不需要 Personal Bot 时通过所属宿主的原生配置取消加载。`/pbot status` 显示环境状态。所属激活生命周期关闭时恢复本扩展修改的进程环境变量;dsh 按整个 pbot 组件共享激活,不随某个浏览器页面或单个工作区关闭而恢复,不覆盖期间其他代码对变量的新修改。外部终端和 shell 配置不变。
21
+ 首次安装可能需要数分钟和网络。`/pbot init` 创建共用启用配置并启动环境准备;手工设置 enabled 为 true 后重载也会准备环境。不需要 Personal Bot 的工作区可保留配置缺失或设置 enabled 为 false,再重载或重启宿主;也可通过宿主原生配置取消加载整个插件。`/pbot status` 显示环境状态。所属激活生命周期关闭时恢复本扩展修改的进程环境变量;dsh 按整个 pbot 组件共享激活,不随某个浏览器页面或单个工作区关闭而恢复,不覆盖期间其他代码对变量的新修改。外部终端和 shell 配置不变。
22
22
 
23
23
  ## 失败与手工重试
24
24
 
25
- 环境异常时提醒,并在状态中保留错误。pbot 的其他能力仍可使用,但不把 Python 标记为就绪,也不替模型自动换用系统 Python 执行研究。原生 Bash 仍然可用,这不是命令黑名单或系统隔离。
25
+ 环境异常时提醒,并在状态中保留错误。pbot 的其他能力仍可使用,但不把 Python 标记为就绪,也不替模型自动换用系统 Python 执行研究。原生 Bash 仍然可用。
26
26
 
27
27
  检查错误后可执行:
28
28
 
@@ -34,7 +34,7 @@ Pi 加载 Personal Bot、原生 `/reload`,或 dsh 加载 pbot Host 组件时
34
34
 
35
35
  安装只接受 wheel,不编译 C 库、不安装系统软件,不绕过系统 Python 的安装保护。没有匹配 wheel、网络不可用、权限不足或环境损坏时停止。TA-Lib wheel 包含底层库,但并非所有系统和架构都有匹配 wheel。pip 安装配置被忽略,避免包被重定向到其他目标。
36
36
 
37
- 多个 pbot 进程同时启动时,通过共享排他锁串行准备,后取得锁的进程重新检查。锁文件不删除。该锁不管理普通 Python 进程;更换插件版本或手工重试前,应停止使用该环境的脚本。不同插件版本可能要求不同依赖,不宜同时使用。不能承诺自动更新不会影响已运行的外部脚本。
37
+ 多个 pbot 进程同时启动时,通过共享排他锁串行准备,后取得锁的进程重新检查。锁文件不删除。该锁不管理普通 Python 进程;更换插件版本或手工重试前,应停止使用该环境的脚本。不同插件版本可能要求不同依赖,不宜同时使用。
38
38
 
39
39
  ## 独立准备
40
40
 
@@ -51,7 +51,7 @@ python3 scripts/setup-python.py --check # 只检查,不创建或安装
51
51
 
52
52
  setup 安装 numpy、pandas、TA-Lib、backtesting、scipy、statsmodels、matplotlib、scikit-learn、pyarrow、openpyxl、seaborn 和 Bokeh。按任务导入,环境与项目运行见[计算参考](../resources/references/compute.md),需要技术指标或回测时再读[指标与回测](../resources/references/compute-finance.md)。市场规则、费用、成交时点和数据切分由具体研究确定,原生 Backtest 不自动提供 A 股 T+1、整手或涨跌停约束。
53
53
 
54
- 激活后直接执行 `python scripts/analyze.py` 或 `python -m analysis.main`。工作目录、模块导入、参数和结果位置遵循项目约定。项目构建或测试明确要求另一环境时,按项目约定执行。pbot 不复制源码、不生成输入文件或计算记录、不管理研究进程的超时。Python 与 Bash 不是文件或网络沙箱,也不授予交易权限。
54
+ 激活后直接执行 `python scripts/analyze.py` 或 `python -m analysis.main`。工作目录、模块导入、参数和结果位置遵循项目约定。项目构建或测试明确要求另一环境时,按项目约定执行。pbot 不复制源码、不生成输入文件或计算记录、不管理研究进程的超时。Python 与 Bash 使用实际账户权限,操作范围见[工具与执行](../docs/capability-design.md)。
55
55
 
56
56
  在已激活环境中验证:
57
57
 
@@ -60,7 +60,7 @@ python -m pip check
60
60
  python -B -m unittest discover -s python/tests -v
61
61
  ```
62
62
 
63
- Node 测试使用真实本地文件、进程、临时 venv 与 Pi/dsh 原生 Bash,验证检查、错误、取消和激活恢复;不下载依赖。完整安装与库测试使用准备好的真实环境,不能以损坏环境的启动测试替代成功安装验收。Python 集成测试验证原生库配合与文件往返,不证明市场规则或策略有效;macOS 的 dsh 桌面应用已在临时 HOME 中完成真实依赖安装、就绪激活、pip check 与 5 项原生库测试;真实模型使用这些库的行为仍需验收。
63
+ Node 测试检查环境探测、错误、取消与激活恢复,使用临时 venv 和原生 Bash。上述 Python 测试在已准备的环境中检查原生库配合与文件往返。
64
64
 
65
65
  依赖版本独立于插件版本。更新锁文件须检查最低 Python 3.11 与目标环境的兼容性,不只按开发机选择最新版。
66
66
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  ## 资料与环境
12
12
 
13
- 沿用已有工作区约定;新资料按下列用途组织。正文维护一份,其他位置用引用连接。
13
+ 沿用已有工作区约定;新资料按下列用途组织。文档按主题维护当前说明,保留读者理解、决策和操作所需的信息。正文维护一份,其他位置用引用连接。
14
14
 
15
15
  | 路径 | 用途 |
16
16
  | --- | --- |
@@ -23,8 +23,8 @@
23
23
  | `agents/` | `coordinator.md`、`general-purpose.md` 的职责补充 |
24
24
  | `.agents/skills/` | 工作区自有专业方法 |
25
25
 
26
- 保存有后续用途的结论、方法、决策和产物;普通问答不逐轮存档。保存前查找同主题正文,更新或整理时使用 project-maintenance。可再次查询的数据按记录的口径获取。保留用户资料、历史记录和备份。
26
+ 保存有后续用途的结论、方法、决策和产物;普通问答不逐轮存档。保存前查找同主题正文,直接修订,合并重复内容并替换过时说明。必要的实现细节放入对应技术资料,排查过程和测试流水不逐次追加到正文。更新或整理时使用 project-maintenance。可再次查询的数据按记录的口径获取。保留用户资料、历史记录和备份。
27
27
 
28
28
  环境上下文中的 `workspaceRoot` 是资料工作区根目录,`resourceRoot` 是包内资料根目录,`python` 给出共享环境状态。资料路径相对工作区根目录,命令与文件工具按实际 cwd 解析路径。需要 Python 时读取包内 `references/compute.md`;共享环境未就绪时不退回系统 Python。项目构建和测试遵循项目声明的环境。
29
29
 
30
- 答复先给结论,按需补充说明和产物路径。数值注明影响理解的对象、期间、单位与口径。
30
+ 答复先给结论,按需补充说明和产物路径。自然语言说明直接回答当前问题,不惯常追加否定式限定或免责声明,也不设想用户未提出的推论再逐一否定。必要的未知、条件和风险清楚说明一次。数值注明影响理解的对象、期间、单位与口径。任务要求、操作权限、必要确认和规定的输出格式照常遵循。
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,7 +1,4 @@
1
- import { join } from "node:path";
2
1
  import type { Context } from "@deepseek-ai/cordis";
3
- import * as SkillFilesystem from "@deepseek-ai/dsh-skill-filesystem";
4
- import { resourceRoot } from "./common/paths.js";
5
2
  import { registerDshCommand } from "./extension/dsh/command.js";
6
3
  import { registerDshManagement } from "./extension/dsh/management.js";
7
4
  import { DshWorkspaces, dshDependencies } from "./extension/dsh/workspace.js";
@@ -13,7 +10,6 @@ export const inject = ["commands", "typert", ...dshDependencies];
13
10
  export async function apply(ctx: Context) {
14
11
  const [major, minor] = process.versions.node.split(".").map(Number);
15
12
  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") });
17
13
  const workspaces = new DshWorkspaces(ctx);
18
14
  ctx.effect(() => () => workspaces.close());
19
15
  const management = registerDshManagement(ctx, workspaces);
@@ -22,5 +18,4 @@ export async function apply(ctx: Context) {
22
18
  if (ctx.agents.roots().includes(agent) && agent.session.header.origin !== "subagent") await workspaces.open(agent);
23
19
  });
24
20
  for (const agent of ctx.agents.roots()) if (agent.session.header.origin !== "subagent") await workspaces.open(agent);
25
- void workspaces.preparePython().catch(error => ctx.logger.warn(String(error)));
26
21
  }