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,44 @@
|
|
|
1
|
+
import { open, realpath, rename, unlink, link } from "node:fs/promises";
|
|
2
|
+
import { join, dirname, basename } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { requireThat } from "./validation.js";
|
|
5
|
+
export async function atomicFile(path, content, exclusive = false) {
|
|
6
|
+
const temp = path + "." + randomUUID();
|
|
7
|
+
const handle = await open(temp, "wx", 0o600);
|
|
8
|
+
try {
|
|
9
|
+
await handle.writeFile(content);
|
|
10
|
+
await handle.sync();
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
await handle.close();
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
if (exclusive)
|
|
17
|
+
await link(temp, path);
|
|
18
|
+
else
|
|
19
|
+
await rename(temp, path);
|
|
20
|
+
const directory = await open(dirname(path), "r");
|
|
21
|
+
try {
|
|
22
|
+
await directory.sync();
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await directory.close();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
await unlink(temp).catch(error => { if (error.code !== "ENOENT")
|
|
30
|
+
throw error; });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function mutationTarget(path) {
|
|
34
|
+
try {
|
|
35
|
+
return await realpath(path);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code !== "ENOENT")
|
|
39
|
+
throw error;
|
|
40
|
+
const parent = dirname(path);
|
|
41
|
+
requireThat(parent !== path, "无法定位目标路径。");
|
|
42
|
+
return join(await mutationTarget(parent), basename(path));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type Json = null | boolean | number | string | Json[] | {
|
|
2
|
+
[key: string]: Json;
|
|
3
|
+
};
|
|
4
|
+
export declare function object(value: unknown): value is Record<string, unknown>;
|
|
5
|
+
export declare function canonical(value: unknown): string;
|
|
6
|
+
export declare function json(value: unknown): Json;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function object(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function canonical(value) {
|
|
5
|
+
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
6
|
+
return JSON.stringify(value);
|
|
7
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return "[" + value.map(canonical).join(",") + "]";
|
|
11
|
+
if (object(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) {
|
|
12
|
+
return "{" + Object.keys(value).sort().map(key => JSON.stringify(key) + ":" + canonical(value[key])).join(",") + "}";
|
|
13
|
+
}
|
|
14
|
+
throw new Error("需要有限、可序列化的 JSON 值。");
|
|
15
|
+
}
|
|
16
|
+
export function json(value) { return JSON.parse(canonical(value)); }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Json } from "./json.js";
|
|
2
|
+
/** Preserve original text, including embedded Markdown fences. */
|
|
3
|
+
export declare function markdownBlock(text: string): string;
|
|
4
|
+
/** Render structured result fields without changing their machine-readable values. */
|
|
5
|
+
export declare function markdownFields(value: Json, level?: number): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Preserve original text, including embedded Markdown fences. */
|
|
2
|
+
export function markdownBlock(text) {
|
|
3
|
+
let fenceLength = 3;
|
|
4
|
+
for (const match of text.matchAll(/`+/g))
|
|
5
|
+
fenceLength = Math.max(fenceLength, match[0].length + 1);
|
|
6
|
+
const fence = "`".repeat(fenceLength);
|
|
7
|
+
return fence + "text\n" + text + "\n" + fence;
|
|
8
|
+
}
|
|
9
|
+
/** Render structured result fields without changing their machine-readable values. */
|
|
10
|
+
export function markdownFields(value, level = 2) {
|
|
11
|
+
if (value === null || typeof value !== "object")
|
|
12
|
+
return typeof value === "string" ? markdownBlock(value) : String(value);
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return value.length ? value.map((item, index) => "#".repeat(level) + " " + (index + 1) + "\n\n" + markdownFields(item, level + 1)).join("\n\n") : "(空)";
|
|
15
|
+
const fields = [], sections = [];
|
|
16
|
+
for (const [key, item] of Object.entries(value)) {
|
|
17
|
+
if ((item !== null && typeof item === "object") || (typeof item === "string" && (item.includes("\n") || key === "report" || key === "text"))) {
|
|
18
|
+
sections.push("#".repeat(level) + " " + key + "\n\n" + markdownFields(item, level + 1));
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const text = String(item).replace(/[\\`*_{}\[\]<>#!|]/g, "\\$&");
|
|
22
|
+
fields.push("- " + key + ": " + text);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return [fields.join("\n"), ...sections].filter(Boolean).join("\n\n");
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { prerelease, satisfies, valid } from "semver";
|
|
2
|
+
export function npmName(source) {
|
|
3
|
+
return /^npm:((?:@[a-z0-9._-]+\/)?[a-z0-9._-]+)(?:@[^\s]+)?$/.exec(source)?.[1];
|
|
4
|
+
}
|
|
5
|
+
export function ordinaryNpm(source) {
|
|
6
|
+
const name = npmName(source);
|
|
7
|
+
return name !== undefined && (source === `npm:${name}` || source === `npm:${name}@latest`);
|
|
8
|
+
}
|
|
9
|
+
export function checkStableVersion(version, nodeRange) {
|
|
10
|
+
if (!valid(version) || prerelease(version))
|
|
11
|
+
throw new Error(`latest 不是稳定版本:${version}`);
|
|
12
|
+
if (nodeRange && !satisfies(process.versions.node, nodeRange)) {
|
|
13
|
+
throw new Error(`目标版本要求 Node.js ${nodeRange},当前为 ${process.versions.node}。请先升级 Node.js。`);
|
|
14
|
+
}
|
|
15
|
+
return version;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const resourceRoot: string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Ajv } from "ajv";
|
|
2
|
+
import addFormats from "ajv-formats";
|
|
3
|
+
import { object } from "./json.js";
|
|
4
|
+
export function schema(value) {
|
|
5
|
+
if (!object(value))
|
|
6
|
+
throw new Error("需要对象形式的 JSON Schema。");
|
|
7
|
+
const ajv = new Ajv({ strict: false, allErrors: false, validateFormats: true, coerceTypes: false, useDefaults: false, removeAdditional: false });
|
|
8
|
+
addFormats.default(ajv);
|
|
9
|
+
return ajv.compile(value);
|
|
10
|
+
}
|
|
11
|
+
export function requireThat(condition, message) {
|
|
12
|
+
if (!condition)
|
|
13
|
+
throw new Error(message);
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { object } from "./json.js";
|
|
3
|
+
import { requireThat } from "./validation.js";
|
|
4
|
+
/** Before 1.0, the minor release defines compatibility; afterwards, the major does. */
|
|
5
|
+
export function releaseLine(version) {
|
|
6
|
+
const parts = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.exec(version);
|
|
7
|
+
requireThat(parts, "pbot 发布版本必须使用 MAJOR.MINOR.PATCH。");
|
|
8
|
+
return parts[1] === "0" ? `0.${parts[2]}` : parts[1];
|
|
9
|
+
}
|
|
10
|
+
const manifest = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
|
|
11
|
+
requireThat(object(manifest) && typeof manifest.version === "string", "pbot 包缺少发布版本。");
|
|
12
|
+
export const packageVersion = manifest.version;
|
|
13
|
+
releaseLine(packageVersion);
|
package/lib/dsh.d.ts
ADDED
package/lib/dsh.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { registerDshCommand } from "./extension/dsh/command.js";
|
|
2
|
+
import { registerDshManagement } from "./extension/dsh/management.js";
|
|
3
|
+
import { DshWorkspaces, dshDependencies } from "./extension/dsh/workspace.js";
|
|
4
|
+
import { requireThat } from "./common/validation.js";
|
|
5
|
+
export const name = "personal-bot";
|
|
6
|
+
export const inject = ["commands", "typert", ...dshDependencies];
|
|
7
|
+
export async function apply(ctx) {
|
|
8
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
9
|
+
requireThat(major > 24 || (major === 24 && minor >= 9), "dsh 接入需要 Node.js 24.9.0 或更新版本。");
|
|
10
|
+
const workspaces = new DshWorkspaces(ctx);
|
|
11
|
+
ctx.effect(() => () => workspaces.close());
|
|
12
|
+
const management = registerDshManagement(ctx, workspaces);
|
|
13
|
+
registerDshCommand(ctx, workspaces, management);
|
|
14
|
+
ctx.on("agent/created", async ({ agent }) => {
|
|
15
|
+
if (ctx.agents.roots().includes(agent) && agent.session.header.origin !== "subagent")
|
|
16
|
+
await workspaces.open(agent);
|
|
17
|
+
});
|
|
18
|
+
for (const agent of ctx.agents.roots())
|
|
19
|
+
if (agent.session.header.origin !== "subagent")
|
|
20
|
+
await workspaces.open(agent);
|
|
21
|
+
void workspaces.preparePython().catch(error => ctx.logger.warn(String(error)));
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
3
|
+
import type { AgentService } from "../../services/agents/agent-service.js";
|
|
4
|
+
import type { TeamService } from "../../services/agents/team-service.js";
|
|
5
|
+
import type { AgentOwner } from "../../model/agent.js";
|
|
6
|
+
/** Server-owned scheduling survives browser disconnects; native cancellation pauses dispatch. */
|
|
7
|
+
export declare class DshAgentEvents {
|
|
8
|
+
#private;
|
|
9
|
+
readonly ctx: Context;
|
|
10
|
+
readonly agent: Agent;
|
|
11
|
+
readonly agents: AgentService;
|
|
12
|
+
readonly teams: TeamService;
|
|
13
|
+
readonly owner: AgentOwner;
|
|
14
|
+
readonly check: () => void;
|
|
15
|
+
constructor(ctx: Context, agent: Agent, agents: AgentService, teams: TeamService, owner: AgentOwner, check: () => void);
|
|
16
|
+
get error(): string | undefined;
|
|
17
|
+
start(): void;
|
|
18
|
+
private pauseTeams;
|
|
19
|
+
private recordError;
|
|
20
|
+
private notice;
|
|
21
|
+
refresh(): Promise<void>;
|
|
22
|
+
private scan;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
3
|
+
import { agentNotice, teamNotice } from "../../services/agents/agent-notices.js";
|
|
4
|
+
import { deliveredDshNotices, pendingDshNotices } from "../../integrations/dsh/notifications.js";
|
|
5
|
+
import { flushDshAgent } from "../../integrations/dsh/session.js";
|
|
6
|
+
/** Server-owned scheduling survives browser disconnects; native cancellation pauses dispatch. */
|
|
7
|
+
export class DshAgentEvents {
|
|
8
|
+
ctx;
|
|
9
|
+
agent;
|
|
10
|
+
agents;
|
|
11
|
+
teams;
|
|
12
|
+
owner;
|
|
13
|
+
check;
|
|
14
|
+
#dispatching = new Map();
|
|
15
|
+
#signals = new Map();
|
|
16
|
+
#timer;
|
|
17
|
+
#scan;
|
|
18
|
+
#closed = false;
|
|
19
|
+
#pausePending = false;
|
|
20
|
+
#paused = false;
|
|
21
|
+
#error;
|
|
22
|
+
#errorKey;
|
|
23
|
+
#busySince;
|
|
24
|
+
#recovery;
|
|
25
|
+
#completed = [];
|
|
26
|
+
#autoWake = false;
|
|
27
|
+
constructor(ctx, agent, agents, teams, owner, check) {
|
|
28
|
+
this.ctx = ctx;
|
|
29
|
+
this.agent = agent;
|
|
30
|
+
this.agents = agents;
|
|
31
|
+
this.teams = teams;
|
|
32
|
+
this.owner = owner;
|
|
33
|
+
this.check = check;
|
|
34
|
+
}
|
|
35
|
+
get error() { return this.#error; }
|
|
36
|
+
start() {
|
|
37
|
+
this.teams.recover(this.owner);
|
|
38
|
+
this.ctx.on("agent/pre-step", async ({ signal }, next) => {
|
|
39
|
+
if (!this.#signals.has(signal)) {
|
|
40
|
+
const cancel = () => {
|
|
41
|
+
if (!this.#closed) {
|
|
42
|
+
this.#paused = true;
|
|
43
|
+
this.#autoWake = false;
|
|
44
|
+
this.#pausePending = true;
|
|
45
|
+
try {
|
|
46
|
+
this.pauseTeams();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
this.recordError(error);
|
|
50
|
+
}
|
|
51
|
+
void this.refresh();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
this.#signals.set(signal, cancel);
|
|
55
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
56
|
+
if (signal.aborted)
|
|
57
|
+
cancel();
|
|
58
|
+
}
|
|
59
|
+
await this.refresh();
|
|
60
|
+
this.check();
|
|
61
|
+
const decision = await next();
|
|
62
|
+
this.check();
|
|
63
|
+
if (decision.kind !== "enter" || this.#paused)
|
|
64
|
+
return decision;
|
|
65
|
+
const note = this.notice(decision.messages);
|
|
66
|
+
return note ? { ...decision, messages: [...decision.messages, note] } : decision;
|
|
67
|
+
});
|
|
68
|
+
this.ctx.on("session/event", (session, event) => {
|
|
69
|
+
if (session !== this.agent.session || this.#closed)
|
|
70
|
+
return;
|
|
71
|
+
if (event.type === "agent/inbox/spliced" && event.data.inserted.some(message => message.source.kind === "user")) {
|
|
72
|
+
this.#paused = false;
|
|
73
|
+
this.#autoWake = true;
|
|
74
|
+
}
|
|
75
|
+
if (event.type === "user/message" && event.data.source.kind === "pbot-results")
|
|
76
|
+
this.#autoWake = true;
|
|
77
|
+
if (event.type === "turn/end") {
|
|
78
|
+
for (const [signal, cancel] of this.#signals)
|
|
79
|
+
signal.removeEventListener("abort", cancel);
|
|
80
|
+
this.#signals.clear();
|
|
81
|
+
if (event.data.reason.kind === "aborted") {
|
|
82
|
+
this.#paused = true;
|
|
83
|
+
this.#autoWake = false;
|
|
84
|
+
this.#pausePending = true;
|
|
85
|
+
try {
|
|
86
|
+
this.pauseTeams();
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
this.recordError(error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
void this.refresh();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
this.#timer = setInterval(() => { void this.refresh(); }, 1000);
|
|
96
|
+
this.#timer.unref();
|
|
97
|
+
this.ctx.effect(() => () => this.close());
|
|
98
|
+
}
|
|
99
|
+
pauseTeams() {
|
|
100
|
+
if (!this.#pausePending)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
this.teams.pauseAll(this.owner, "主会话中断");
|
|
104
|
+
this.#pausePending = false;
|
|
105
|
+
this.#dispatching.clear();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error.code !== "PBOT_CONTROL_BUSY") {
|
|
109
|
+
this.teams.fail(error);
|
|
110
|
+
this.#dispatching.clear();
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
recordError(error) {
|
|
116
|
+
if (this.#closed || error.name === "AbortError")
|
|
117
|
+
return;
|
|
118
|
+
if (error.code === "PBOT_CONTROL_BUSY") {
|
|
119
|
+
this.#busySince ??= Date.now();
|
|
120
|
+
if (Date.now() - this.#busySince < 5000)
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
else
|
|
124
|
+
this.#busySince = undefined;
|
|
125
|
+
const message = String(error);
|
|
126
|
+
let recovery = error.code === "PBOT_CONTROL_BUSY" ? "retry" : "repair";
|
|
127
|
+
try {
|
|
128
|
+
this.teams.check();
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
recovery = "reload";
|
|
132
|
+
}
|
|
133
|
+
if (message !== this.#error || recovery !== this.#recovery) {
|
|
134
|
+
this.ctx.logger.warn("pbot 协作扫描失败:" + message);
|
|
135
|
+
this.#errorKey = "agent-error:" + randomUUID();
|
|
136
|
+
}
|
|
137
|
+
this.#error = message;
|
|
138
|
+
this.#recovery = recovery;
|
|
139
|
+
}
|
|
140
|
+
notice(proposed = []) {
|
|
141
|
+
const known = new Set([...deliveredDshNotices(this.agent.session.snapshotEvents()),
|
|
142
|
+
...pendingDshNotices([...this.agent.inbox.nextTurn, ...this.agent.inbox.nextStep, ...proposed])]);
|
|
143
|
+
let value;
|
|
144
|
+
if (this.#error && this.#errorKey) {
|
|
145
|
+
if (!known.has(this.#errorKey))
|
|
146
|
+
value = { keys: [this.#errorKey], content: "# 协作扫描故障\n\n" + this.#error +
|
|
147
|
+
"\n\n先核对控制记录和原生报告;结果未知不是重跑许可。" +
|
|
148
|
+
(this.#recovery === "reload" ? "团队服务已停用;修复文件后还需重载服务端 personal-bot 插件。" :
|
|
149
|
+
this.#recovery === "retry" ? "控制锁持续占用,后台会继续重试;不会自动删除锁或重新提交任务。" : "报告或控制记录读取失败,修复后后台会重试扫描。") +
|
|
150
|
+
(this.#pausePending ? "\n\n团队暂停尚未确认,不能据此开始成员私人任务。" : "") };
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
value = teamNotice(this.teams.notifications(this.owner, this.agents.list(this.owner)), known) ??
|
|
154
|
+
agentNotice(this.#completed, this.teams.links(), known);
|
|
155
|
+
}
|
|
156
|
+
return value ? createUserMessage({ content: [{ type: "text", text: value.content }],
|
|
157
|
+
source: { kind: "pbot-results", form: "notice", summary: "Personal Bot 协作更新", keys: value.keys } }) : undefined;
|
|
158
|
+
}
|
|
159
|
+
refresh() {
|
|
160
|
+
if (this.#closed || this.#scan)
|
|
161
|
+
return this.#scan ?? Promise.resolve();
|
|
162
|
+
this.#scan = (async () => {
|
|
163
|
+
try {
|
|
164
|
+
await this.scan();
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
this.recordError(error);
|
|
168
|
+
}
|
|
169
|
+
if (this.#closed || this.#paused || !this.#autoWake || this.agent.status !== "idle")
|
|
170
|
+
return;
|
|
171
|
+
try {
|
|
172
|
+
this.check();
|
|
173
|
+
const note = this.notice();
|
|
174
|
+
if (note) {
|
|
175
|
+
// A failed admission cannot start an automatic retry loop. Actual receipt or new human input re-arms wakeup.
|
|
176
|
+
this.#autoWake = false;
|
|
177
|
+
this.agent.followup(note);
|
|
178
|
+
await flushDshAgent(this.agent);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
this.recordError(error);
|
|
183
|
+
}
|
|
184
|
+
})().finally(() => { this.#scan = undefined; });
|
|
185
|
+
return this.#scan;
|
|
186
|
+
}
|
|
187
|
+
async scan() {
|
|
188
|
+
this.check();
|
|
189
|
+
this.pauseTeams();
|
|
190
|
+
await this.agents.refresh(this.owner);
|
|
191
|
+
if (this.#closed)
|
|
192
|
+
return;
|
|
193
|
+
this.check();
|
|
194
|
+
const completed = await this.agents.completions(this.owner);
|
|
195
|
+
if (this.#closed)
|
|
196
|
+
return;
|
|
197
|
+
this.check();
|
|
198
|
+
this.#completed = completed;
|
|
199
|
+
try {
|
|
200
|
+
this.pauseTeams();
|
|
201
|
+
for (const input of this.teams.advance(this.owner, this.agents.list(this.owner), completed))
|
|
202
|
+
this.#dispatching.set(input.requestId, input);
|
|
203
|
+
for (const [id, input] of this.#dispatching) {
|
|
204
|
+
try {
|
|
205
|
+
this.agents.enqueueTeam(input, this.owner);
|
|
206
|
+
this.#dispatching.delete(id);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
const code = error.code;
|
|
210
|
+
if (code === "PBOT_CONTROL_BUSY")
|
|
211
|
+
throw error;
|
|
212
|
+
if (code !== "TEAM_INPUT_STALE")
|
|
213
|
+
throw error;
|
|
214
|
+
this.teams.pauseMember(input.agentId, "安排失效;未自动重发");
|
|
215
|
+
this.#dispatching.delete(id);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (error.code !== "PBOT_CONTROL_BUSY") {
|
|
221
|
+
this.teams.fail(error);
|
|
222
|
+
this.#dispatching.clear();
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
this.#error = undefined;
|
|
227
|
+
this.#errorKey = undefined;
|
|
228
|
+
this.#busySince = undefined;
|
|
229
|
+
this.#recovery = undefined;
|
|
230
|
+
}
|
|
231
|
+
async close() {
|
|
232
|
+
this.#closed = true;
|
|
233
|
+
if (this.#timer)
|
|
234
|
+
clearInterval(this.#timer);
|
|
235
|
+
this.#timer = undefined;
|
|
236
|
+
for (const [signal, cancel] of this.#signals)
|
|
237
|
+
signal.removeEventListener("abort", cancel);
|
|
238
|
+
this.#signals.clear();
|
|
239
|
+
await this.#scan;
|
|
240
|
+
this.#dispatching.clear();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
3
|
+
import { type FileBlock, type ImageBlock } from "@deepseek-ai/dsh-llm";
|
|
4
|
+
import { AgentRun } from "../../services/agents/agent-run.js";
|
|
5
|
+
import type { TeamService } from "../../services/agents/team-service.js";
|
|
6
|
+
import { AgentFiles } from "../../storage/agent-files.js";
|
|
7
|
+
import type { AgentDescriptor } from "../../model/agent.js";
|
|
8
|
+
/** One expert's native event adapter. AgentRun and TeamService own durable business state. */
|
|
9
|
+
export declare class DshAgentRuntime {
|
|
10
|
+
#private;
|
|
11
|
+
readonly ctx: Context;
|
|
12
|
+
readonly agent: Agent;
|
|
13
|
+
readonly d: AgentDescriptor;
|
|
14
|
+
readonly generation: string;
|
|
15
|
+
readonly files: AgentFiles;
|
|
16
|
+
readonly teams: TeamService;
|
|
17
|
+
readonly owner: {
|
|
18
|
+
check(): void;
|
|
19
|
+
release(): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
readonly execution: AgentRun;
|
|
22
|
+
constructor(ctx: Context, agent: Agent, d: AgentDescriptor, generation: string, files: AgentFiles, teams: TeamService, owner: {
|
|
23
|
+
check(): void;
|
|
24
|
+
release(): Promise<void>;
|
|
25
|
+
});
|
|
26
|
+
check: () => void;
|
|
27
|
+
actor: () => {
|
|
28
|
+
requestId?: string;
|
|
29
|
+
kind: "member";
|
|
30
|
+
agentId: string;
|
|
31
|
+
generation: string;
|
|
32
|
+
};
|
|
33
|
+
private current;
|
|
34
|
+
private pause;
|
|
35
|
+
private enqueue;
|
|
36
|
+
private fail;
|
|
37
|
+
initialize(teamTool: boolean): void;
|
|
38
|
+
/** Called only after native Agent creation has resolved, never from setup. */
|
|
39
|
+
start(): void;
|
|
40
|
+
interact(text: string, delivery?: "steer" | "followUp", files?: readonly string[], attachments?: readonly (ImageBlock | FileBlock)[], caller?: AbortSignal): Promise<{
|
|
41
|
+
status: "queued";
|
|
42
|
+
messageId: import("@deepseek-ai/dsh-llm").MessageId;
|
|
43
|
+
}>;
|
|
44
|
+
private event;
|
|
45
|
+
private humanPending;
|
|
46
|
+
private pump;
|
|
47
|
+
private deliver;
|
|
48
|
+
stop(): Promise<void>;
|
|
49
|
+
}
|