personal-bot 0.2.3 → 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.
- package/CONTEXT.md +13 -7
- package/README.md +46 -9
- package/README.zh-CN.md +46 -9
- package/bin/pbot.mjs +3 -0
- package/docs/README.md +4 -1
- package/docs/agent-collaboration.md +26 -12
- package/docs/architecture-guidelines.md +2 -2
- package/docs/capability-design.md +6 -4
- package/docs/dsh-implementation-plan.md +158 -0
- package/docs/dsh-support-proposal.md +314 -0
- package/docs/dsh.md +103 -0
- package/docs/file-storage.md +14 -9
- package/docs/history.md +29 -18
- package/docs/installation-design.md +226 -0
- package/docs/interaction-design.md +13 -0
- package/docs/release.md +5 -5
- package/docs/technical-design.md +29 -15
- package/docs/validation.md +53 -3
- package/lib/client.js +1071 -0
- package/lib/common/files.d.ts +2 -0
- package/lib/common/files.js +44 -0
- package/lib/common/json.d.ts +6 -0
- package/lib/common/json.js +16 -0
- package/lib/common/markdown.d.ts +5 -0
- package/lib/common/markdown.js +26 -0
- package/lib/common/package-source.js +16 -0
- package/lib/common/paths.d.ts +1 -0
- package/lib/common/paths.js +2 -0
- package/lib/common/validation.d.ts +3 -0
- package/lib/common/validation.js +14 -0
- package/lib/common/version.d.ts +3 -0
- package/lib/common/version.js +13 -0
- package/lib/dsh.d.ts +4 -0
- package/lib/dsh.js +22 -0
- package/lib/extension/dsh/agent-events.d.ts +24 -0
- package/lib/extension/dsh/agent-events.js +242 -0
- package/lib/extension/dsh/agent-runtime.d.ts +49 -0
- package/lib/extension/dsh/agent-runtime.js +348 -0
- package/lib/extension/dsh/command.d.ts +4 -0
- package/lib/extension/dsh/command.js +83 -0
- package/lib/extension/dsh/files.d.ts +5 -0
- package/lib/extension/dsh/files.js +67 -0
- package/lib/extension/dsh/history.d.ts +15 -0
- package/lib/extension/dsh/history.js +88 -0
- package/lib/extension/dsh/management.d.ts +21 -0
- package/lib/extension/dsh/management.js +217 -0
- package/lib/extension/dsh/workspace.d.ts +48 -0
- package/lib/extension/dsh/workspace.js +290 -0
- package/lib/extension/setup-cli.js +136 -0
- package/lib/extension/setup-guard.js +11 -0
- package/lib/integrations/agent-resources.d.ts +1 -0
- package/lib/integrations/agent-resources.js +18 -0
- package/lib/integrations/dsh/dsh-agents.d.ts +30 -0
- package/lib/integrations/dsh/dsh-agents.js +104 -0
- package/lib/integrations/dsh/history-summary.d.ts +11 -0
- package/lib/integrations/dsh/history-summary.js +40 -0
- package/lib/integrations/dsh/history.d.ts +16 -0
- package/lib/integrations/dsh/history.js +156 -0
- package/lib/integrations/dsh/input.d.ts +21 -0
- package/lib/integrations/dsh/input.js +62 -0
- package/lib/integrations/dsh/messages.d.ts +36 -0
- package/lib/integrations/dsh/messages.js +11 -0
- package/lib/integrations/dsh/notifications.d.ts +8 -0
- package/lib/integrations/dsh/notifications.js +55 -0
- package/lib/integrations/dsh/resources.d.ts +10 -0
- package/lib/integrations/dsh/resources.js +41 -0
- package/lib/integrations/dsh/schema.d.ts +7 -0
- package/lib/integrations/dsh/schema.js +28 -0
- package/lib/integrations/dsh/session.d.ts +14 -0
- package/lib/integrations/dsh/session.js +59 -0
- package/lib/integrations/history-summary.d.ts +2 -0
- package/lib/integrations/history-summary.js +21 -0
- package/lib/integrations/installation/pi-bridge.js +131 -0
- package/lib/integrations/installation/pi-setup.js +193 -0
- package/lib/integrations/installation/processes.js +155 -0
- package/lib/integrations/installation/workspace.js +21 -0
- package/lib/integrations/process.d.ts +14 -0
- package/lib/integrations/process.js +48 -0
- package/lib/integrations/python-activation.d.ts +2 -0
- package/lib/integrations/python-activation.js +29 -0
- package/lib/integrations/team-context.d.ts +18 -0
- package/lib/integrations/team-context.js +57 -0
- package/lib/model/agent.d.ts +92 -0
- package/lib/model/agent.js +1 -0
- package/lib/model/history.d.ts +43 -0
- package/lib/model/history.js +1 -0
- package/lib/model/team.d.ts +85 -0
- package/lib/model/team.js +1 -0
- package/lib/services/agents/agent-notices.d.ts +13 -0
- package/lib/services/agents/agent-notices.js +25 -0
- package/lib/services/agents/agent-run.d.ts +39 -0
- package/lib/services/agents/agent-run.js +138 -0
- package/lib/services/agents/agent-service.d.ts +166 -0
- package/lib/services/agents/agent-service.js +325 -0
- package/lib/services/agents/team-service.d.ts +168 -0
- package/lib/services/agents/team-service.js +343 -0
- package/lib/services/history/history-service.d.ts +103 -0
- package/lib/services/history/history-service.js +269 -0
- package/lib/services/installation/setup-service.js +121 -0
- package/lib/services/workspace/python-environment-service.d.ts +15 -0
- package/lib/services/workspace/python-environment-service.js +50 -0
- package/lib/services/workspace/workspace-files.d.ts +2 -0
- package/lib/services/workspace/workspace-files.js +17 -0
- package/lib/services/workspace/workspace-preparation.d.ts +2 -0
- package/lib/services/workspace/workspace-preparation.js +18 -0
- package/lib/storage/agent-files.d.ts +30 -0
- package/lib/storage/agent-files.js +166 -0
- package/lib/storage/atomic-json.d.ts +11 -0
- package/lib/storage/atomic-json.js +101 -0
- package/lib/storage/file-access.d.ts +2 -0
- package/lib/storage/file-access.js +26 -0
- package/lib/storage/history-files.d.ts +10 -0
- package/lib/storage/history-files.js +63 -0
- package/lib/storage/team-files.d.ts +16 -0
- package/lib/storage/team-files.js +111 -0
- package/lib/tools/agents/actions.d.ts +52 -0
- package/lib/tools/agents/actions.js +49 -0
- package/lib/tools/agents/contract.d.ts +163 -0
- package/lib/tools/agents/contract.js +56 -0
- package/lib/tools/agents/dsh-contract.d.ts +52 -0
- package/lib/tools/agents/dsh-contract.js +15 -0
- package/lib/tools/agents/dsh-tool.d.ts +5 -0
- package/lib/tools/agents/dsh-tool.js +38 -0
- package/lib/tools/history/actions.d.ts +70 -0
- package/lib/tools/history/actions.js +7 -0
- package/lib/tools/history/contract.d.ts +81 -0
- package/lib/tools/history/contract.js +32 -0
- package/lib/tools/history/dsh-tool.d.ts +4 -0
- package/lib/tools/history/dsh-tool.js +22 -0
- package/lib/tools/result.d.ts +18 -0
- package/lib/tools/result.js +10 -0
- package/lib/tools/teams/actions.d.ts +24 -0
- package/lib/tools/teams/actions.js +38 -0
- package/lib/tools/teams/contract.d.ts +232 -0
- package/lib/tools/teams/contract.js +67 -0
- package/lib/tools/teams/dsh-tool.d.ts +8 -0
- package/lib/tools/teams/dsh-tool.js +49 -0
- package/lib/ui/dsh/attachments.d.ts +5 -0
- package/lib/ui/dsh/attachments.js +39 -0
- package/lib/ui/dsh/elements.d.ts +17 -0
- package/lib/ui/dsh/elements.js +79 -0
- package/lib/ui/dsh/index.d.ts +4 -0
- package/lib/ui/dsh/index.js +13 -0
- package/lib/ui/dsh/management.d.ts +4 -0
- package/lib/ui/dsh/management.js +711 -0
- package/lib/ui/dsh/remote.d.ts +15 -0
- package/lib/ui/dsh/remote.js +11 -0
- package/lib/ui/dsh/styles.d.ts +1 -0
- package/lib/ui/dsh/styles.js +132 -0
- package/lib/ui/presentation.d.ts +3 -0
- package/lib/ui/presentation.js +12 -0
- package/lib/ui/setup.js +52 -0
- package/npm-shrinkwrap.json +11118 -783
- package/package.json +113 -9
- package/python/README.md +6 -6
- package/resources/agents/coordinator.md +1 -1
- package/resources/dsh/cordis.patch.yml +3 -0
- package/resources/installer/plugins.json +16 -0
- package/resources/prompts/workspace.md +1 -1
- package/resources/references/compute.md +1 -1
- package/resources/references/task-conductor.md +6 -4
- package/resources/skills/provider-connections/SKILL.md +10 -0
- package/scripts/README.md +13 -1
- package/src/common/package-source.ts +16 -0
- package/src/dsh.ts +22 -0
- package/src/extension/agent-events.ts +51 -46
- package/src/extension/agents-command.ts +48 -34
- package/src/extension/command.ts +42 -23
- package/src/extension/dsh/agent-events.ts +148 -0
- package/src/extension/dsh/agent-runtime.ts +251 -0
- package/src/extension/dsh/command.ts +78 -0
- package/src/extension/dsh/files.ts +61 -0
- package/src/extension/dsh/history.ts +69 -0
- package/src/extension/dsh/management.ts +108 -0
- package/src/extension/dsh/workspace.ts +206 -0
- package/src/extension/events.ts +6 -4
- package/src/extension/history.ts +15 -12
- package/src/extension/setup-cli.ts +109 -0
- package/src/extension/setup-guard.ts +12 -0
- package/src/extension/subagent-entry.ts +34 -63
- package/src/extension/teams-command.ts +53 -46
- package/src/integrations/{pi/agent-resources.ts → agent-resources.ts} +2 -2
- package/src/integrations/dsh/dsh-agents.ts +97 -0
- package/src/integrations/dsh/history-summary.ts +43 -0
- package/src/integrations/dsh/history.ts +76 -0
- package/src/integrations/dsh/input.ts +66 -0
- package/src/integrations/dsh/messages.ts +22 -0
- package/src/integrations/dsh/notifications.ts +46 -0
- package/src/integrations/dsh/resources.ts +44 -0
- package/src/integrations/dsh/schema.ts +26 -0
- package/src/integrations/dsh/session.ts +67 -0
- package/src/integrations/history-summary.ts +20 -0
- package/src/integrations/installation/pi-bridge.ts +107 -0
- package/src/integrations/installation/pi-setup.ts +162 -0
- package/src/integrations/installation/processes.ts +124 -0
- package/src/integrations/installation/workspace.ts +18 -0
- package/src/integrations/pi/history.ts +4 -14
- package/src/integrations/pi/pi-agents.ts +17 -6
- package/src/integrations/pi/team-context.ts +6 -58
- package/src/integrations/team-context.ts +60 -0
- package/src/model/agent.ts +2 -0
- package/src/model/history.ts +3 -1
- package/src/pbot.ts +1 -1
- package/src/services/agents/agent-notices.ts +28 -0
- package/src/services/agents/agent-run.ts +114 -0
- package/src/services/agents/agent-service.ts +127 -64
- package/src/services/history/history-service.ts +120 -59
- package/src/services/installation/setup-service.ts +145 -0
- package/src/storage/agent-files.ts +17 -4
- package/src/storage/history-files.ts +30 -5
- package/src/tools/agents/actions.ts +47 -0
- package/src/tools/agents/contract.ts +8 -5
- package/src/tools/agents/dsh-contract.ts +16 -0
- package/src/tools/agents/dsh-tool.ts +41 -0
- package/src/tools/agents/tool.ts +7 -43
- package/src/tools/history/actions.ts +9 -0
- package/src/tools/history/contract.ts +34 -0
- package/src/tools/history/dsh-tool.ts +22 -0
- package/src/tools/history/tool.ts +7 -30
- package/src/tools/teams/actions.ts +38 -0
- package/src/tools/teams/contract.ts +7 -3
- package/src/tools/teams/dsh-tool.ts +55 -0
- package/src/tools/teams/tool.ts +6 -31
- package/src/ui/agent-display.ts +7 -4
- package/src/ui/dsh/attachments.ts +42 -0
- package/src/ui/dsh/elements.ts +50 -0
- package/src/ui/dsh/index.ts +18 -0
- package/src/ui/dsh/management.ts +412 -0
- package/src/ui/dsh/remote.ts +20 -0
- package/src/ui/dsh/styles.ts +132 -0
- package/src/ui/pi/menu.ts +37 -0
- package/src/ui/pi/reader.ts +46 -0
- package/src/ui/presentation.ts +12 -0
- package/src/ui/setup.ts +40 -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;
|