personal-bot 0.2.4 → 0.2.5

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 (214) hide show
  1. package/CONTEXT.md +9 -7
  2. package/README.md +13 -6
  3. package/README.zh-CN.md +13 -6
  4. package/docs/README.md +3 -1
  5. package/docs/agent-collaboration.md +26 -12
  6. package/docs/architecture-guidelines.md +2 -2
  7. package/docs/capability-design.md +6 -4
  8. package/docs/dsh-implementation-plan.md +158 -0
  9. package/docs/dsh-support-proposal.md +314 -0
  10. package/docs/dsh.md +103 -0
  11. package/docs/file-storage.md +14 -9
  12. package/docs/history.md +29 -18
  13. package/docs/installation-design.md +3 -1
  14. package/docs/interaction-design.md +13 -0
  15. package/docs/release.md +3 -3
  16. package/docs/technical-design.md +29 -15
  17. package/docs/validation.md +38 -2
  18. package/lib/client.js +1071 -0
  19. package/lib/common/files.d.ts +2 -0
  20. package/lib/common/files.js +44 -0
  21. package/lib/common/json.d.ts +6 -0
  22. package/lib/common/json.js +16 -0
  23. package/lib/common/markdown.d.ts +5 -0
  24. package/lib/common/markdown.js +26 -0
  25. package/lib/common/paths.d.ts +1 -0
  26. package/lib/common/paths.js +2 -0
  27. package/lib/common/validation.d.ts +3 -0
  28. package/lib/common/validation.js +14 -0
  29. package/lib/common/version.d.ts +3 -0
  30. package/lib/common/version.js +13 -0
  31. package/lib/dsh.d.ts +4 -0
  32. package/lib/dsh.js +22 -0
  33. package/lib/extension/dsh/agent-events.d.ts +24 -0
  34. package/lib/extension/dsh/agent-events.js +242 -0
  35. package/lib/extension/dsh/agent-runtime.d.ts +49 -0
  36. package/lib/extension/dsh/agent-runtime.js +348 -0
  37. package/lib/extension/dsh/command.d.ts +4 -0
  38. package/lib/extension/dsh/command.js +83 -0
  39. package/lib/extension/dsh/files.d.ts +5 -0
  40. package/lib/extension/dsh/files.js +67 -0
  41. package/lib/extension/dsh/history.d.ts +15 -0
  42. package/lib/extension/dsh/history.js +88 -0
  43. package/lib/extension/dsh/management.d.ts +21 -0
  44. package/lib/extension/dsh/management.js +217 -0
  45. package/lib/extension/dsh/workspace.d.ts +48 -0
  46. package/lib/extension/dsh/workspace.js +290 -0
  47. package/lib/integrations/agent-resources.d.ts +1 -0
  48. package/lib/integrations/agent-resources.js +18 -0
  49. package/lib/integrations/dsh/dsh-agents.d.ts +30 -0
  50. package/lib/integrations/dsh/dsh-agents.js +104 -0
  51. package/lib/integrations/dsh/history-summary.d.ts +11 -0
  52. package/lib/integrations/dsh/history-summary.js +40 -0
  53. package/lib/integrations/dsh/history.d.ts +16 -0
  54. package/lib/integrations/dsh/history.js +156 -0
  55. package/lib/integrations/dsh/input.d.ts +21 -0
  56. package/lib/integrations/dsh/input.js +62 -0
  57. package/lib/integrations/dsh/messages.d.ts +36 -0
  58. package/lib/integrations/dsh/messages.js +11 -0
  59. package/lib/integrations/dsh/notifications.d.ts +8 -0
  60. package/lib/integrations/dsh/notifications.js +55 -0
  61. package/lib/integrations/dsh/resources.d.ts +10 -0
  62. package/lib/integrations/dsh/resources.js +41 -0
  63. package/lib/integrations/dsh/schema.d.ts +7 -0
  64. package/lib/integrations/dsh/schema.js +28 -0
  65. package/lib/integrations/dsh/session.d.ts +14 -0
  66. package/lib/integrations/dsh/session.js +59 -0
  67. package/lib/integrations/history-summary.d.ts +2 -0
  68. package/lib/integrations/history-summary.js +21 -0
  69. package/lib/integrations/process.d.ts +14 -0
  70. package/lib/integrations/process.js +48 -0
  71. package/lib/integrations/python-activation.d.ts +2 -0
  72. package/lib/integrations/python-activation.js +29 -0
  73. package/lib/integrations/team-context.d.ts +18 -0
  74. package/lib/integrations/team-context.js +57 -0
  75. package/lib/model/agent.d.ts +92 -0
  76. package/lib/model/agent.js +1 -0
  77. package/lib/model/history.d.ts +43 -0
  78. package/lib/model/history.js +1 -0
  79. package/lib/model/team.d.ts +85 -0
  80. package/lib/model/team.js +1 -0
  81. package/lib/services/agents/agent-notices.d.ts +13 -0
  82. package/lib/services/agents/agent-notices.js +25 -0
  83. package/lib/services/agents/agent-run.d.ts +39 -0
  84. package/lib/services/agents/agent-run.js +138 -0
  85. package/lib/services/agents/agent-service.d.ts +166 -0
  86. package/lib/services/agents/agent-service.js +325 -0
  87. package/lib/services/agents/team-service.d.ts +168 -0
  88. package/lib/services/agents/team-service.js +343 -0
  89. package/lib/services/history/history-service.d.ts +103 -0
  90. package/lib/services/history/history-service.js +269 -0
  91. package/lib/services/workspace/python-environment-service.d.ts +15 -0
  92. package/lib/services/workspace/python-environment-service.js +50 -0
  93. package/lib/services/workspace/workspace-files.d.ts +2 -0
  94. package/lib/services/workspace/workspace-files.js +17 -0
  95. package/lib/services/workspace/workspace-preparation.d.ts +2 -0
  96. package/lib/services/workspace/workspace-preparation.js +18 -0
  97. package/lib/storage/agent-files.d.ts +30 -0
  98. package/lib/storage/agent-files.js +166 -0
  99. package/lib/storage/atomic-json.d.ts +11 -0
  100. package/lib/storage/atomic-json.js +101 -0
  101. package/lib/storage/file-access.d.ts +2 -0
  102. package/lib/storage/file-access.js +26 -0
  103. package/lib/storage/history-files.d.ts +10 -0
  104. package/lib/storage/history-files.js +63 -0
  105. package/lib/storage/team-files.d.ts +16 -0
  106. package/lib/storage/team-files.js +111 -0
  107. package/lib/tools/agents/actions.d.ts +52 -0
  108. package/lib/tools/agents/actions.js +49 -0
  109. package/lib/tools/agents/contract.d.ts +163 -0
  110. package/lib/tools/agents/contract.js +56 -0
  111. package/lib/tools/agents/dsh-contract.d.ts +52 -0
  112. package/lib/tools/agents/dsh-contract.js +15 -0
  113. package/lib/tools/agents/dsh-tool.d.ts +5 -0
  114. package/lib/tools/agents/dsh-tool.js +38 -0
  115. package/lib/tools/history/actions.d.ts +70 -0
  116. package/lib/tools/history/actions.js +7 -0
  117. package/lib/tools/history/contract.d.ts +81 -0
  118. package/lib/tools/history/contract.js +32 -0
  119. package/lib/tools/history/dsh-tool.d.ts +4 -0
  120. package/lib/tools/history/dsh-tool.js +22 -0
  121. package/lib/tools/result.d.ts +18 -0
  122. package/lib/tools/result.js +10 -0
  123. package/lib/tools/teams/actions.d.ts +24 -0
  124. package/lib/tools/teams/actions.js +38 -0
  125. package/lib/tools/teams/contract.d.ts +232 -0
  126. package/lib/tools/teams/contract.js +67 -0
  127. package/lib/tools/teams/dsh-tool.d.ts +8 -0
  128. package/lib/tools/teams/dsh-tool.js +49 -0
  129. package/lib/ui/dsh/attachments.d.ts +5 -0
  130. package/lib/ui/dsh/attachments.js +39 -0
  131. package/lib/ui/dsh/elements.d.ts +17 -0
  132. package/lib/ui/dsh/elements.js +79 -0
  133. package/lib/ui/dsh/index.d.ts +4 -0
  134. package/lib/ui/dsh/index.js +13 -0
  135. package/lib/ui/dsh/management.d.ts +4 -0
  136. package/lib/ui/dsh/management.js +711 -0
  137. package/lib/ui/dsh/remote.d.ts +15 -0
  138. package/lib/ui/dsh/remote.js +11 -0
  139. package/lib/ui/dsh/styles.d.ts +1 -0
  140. package/lib/ui/dsh/styles.js +132 -0
  141. package/lib/ui/presentation.d.ts +3 -0
  142. package/lib/ui/presentation.js +12 -0
  143. package/npm-shrinkwrap.json +11140 -830
  144. package/package.json +94 -12
  145. package/python/README.md +6 -6
  146. package/resources/agents/coordinator.md +1 -1
  147. package/resources/dsh/cordis.patch.yml +3 -0
  148. package/resources/prompts/workspace.md +1 -1
  149. package/resources/references/compute.md +1 -1
  150. package/resources/references/task-conductor.md +6 -4
  151. package/resources/skills/provider-connections/SKILL.md +10 -0
  152. package/scripts/README.md +9 -1
  153. package/src/dsh.ts +22 -0
  154. package/src/extension/agent-events.ts +51 -46
  155. package/src/extension/agents-command.ts +48 -34
  156. package/src/extension/command.ts +42 -23
  157. package/src/extension/dsh/agent-events.ts +148 -0
  158. package/src/extension/dsh/agent-runtime.ts +251 -0
  159. package/src/extension/dsh/command.ts +78 -0
  160. package/src/extension/dsh/files.ts +61 -0
  161. package/src/extension/dsh/history.ts +69 -0
  162. package/src/extension/dsh/management.ts +108 -0
  163. package/src/extension/dsh/workspace.ts +206 -0
  164. package/src/extension/events.ts +6 -4
  165. package/src/extension/history.ts +15 -12
  166. package/src/extension/subagent-entry.ts +34 -63
  167. package/src/extension/teams-command.ts +53 -46
  168. package/src/integrations/{pi/agent-resources.ts → agent-resources.ts} +2 -2
  169. package/src/integrations/dsh/dsh-agents.ts +97 -0
  170. package/src/integrations/dsh/history-summary.ts +43 -0
  171. package/src/integrations/dsh/history.ts +76 -0
  172. package/src/integrations/dsh/input.ts +66 -0
  173. package/src/integrations/dsh/messages.ts +22 -0
  174. package/src/integrations/dsh/notifications.ts +46 -0
  175. package/src/integrations/dsh/resources.ts +44 -0
  176. package/src/integrations/dsh/schema.ts +26 -0
  177. package/src/integrations/dsh/session.ts +67 -0
  178. package/src/integrations/history-summary.ts +20 -0
  179. package/src/integrations/pi/history.ts +4 -14
  180. package/src/integrations/pi/pi-agents.ts +17 -6
  181. package/src/integrations/pi/team-context.ts +6 -58
  182. package/src/integrations/team-context.ts +60 -0
  183. package/src/model/agent.ts +2 -0
  184. package/src/model/history.ts +3 -1
  185. package/src/pbot.ts +1 -1
  186. package/src/services/agents/agent-notices.ts +28 -0
  187. package/src/services/agents/agent-run.ts +114 -0
  188. package/src/services/agents/agent-service.ts +127 -64
  189. package/src/services/history/history-service.ts +120 -59
  190. package/src/storage/agent-files.ts +17 -4
  191. package/src/storage/history-files.ts +30 -5
  192. package/src/tools/agents/actions.ts +47 -0
  193. package/src/tools/agents/contract.ts +8 -5
  194. package/src/tools/agents/dsh-contract.ts +16 -0
  195. package/src/tools/agents/dsh-tool.ts +41 -0
  196. package/src/tools/agents/tool.ts +7 -43
  197. package/src/tools/history/actions.ts +9 -0
  198. package/src/tools/history/contract.ts +34 -0
  199. package/src/tools/history/dsh-tool.ts +22 -0
  200. package/src/tools/history/tool.ts +7 -30
  201. package/src/tools/teams/actions.ts +38 -0
  202. package/src/tools/teams/contract.ts +7 -3
  203. package/src/tools/teams/dsh-tool.ts +55 -0
  204. package/src/tools/teams/tool.ts +6 -31
  205. package/src/ui/agent-display.ts +7 -4
  206. package/src/ui/dsh/attachments.ts +42 -0
  207. package/src/ui/dsh/elements.ts +50 -0
  208. package/src/ui/dsh/index.ts +18 -0
  209. package/src/ui/dsh/management.ts +412 -0
  210. package/src/ui/dsh/remote.ts +20 -0
  211. package/src/ui/dsh/styles.ts +132 -0
  212. package/src/ui/pi/menu.ts +37 -0
  213. package/src/ui/pi/reader.ts +46 -0
  214. package/src/ui/presentation.ts +12 -0
@@ -0,0 +1,348 @@
1
+ import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
2
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
3
+ import { AgentRun } from "../../services/agents/agent-run.js";
4
+ import { AgentFiles, launch } from "../../storage/agent-files.js";
5
+ import { requireThat } from "../../common/validation.js";
6
+ import { dshInput, dshHumanInput } from "../../integrations/dsh/input.js";
7
+ import { requestSource, finalDshAnswer } from "../../integrations/dsh/messages.js";
8
+ import { flushDshAgent } from "../../integrations/dsh/session.js";
9
+ import { createTeamContext, teamBrief } from "../../integrations/team-context.js";
10
+ /** One expert's native event adapter. AgentRun and TeamService own durable business state. */
11
+ export class DshAgentRuntime {
12
+ ctx;
13
+ agent;
14
+ d;
15
+ generation;
16
+ files;
17
+ teams;
18
+ owner;
19
+ execution;
20
+ #lifetime = new AbortController();
21
+ #inbox = { "next-turn": [], "next-step": [] };
22
+ #seen = new Map();
23
+ #events = Promise.resolve();
24
+ #turnEvents = [];
25
+ #answer = "";
26
+ #hasAnswer = false;
27
+ #activeTeam;
28
+ #pendingPause;
29
+ #pump;
30
+ #timer;
31
+ #stopping;
32
+ #closing = false;
33
+ #failure;
34
+ #ready = false;
35
+ #teamTool = false;
36
+ constructor(ctx, agent, d, generation, files, teams, owner) {
37
+ this.ctx = ctx;
38
+ this.agent = agent;
39
+ this.d = d;
40
+ this.generation = generation;
41
+ this.files = files;
42
+ this.teams = teams;
43
+ this.owner = owner;
44
+ this.execution = new AgentRun(files, d.agentId, generation);
45
+ }
46
+ check = () => {
47
+ this.#lifetime.signal.throwIfAborted();
48
+ this.owner.check();
49
+ this.files.check();
50
+ const run = this.files.read(this.d.agentId, "launch.json", launch);
51
+ requireThat(run.generation === this.generation && !run.stop, "专家已停止或启动代次变化。");
52
+ };
53
+ actor = () => ({ kind: "member", agentId: this.d.agentId, generation: this.generation,
54
+ ...(this.#activeTeam && this.execution.current?.source === "parent" ? { requestId: this.#activeTeam.request.requestId } : {}) });
55
+ current() { return this.files.read(this.d.agentId, "launch.json", launch).generation === this.generation; }
56
+ pause(reason) {
57
+ this.#pendingPause = reason;
58
+ try {
59
+ this.teams.pauseMember(this.d.agentId, reason);
60
+ this.#pendingPause = undefined;
61
+ }
62
+ catch (error) {
63
+ if (error.code !== "PBOT_CONTROL_BUSY")
64
+ throw error;
65
+ }
66
+ }
67
+ enqueue(work) {
68
+ this.#events = this.#events.then(work).catch(error => this.fail(error));
69
+ }
70
+ fail(error) {
71
+ if (this.#failure)
72
+ return;
73
+ this.#failure = error;
74
+ this.#lifetime.abort();
75
+ try {
76
+ if (this.current()) {
77
+ this.teams.files.setReady(this.d.agentId, this.generation, false);
78
+ this.execution.save({ state: "error", activity: "控制失败", error: String(error) });
79
+ this.pause("专家控制失败");
80
+ }
81
+ }
82
+ catch (failure) {
83
+ this.ctx.logger.error("pbot 专家状态保存失败", failure);
84
+ }
85
+ void this.owner.release().catch(failure => this.ctx.logger.error("pbot 专家释放失败", failure));
86
+ }
87
+ initialize(teamTool) {
88
+ this.check();
89
+ this.#teamTool = teamTool;
90
+ this.teams.files.setReady(this.d.agentId, this.generation, false);
91
+ this.pause("专家载入:旧批次不自动恢复");
92
+ this.execution.recover();
93
+ for (const event of this.agent.session.snapshotEvents())
94
+ if (event.type === "user/message" && event.data.source.kind === "pbot-team-context") {
95
+ const { teamId, seqs } = event.data.source, seen = this.#seen.get(teamId) ?? new Set();
96
+ for (const seq of seqs)
97
+ seen.add(seq);
98
+ this.#seen.set(teamId, seen);
99
+ }
100
+ this.ctx.on("session/event", (session, event) => {
101
+ if (session === this.agent.session)
102
+ this.enqueue(() => this.event(event));
103
+ });
104
+ this.ctx.on("agent/status", ({ agent, status }) => {
105
+ if (agent !== this.agent)
106
+ return;
107
+ this.enqueue(() => {
108
+ if (!this.current() || this.#closing || this.#failure)
109
+ return;
110
+ this.execution.save({ state: status === "idle" ? "idle" : "busy", activity: status === "idle" ? "空闲" : "执行" });
111
+ });
112
+ });
113
+ this.ctx.on("agent/pre-step", async (_payload, next) => {
114
+ await this.#events;
115
+ this.check();
116
+ if (this.#activeTeam && !this.humanPending() && this.execution.current?.source === "parent" && Date.now() >= this.#activeTeam.run.deadlineAt) {
117
+ this.pause("已到期限");
118
+ this.agent.cancel({ kind: "hook", reason: "团队请求已到期限" });
119
+ return { kind: "reject" };
120
+ }
121
+ const decision = await next();
122
+ this.check();
123
+ if (decision.kind !== "enter" || !this.#activeTeam || this.execution.current?.source !== "parent" || this.humanPending() ||
124
+ decision.messages.some(message => message.source.kind === "user"))
125
+ return decision;
126
+ const link = this.teams.links().get(this.#activeTeam.request.requestId);
127
+ requireThat(link, "团队关联不可读取。");
128
+ const seen = this.#seen.get(link.team.teamId) ?? new Set();
129
+ const context = createTeamContext(link, seen);
130
+ if (!context)
131
+ return decision;
132
+ return { ...decision, messages: [...decision.messages, createUserMessage({ content: [{ type: "text", text: context.content }],
133
+ source: { kind: "pbot-team-context", form: "relay", teamId: link.team.teamId, seqs: context.seqs } })] };
134
+ });
135
+ this.ctx.systemPrompt.section({ name: "pbot:team", order: 600, interpolate: false,
136
+ text: () => this.#activeTeam && !this.humanPending() && this.execution.current?.source === "parent" ? teamBrief(this.#activeTeam) : "" });
137
+ this.execution.save({ state: "idle", activity: "空闲" });
138
+ this.ctx.effect(() => () => this.stop());
139
+ }
140
+ /** Called only after native Agent creation has resolved, never from setup. */
141
+ start() {
142
+ this.check();
143
+ this.#timer = setInterval(() => { void this.pump(); }, 250);
144
+ this.#timer.unref();
145
+ void this.pump();
146
+ }
147
+ async interact(text, delivery = "steer", files = [], attachments = [], caller) {
148
+ this.check();
149
+ requireThat((text.trim().length > 0 || files.length || attachments.length) && text.length <= 24000, "请输入消息或附加文件;消息最多 24000 字符。");
150
+ const signal = caller ? AbortSignal.any([caller, this.#lifetime.signal]) : this.#lifetime.signal;
151
+ const message = await dshHumanInput(this.ctx, this.d.cwd, text, files, attachments, signal);
152
+ this.check();
153
+ signal.throwIfAborted();
154
+ this.teams.pauseMember(this.d.agentId, "成员由人工接管");
155
+ if (delivery === "followUp")
156
+ this.agent.followup(message);
157
+ else
158
+ this.agent.steer(message);
159
+ // Native inbox admission transfers ownership to the expert, independently of the UI request.
160
+ await flushDshAgent(this.agent);
161
+ return { status: "queued", messageId: message.id };
162
+ }
163
+ async event(event) {
164
+ if (!this.current())
165
+ return;
166
+ if (event.type === "turn/start") {
167
+ this.#turnEvents = [];
168
+ this.#answer = "";
169
+ this.#hasAnswer = false;
170
+ this.execution.begin();
171
+ }
172
+ if (this.execution.current)
173
+ this.#turnEvents.push(event);
174
+ if (event.type === "agent/inbox/spliced") {
175
+ const change = event.data;
176
+ const removed = this.#inbox[change.target].splice(change.start, change.removedCount ?? 0, ...change.inserted);
177
+ if (change.inserted.some(message => message.source.kind === "user"))
178
+ this.pause("成员由人工接管");
179
+ for (const message of removed) {
180
+ if (message.source.kind === "user" && change.outcome !== "canceled")
181
+ this.execution.enterHuman();
182
+ const source = requestSource(message, this.generation);
183
+ if (!source)
184
+ continue;
185
+ if (change.outcome === "canceled") {
186
+ this.execution.cancelQueued(source.requestId);
187
+ if (this.#activeTeam?.request.requestId === source.requestId && !this.execution.current?.requestIds.includes(source.requestId))
188
+ this.#activeTeam = undefined;
189
+ }
190
+ else
191
+ this.execution.enter(source.requestId);
192
+ }
193
+ }
194
+ if (event.type === "user/message") {
195
+ const source = requestSource(event.data, this.generation);
196
+ if (source)
197
+ this.execution.consume(source.requestId);
198
+ if (event.data.source.kind === "user")
199
+ this.execution.humanInput();
200
+ if (event.data.source.kind === "pbot-team-context") {
201
+ const { teamId, seqs } = event.data.source, seen = this.#seen.get(teamId) ?? new Set();
202
+ for (const seq of seqs)
203
+ seen.add(seq);
204
+ this.#seen.set(teamId, seen);
205
+ }
206
+ }
207
+ if (event.type === "assistant/message") {
208
+ this.#answer = finalDshAnswer(event);
209
+ this.#hasAnswer = true;
210
+ }
211
+ if (event.type === "turn/end") {
212
+ const accounted = foldConsumedWork(this.#turnEvents).end?.seq === event.seq;
213
+ await flushDshAgent(this.agent);
214
+ const reason = event.data.reason;
215
+ this.execution.finish({
216
+ state: reason.kind === "aborted" ? "cancelled" : reason.kind === "interrupted" || reason.kind === "forked" ? "interrupted"
217
+ : reason.kind === "completed" && accounted ? "completed" : "failed",
218
+ position: this.#hasAnswer ? "dsh:" + event.seq : null,
219
+ answer: this.#answer,
220
+ ...(reason.kind === "error" ? { error: reason.error.message } : !["completed", "aborted"].includes(reason.kind) ? { error: reason.kind } : {}),
221
+ });
222
+ this.#activeTeam = undefined;
223
+ this.#turnEvents = [];
224
+ }
225
+ }
226
+ humanPending() { return [...this.agent.inbox.nextTurn, ...this.agent.inbox.nextStep].some(message => message.source.kind === "user"); }
227
+ pump() {
228
+ if (this.#pump || this.#closing)
229
+ return this.#pump ?? Promise.resolve();
230
+ this.#pump = this.deliver().catch(error => {
231
+ if (error.code === "PBOT_CONTROL_BUSY" || (this.#closing && this.#lifetime.signal.aborted))
232
+ return;
233
+ this.fail(error);
234
+ }).finally(() => { this.#pump = undefined; });
235
+ return this.#pump;
236
+ }
237
+ async deliver() {
238
+ const run = this.files.read(this.d.agentId, "launch.json", launch);
239
+ if (run.generation !== this.generation || run.stop) {
240
+ void this.owner.release().catch(error => this.fail(error));
241
+ return;
242
+ }
243
+ await this.#events;
244
+ this.check();
245
+ if (this.#pendingPause) {
246
+ this.pause(this.#pendingPause);
247
+ if (this.#pendingPause)
248
+ return;
249
+ }
250
+ const capable = this.#teamTool && !!this.ctx.tools.get("pbot_team_message", this.agent);
251
+ if (capable !== this.#ready) {
252
+ this.teams.files.setReady(this.d.agentId, this.generation, capable);
253
+ this.#ready = capable;
254
+ if (!capable)
255
+ this.pause("成员团队工具已停用");
256
+ }
257
+ if (this.#activeTeam) {
258
+ if (Date.now() >= this.#activeTeam.run.deadlineAt) {
259
+ this.pause("已到期限");
260
+ if (!this.humanPending() && !this.execution.interactive && this.execution.current?.source !== "mixed" && this.execution.current?.source !== "interactive")
261
+ this.agent.cancel({ kind: "hook", reason: "团队请求已到期限" });
262
+ }
263
+ return;
264
+ }
265
+ const links = this.teams.links();
266
+ const idle = () => this.agent.status === "idle" && !this.agent.inbox.nextTurn.length && !this.agent.inbox.nextStep.length && !this.execution.current && !this.execution.pending && !this.execution.interactive;
267
+ for (const request of this.files.messages(this.d.agentId)) {
268
+ if (request.generation !== this.generation || this.execution.accepted(request.requestId))
269
+ continue;
270
+ const link = links.get(request.requestId);
271
+ if (link && (link.team.closed || link.run.state !== "running" || link.run.deadlineAt <= Date.now()))
272
+ continue;
273
+ if (link && !idle())
274
+ break;
275
+ const message = await dshInput(this.ctx, this.d, request, this.#lifetime.signal);
276
+ this.check();
277
+ const submit = async () => {
278
+ this.check();
279
+ const accept = () => this.execution.claim(request.requestId);
280
+ if (link) {
281
+ if (!idle())
282
+ return;
283
+ this.#activeTeam = this.teams.claim(this.d.agentId, this.generation, request.requestId, accept);
284
+ if (!this.#activeTeam)
285
+ return;
286
+ }
287
+ else
288
+ accept();
289
+ if (request.delivery === "followUp")
290
+ this.agent.followup(message);
291
+ else
292
+ this.agent.steer(message);
293
+ await flushDshAgent(this.agent);
294
+ };
295
+ if (link) {
296
+ // Public status stays idle during native maintenance. Its idle admission is authoritative.
297
+ let entered = false;
298
+ try {
299
+ await this.agent.runMaintenance(async (signal) => { entered = true; signal.throwIfAborted(); await submit(); });
300
+ }
301
+ catch (error) {
302
+ if (entered)
303
+ throw error;
304
+ this.check();
305
+ }
306
+ }
307
+ else
308
+ await submit();
309
+ break;
310
+ }
311
+ }
312
+ stop() {
313
+ if (this.#stopping)
314
+ return this.#stopping;
315
+ this.#closing = true;
316
+ this.#lifetime.abort();
317
+ if (this.#timer)
318
+ clearInterval(this.#timer);
319
+ this.#timer = undefined;
320
+ this.#stopping = (async () => {
321
+ try {
322
+ if (this.current()) {
323
+ this.files.control(() => {
324
+ const run = this.files.read(this.d.agentId, "launch.json", launch);
325
+ if (run.generation === this.generation && !run.stop) {
326
+ run.stop = true;
327
+ this.files.write(this.d.agentId, "launch.json", run, launch);
328
+ }
329
+ });
330
+ this.teams.files.setReady(this.d.agentId, this.generation, false);
331
+ this.#ready = false;
332
+ this.pause("专家停止");
333
+ if (!this.#failure)
334
+ this.execution.save({ state: "stopping", activity: "停止" });
335
+ }
336
+ }
337
+ finally {
338
+ this.agent.cancel({ kind: "disposed" });
339
+ }
340
+ await this.agent.whenIdle();
341
+ await this.#pump;
342
+ await this.#events;
343
+ if (this.current() && !this.#failure)
344
+ this.execution.save({ state: "stopped", activity: "已停止" });
345
+ })();
346
+ return this.#stopping;
347
+ }
348
+ }
@@ -0,0 +1,4 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import { type PbotManagement } from "./management.js";
3
+ import type { DshWorkspaces } from "./workspace.js";
4
+ export declare function registerDshCommand(ctx: Context, workspaces: DshWorkspaces, management: PbotManagement): void;
@@ -0,0 +1,83 @@
1
+ import { Type } from "typebox";
2
+ import { object, json } from "../../common/json.js";
3
+ import { requireThat, schema } from "../../common/validation.js";
4
+ import { prepareWorkspace } from "../../services/workspace/workspace-preparation.js";
5
+ import { executeAgent } from "../../tools/agents/actions.js";
6
+ import { dshAgentActionSchema } from "../../tools/agents/dsh-contract.js";
7
+ import { executeTeam, executeTeamMessage } from "../../tools/teams/actions.js";
8
+ import { teamActionSchema, teamParametersSchema, teamMessageActionSchema, teamMessageParametersSchema } from "../../tools/teams/contract.js";
9
+ import { dshStatus } from "./management.js";
10
+ const validAgent = schema(dshAgentActionSchema), validTeam = schema(teamActionSchema), validMessage = schema(teamMessageActionSchema);
11
+ const chatSchema = Type.Object({ agentId: Type.String({ minLength: 1 }), text: Type.Optional(Type.String({ maxLength: 24000 })),
12
+ delivery: Type.Optional(Type.Union([Type.Literal("steer"), Type.Literal("followUp")])),
13
+ files: Type.Optional(Type.Array(Type.String({ minLength: 1 }), { maxItems: 32 })) }, { additionalProperties: false });
14
+ const validChat = schema(chatSchema);
15
+ const usage = '用法:/pbot status | init | python setup | agent {"action":…} | chat {"agentId":…,"text":…} | team {"action":…} | post {"teamId":…,"text":…} | history {"action":…} | records {"agentId":…}';
16
+ export function registerDshCommand(ctx, workspaces, management) {
17
+ ctx.commands.register({ name: "pbot", description: "Personal Bot 工作区与协作管理", recordInput: false,
18
+ input: { hint: "status | init | python setup | agent/chat/team/post/history/records JSON", attachments: true },
19
+ async handler({ agent, rawInput, signal, attachments, commandId }) {
20
+ signal.throwIfAborted();
21
+ const workspace = await workspaces.get(agent);
22
+ signal.throwIfAborted();
23
+ const command = rawInput.trim(), split = command.search(/\s/);
24
+ const verb = split < 0 ? command : command.slice(0, split);
25
+ requireThat(verb === "chat" || !attachments.length, "输入框附件仅用于 chat 人工对话。");
26
+ if (command === "init") {
27
+ await prepareWorkspace(workspace.root);
28
+ workspace.check();
29
+ return { kind: "success", text: "资料目录已准备;已有文件保留。" };
30
+ }
31
+ if (command === "python setup") {
32
+ const result = await workspaces.preparePython();
33
+ workspace.check();
34
+ return { kind: "success", text: JSON.stringify(result, null, 2) };
35
+ }
36
+ if (!command || command === "status")
37
+ return { kind: "success", text: JSON.stringify(dshStatus(workspaces, workspace), null, 2) };
38
+ requireThat(split >= 0, usage);
39
+ const input = JSON.parse(command.slice(split));
40
+ requireThat(object(input), "命令参数必须是 JSON 对象。");
41
+ let result;
42
+ switch (verb) {
43
+ case "agent": {
44
+ requireThat(validAgent(input), "dsh 专家参数无效或包含不支持的宿主选项。");
45
+ const selection = agent.model.current();
46
+ const output = await executeAgent(workspace.agents, input, workspace.owner, selection ? {
47
+ provider: selection.provider, model: selection.model,
48
+ ...(selection.reasoningEffort && (!input.provider || input.provider === selection.provider) && (!input.model || input.model === selection.model)
49
+ ? { thinking: selection.reasoningEffort } : {}),
50
+ } : {});
51
+ result = output.structuredContent;
52
+ break;
53
+ }
54
+ case "chat": {
55
+ requireThat(validChat(input), "人工对话参数无效。");
56
+ const args = input;
57
+ result = await workspace.expert(args.agentId).interact(args.text ?? "", args.delivery, args.files, attachments, signal);
58
+ break;
59
+ }
60
+ case "team": {
61
+ requireThat(validTeam(input), "团队参数无效。");
62
+ result = executeTeam(workspace.teams, workspace.agents, input, workspace.owner).structuredContent;
63
+ break;
64
+ }
65
+ case "post": {
66
+ const args = { ...input, action: "post" };
67
+ requireThat(input.action === undefined && validMessage(args), "公共消息参数无效。");
68
+ result = executeTeamMessage(workspace.teams, args, { kind: "user", owner: workspace.owner }, commandId).structuredContent;
69
+ break;
70
+ }
71
+ case "history":
72
+ result = await management.history({ parentSessionId: agent.id, input: json(input) }, signal);
73
+ break;
74
+ case "records":
75
+ result = await management.expert({ parentSessionId: agent.id, input: json(input) }, signal);
76
+ break;
77
+ default: throw new Error(usage);
78
+ }
79
+ workspace.check();
80
+ return { kind: "success", text: JSON.stringify(result, null, 2) };
81
+ },
82
+ });
83
+ }
@@ -0,0 +1,5 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { Agent } from "@deepseek-ai/dsh-agent";
3
+ /** Native filesystem mutations share the original workspace's confirmation rule.
4
+ * This does not confine Bash, Python, MCP, or other extensions. */
5
+ export declare function registerDshFileEvents(ctx: Context, agent: Agent, root: string, expert: boolean, check: () => void): void;
@@ -0,0 +1,67 @@
1
+ import { parsePatch } from "@deepseek-ai/dsh-fs-patch";
2
+ import { object } from "../../common/json.js";
3
+ import { requireThat } from "../../common/validation.js";
4
+ import { mutationScope } from "../../services/workspace/workspace-files.js";
5
+ /** Native filesystem mutations share the original workspace's confirmation rule.
6
+ * This does not confine Bash, Python, MCP, or other extensions. */
7
+ export function registerDshFileEvents(ctx, agent, root, expert, check) {
8
+ const lifetime = new AbortController();
9
+ const mutations = new Set(["write", "edit", "apply_patch"]);
10
+ const captured = new Map();
11
+ ctx.effect(() => () => { lifetime.abort(); captured.clear(); });
12
+ async function targets(exec) {
13
+ check();
14
+ const args = exec.arguments;
15
+ requireThat(exec.agent === agent && object(args), "文件操作会话或参数无效。");
16
+ const signal = AbortSignal.any([lifetime.signal, exec.signal]);
17
+ signal.throwIfAborted();
18
+ let paths;
19
+ if (exec.name === "apply_patch") {
20
+ requireThat(typeof args.input === "string", "补丁参数无效。");
21
+ paths = parsePatch(args.input).flatMap(operation => [operation.path, ...(operation.kind === "update" && operation.moveTo ? [operation.moveTo] : [])]);
22
+ }
23
+ else {
24
+ requireThat(typeof args.file_path === "string" && args.file_path.trim().length > 0, "文件路径无效。");
25
+ paths = [args.file_path];
26
+ }
27
+ const anchor = ctx.fs.processPathFromHostPath(root);
28
+ requireThat(anchor !== undefined, "原生文件系统未映射当前工作区。");
29
+ const cwd = ctx.workingDirectory.get(agent.session);
30
+ const resolved = await Promise.all(paths.map(path => ctx.fs.resolve(path, { cwd, signal })));
31
+ signal.throwIfAborted();
32
+ check();
33
+ const outside = resolved.map(target => ctx.fs.processPath(target)).filter(path => mutationScope(anchor, path) === "outside");
34
+ return { cwd, keys: resolved.map(target => target.targetKey), outside, signal };
35
+ }
36
+ ctx.on("tools/pre-execute", async (exec, next) => {
37
+ if (!mutations.has(exec.name))
38
+ return next();
39
+ const value = await targets(exec), record = { cwd: value.cwd, keys: value.keys, approved: !value.outside.length };
40
+ captured.set(exec.token, record);
41
+ if (value.outside.length && expert)
42
+ return { kind: "deny", reason: "专家不能修改工作区外文件。" };
43
+ const policy = await next();
44
+ value.signal.throwIfAborted();
45
+ check();
46
+ if (policy.kind === "deny" || policy.kind === "cancel" || !value.outside.length)
47
+ return policy;
48
+ const scopeReason = "Personal Bot: confirm changes outside the original workspace: " + JSON.stringify(value.outside);
49
+ const reason = [policy.kind === "ask" ? policy.reason : undefined, scopeReason].filter(Boolean).join("\n");
50
+ const outcome = await ctx.approval.request({ agent, toolName: exec.name, callId: exec.callId, signal: value.signal, reason,
51
+ displayReason: { en: reason, "zh-CN": "允许本次修改原工作区之外的文件:" + value.outside.join("、") + (policy.kind === "ask" && policy.reason ? "\n" + policy.reason : "") } });
52
+ value.signal.throwIfAborted();
53
+ check();
54
+ record.approved = outcome === "allowed-once";
55
+ return record.approved ? { kind: "allow" } : { kind: "deny", reason: "用户未确认文件修改。" };
56
+ });
57
+ ctx.on("tools/execute", async (exec, next) => {
58
+ if (!mutations.has(exec.name))
59
+ return next();
60
+ const record = captured.get(exec.token);
61
+ requireThat(record?.approved, "文件操作没有有效的范围检查或确认。");
62
+ const value = await targets(exec);
63
+ requireThat(record.cwd === value.cwd && record.keys.length === value.keys.length && record.keys.every((key, index) => key === value.keys[index]), "确认期间文件路径或工作目录已变化。");
64
+ return next();
65
+ });
66
+ ctx.on("tools/result", exec => { captured.delete(exec.token); });
67
+ }
@@ -0,0 +1,15 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { Agent } from "@deepseek-ai/dsh-agent";
3
+ import { HistoryService } from "../../services/history/history-service.js";
4
+ /** One server-owned organizer per workspace, shared by its parent Sessions. */
5
+ export declare class DshHistoryWork {
6
+ #private;
7
+ private readonly ctx;
8
+ readonly service: HistoryService;
9
+ constructor(ctx: Context, root: string);
10
+ attach(agent: Agent): () => void;
11
+ cancel(): void;
12
+ schedule(agent: Agent): void;
13
+ close(): Promise<void>;
14
+ }
15
+ export declare function registerDshHistoryEvents(ctx: Context, agent: Agent, work: DshHistoryWork, check: () => void): void;
@@ -0,0 +1,88 @@
1
+ import { join } from "node:path";
2
+ import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
3
+ import { AgentFiles } from "../../storage/agent-files.js";
4
+ import { HistoryFiles } from "../../storage/history-files.js";
5
+ import { HistoryService } from "../../services/history/history-service.js";
6
+ import { DshHistory } from "../../integrations/dsh/history.js";
7
+ import { dshHistorySummarizer } from "../../integrations/dsh/history-summary.js";
8
+ import { finalDshAnswer } from "../../integrations/dsh/messages.js";
9
+ /** One server-owned organizer per workspace, shared by its parent Sessions. */
10
+ export class DshHistoryWork {
11
+ ctx;
12
+ service;
13
+ #parents = new Set();
14
+ #controller;
15
+ #work;
16
+ #closing;
17
+ constructor(ctx, root) {
18
+ this.ctx = ctx;
19
+ const agents = new AgentFiles(dshHomePath("pbot", "subagents"), "dsh");
20
+ this.service = new HistoryService(root, new HistoryFiles(join(root, ".dsh/pbot/history"), "dsh"), new DshHistory(ctx, root, () => agents.list().filter(value => value.workspace === root).map(value => value.agentId)), undefined, new HistoryFiles(join(root, ".pi/pbot/history")));
21
+ }
22
+ attach(agent) { this.#parents.add(agent); return () => { this.#parents.delete(agent); }; }
23
+ cancel() { this.#controller?.abort(); }
24
+ schedule(agent) {
25
+ if (this.#closing || [...this.#parents].some(parent => parent.status !== "idle" || parent.inbox.nextTurn.length || parent.inbox.nextStep.length))
26
+ return;
27
+ this.cancel();
28
+ const controller = new AbortController(), selection = agent.model.current(), sessionId = agent.id;
29
+ this.#controller = controller;
30
+ const signal = controller.signal;
31
+ const work = (this.#work ?? Promise.resolve()).catch(() => { }).then(async () => {
32
+ signal.throwIfAborted();
33
+ await this.service.refresh({ host: "dsh", sessionId }, signal);
34
+ signal.throwIfAborted();
35
+ await this.service.organize(dshHistorySummarizer(this.ctx, selection, sessionId), signal);
36
+ }).catch(error => { if (!signal.aborted)
37
+ this.ctx.logger.warn("pbot 历史整理未完成:" + String(error)); });
38
+ this.#work = work;
39
+ void work.finally(() => { if (this.#work === work) {
40
+ this.#work = undefined;
41
+ this.#controller = undefined;
42
+ } });
43
+ }
44
+ close() {
45
+ if (this.#closing)
46
+ return this.#closing;
47
+ this.cancel();
48
+ this.#closing = (async () => { await this.#work; await this.service.close(); this.#parents.clear(); })();
49
+ return this.#closing;
50
+ }
51
+ }
52
+ export function registerDshHistoryEvents(ctx, agent, work, check) {
53
+ let normal = false, final = false;
54
+ ctx.effect(() => work.attach(agent));
55
+ ctx.on("session/event", (session, event) => {
56
+ if (session !== agent.session)
57
+ return;
58
+ if (event.type === "turn/start") {
59
+ normal = false;
60
+ final = false;
61
+ }
62
+ if (event.type === "assistant/message")
63
+ final = Boolean(finalDshAnswer(event).trim());
64
+ if (event.type === "agent/inbox/spliced" && event.data.inserted.some(message => message.source.kind === "user")) {
65
+ normal = false;
66
+ work.cancel();
67
+ }
68
+ if (event.type === "turn/end")
69
+ normal = event.data.reason.kind === "completed" && final;
70
+ });
71
+ ctx.on("agent/pre-step", async (_payload, next) => { work.cancel(); check(); return next(); });
72
+ ctx.on("agent/status", ({ agent: changed, status }) => {
73
+ if (changed !== agent || status !== "idle" || !normal)
74
+ return;
75
+ normal = false;
76
+ if (!agent.inbox.nextTurn.length && !agent.inbox.nextStep.length) {
77
+ try {
78
+ check();
79
+ }
80
+ catch (error) {
81
+ if (error.name === "AbortError")
82
+ return;
83
+ throw error;
84
+ }
85
+ work.schedule(agent);
86
+ }
87
+ });
88
+ }
@@ -0,0 +1,21 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import { TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
3
+ import { type Json } from "../../common/json.js";
4
+ import type { DshWorkspaces, DshWorkspace } from "./workspace.js";
5
+ export declare function dshStatus(workspaces: DshWorkspaces, workspace: DshWorkspace): Json;
6
+ /** Native Remote owns transport and access checks. These workspace reads do not admit or cancel execution. */
7
+ export declare class PbotManagement extends TypertRemoteService {
8
+ private readonly workspaces;
9
+ constructor(ctx: Context, workspaces: DshWorkspaces);
10
+ private workspace;
11
+ snapshot(request: Json, signal: AbortSignal): Promise<Json>;
12
+ expert(request: Json, signal: AbortSignal): Promise<Json>;
13
+ team(request: Json, signal: AbortSignal): Promise<Json>;
14
+ history(request: Json, signal: AbortSignal): Promise<Json>;
15
+ }
16
+ declare module "@deepseek-ai/cordis" {
17
+ interface Context {
18
+ pbotManagement: PbotManagement;
19
+ }
20
+ }
21
+ export declare function registerDshManagement(ctx: Context, workspaces: DshWorkspaces): PbotManagement;