dsh-plugin-aghr 0.0.0-stage → 0.6.1

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/lib/client.js ADDED
@@ -0,0 +1,294 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-plugin-aghr",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ const React = require("react");
7
+ const h = React.createElement;
8
+
9
+ /** 本插件在客户端语言服务里的命名空间。 */
10
+ const NS = "dshPluginAghr";
11
+
12
+ //#region 样式
13
+ /**
14
+ * 本页样式。
15
+ *
16
+ * 手写注入而不是 CSS 模块:本包没有编译步骤,`lib/client.js` 就是最终产物。
17
+ * 只用 dsh 自己的设计令牌(`--dsw-alias-*` / `--dsw-radius-*`),跟随主题与明暗模式;
18
+ * `data-plugin-css` 上的标记与官方客户端插件同一写法,重复挂载只注入一次。
19
+ */
20
+ const CSS = [
21
+ ".dsh-aghr-section{display:flex;flex-direction:column;gap:16px}",
22
+ ".dsh-aghr-head{display:flex;flex-direction:column;gap:6px}",
23
+ ".dsh-aghr-title{margin:0;font-size:20px;font-weight:500;line-height:28px}",
24
+ ".dsh-aghr-lede{margin:0;font-size:14px;line-height:22px;color:var(--dsw-alias-label-secondary)}",
25
+ ".dsh-aghr-block{display:flex;flex-direction:column;gap:8px}",
26
+ ".dsh-aghr-block-title{margin:0;font-size:14px;font-weight:500;line-height:20px}",
27
+ ".dsh-aghr-muted{margin:0;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}",
28
+ ".dsh-aghr-list{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:6px}",
29
+ ".dsh-aghr-list li{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}",
30
+ ".dsh-aghr-facts{margin:0}",
31
+ ".dsh-aghr-facts div{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:40px;padding:6px 2px;border-bottom:.5px solid var(--dsw-alias-border-l2)}",
32
+ ".dsh-aghr-facts dt{flex:none;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}",
33
+ ".dsh-aghr-facts dd{margin:0;min-width:0;text-align:end;overflow-wrap:anywhere;font-size:13px;line-height:20px}",
34
+ ".dsh-aghr-code{font-family:var(--ds-font-family-code);font-size:12px;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-module-platform);box-decoration-break:clone}",
35
+ ".dsh-aghr-note{margin:0;display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border-radius:var(--dsw-radius-md);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}",
36
+ ".dsh-aghr-steps{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:10px;counter-reset:dsh-aghr-step}",
37
+ ".dsh-aghr-steps li{display:flex;gap:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}",
38
+ ".dsh-aghr-steps li:before{counter-increment:dsh-aghr-step;content:counter(dsh-aghr-step);flex:none;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-size:11px;font-weight:500}",
39
+ ].join("");
40
+
41
+ const CSS_TAG_ID = "dsh-plugin-aghr/aghrSection.css";
42
+ if (
43
+ typeof document !== "undefined" &&
44
+ document.querySelector("style[data-plugin-css=" + JSON.stringify(CSS_TAG_ID) + "]") === null
45
+ ) {
46
+ const tag = document.createElement("style");
47
+ tag.dataset.plugin = "dsh-plugin-aghr";
48
+ tag.dataset.pluginCss = CSS_TAG_ID;
49
+ tag.textContent = CSS;
50
+ document.head.appendChild(tag);
51
+ }
52
+ //#endregion
53
+
54
+ //#region 文案
55
+ /** 中文文案。 */
56
+ const zh = {
57
+ tabLabel: "AGHR",
58
+ title: "AGHR 团队(中文)",
59
+ lede: "让模型把复杂任务做完、做对:先探清题目,再决定拆不拆;拆开的每一步都能被检查,验不了的地方老实标成不确定。",
60
+ whatTitle: "它解决什么",
61
+ whatBody:
62
+ "一次性要模型给出完整答案,常见毛病是跳步、隐含假设不说、中间错了一路往后传,最后你还没法核验。这个包把「拆不拆、拆到哪、怎么验」变成一套能照着走的流程,并且让执行和审计由不同的角色来做——因为模型说「我算对了」本身不是证据。",
63
+ contentsTitle: "装了什么",
64
+ contentPresetTitle: "1. 一个会话预设",
65
+ contentPresetBody:
66
+ "新建会话时选上它,会话里就有四个职责位:Leader 对接你并对交付负责,Planner 拆任务定验收标准,Operator 干活,Validator 事后审计。",
67
+ contentSkillTitle: "2. 一份方法技能",
68
+ contentSkillBody:
69
+ "技能正文随包分发,插件启用时注册给会话。内容是照着做的流程:先探测、按认知难点拆、给每个节点定验收条件、验不过就分类回退、结论标验证状态。",
70
+ contentUiTitle: "3. 这一页",
71
+ contentUiBody: "Plugins 页里的说明页,讲清这个包是什么、怎么用、代价在哪。",
72
+ usageTitle: "怎么用",
73
+ usageStep1: "新建会话时在预设选择里挑「AGHR 团队(中文)」,会话就按四个职责位分工。",
74
+ usageStep2: "技能默认可用,模型自己会调;也可以直接说「用 aghr 拆这个问题」。",
75
+ usageStep3: "适合结果必须能验的任务:教材级题目、工程计算、多步代码改动、多来源核对。",
76
+ usageStep4: "在 Plugins 页停用本包就回到你原来的预设,它不改任何全局配置。",
77
+ factsTitle: "包信息",
78
+ factPackage: "包名",
79
+ factVersion: "版本",
80
+ factPreset: "预设",
81
+ factSkill: "技能",
82
+ factSlots: "客户端位置",
83
+ factSlotsValue: "Plugins 页:一个标签页,加详情页里的一段说明",
84
+ noteTitle: "注意",
85
+ noteDuplicate: "这个包比别的预设更费上下文和时间:它的取向是宁可多花也要把任务做完,不是省 token。",
86
+ noteShadow:
87
+ "技能同名冲突:工作区里已经有同名技能时以工作区那份为准——项目级目录的优先级高于包内注册,不是本包的缺陷。",
88
+ detailTitle: "这个包里的东西",
89
+ detailBody: "一个会话预设、一份方法技能,以及 Plugins 页里的这一页说明。",
90
+ };
91
+
92
+ /** 英文文案。 */
93
+ const en = {
94
+ tabLabel: "AGHR",
95
+ title: "AGHR team (Chinese)",
96
+ lede: "For finishing hard tasks right: probe the task first, decide whether and where to split, keep every step checkable, and mark what cannot be verified as uncertain.",
97
+ whatTitle: "What it solves",
98
+ whatBody:
99
+ "Asking a model for the full answer in one shot tends to skip steps, hide assumptions, carry an early error to the end, and leave you no way to check. This package turns “whether to split, where, and how to verify” into a procedure, and separates doing from auditing — a model saying “it is correct” is not evidence.",
100
+ contentsTitle: "What is inside",
101
+ contentPresetTitle: "1. A session preset",
102
+ contentPresetBody:
103
+ "Pick it when starting a session and you get four seats: Leader faces you and owns delivery, Planner splits the task and sets acceptance criteria, Operator does the work, Validator audits afterwards.",
104
+ contentSkillTitle: "2. A method skill",
105
+ contentSkillBody:
106
+ "The skill body ships with the package and is registered when the plugin loads: probe first, split by cognitive difficulty, give each node acceptance criteria, classify failures and roll back, label every conclusion with a verification status.",
107
+ contentUiTitle: "3. This page",
108
+ contentUiBody: "A page in Plugins explaining what the package is, how to use it, and what it costs.",
109
+ usageTitle: "How to use it",
110
+ usageStep1: "Start a session and choose “AGHR 团队(中文)” in the preset selector; the session then works in four seats.",
111
+ usageStep2: "The skill is available by default; you can also say “use aghr to split this”.",
112
+ usageStep3: "Best fit: tasks whose results must be checkable — textbook problems, engineering calculations, multi-step code changes, cross-source checks.",
113
+ usageStep4: "Disabling the bundle in Plugins returns you to your previous preset; no global configuration is changed.",
114
+ factsTitle: "Package",
115
+ factPackage: "Package",
116
+ factVersion: "Version",
117
+ factPreset: "Preset",
118
+ factSkill: "Skill",
119
+ factSlots: "Client surface",
120
+ factSlotsValue: "Plugins page: one tab, plus a section on the detail page",
121
+ noteTitle: "Caveats",
122
+ noteDuplicate: "This preset spends more context and time than others: it is built to finish the task, not to save tokens.",
123
+ noteShadow:
124
+ "Same-named skills: a skill in the workspace wins over the bundled one — project roots outrank runtime registration, and that is by design.",
125
+ detailTitle: "Inside this package",
126
+ detailBody: "A session preset, a method skill, and this page in Plugins.",
127
+ };
128
+ //#endregion
129
+
130
+ //#region 组件
131
+ /**
132
+ * 一行键值对。
133
+ *
134
+ * @param {object} props - `{ label, value }`。
135
+ * @returns {object} React 元素。
136
+ */
137
+ function Fact(props) {
138
+ return h("div", null, h("dt", null, props.label), h("dd", null, props.value));
139
+ }
140
+
141
+ /**
142
+ * 小标题 + 内容块。
143
+ *
144
+ * @param {object} props - `{ title, children }`。
145
+ * @returns {object} React 元素。
146
+ */
147
+ function Block(props) {
148
+ return h(
149
+ "section",
150
+ { className: "dsh-aghr-block" },
151
+ h("h3", { className: "dsh-aghr-block-title" }, props.title),
152
+ props.children,
153
+ );
154
+ }
155
+
156
+ /**
157
+ * 本包的说明正文。
158
+ *
159
+ * @param {object} props - 由 `inject()` 合入的 `{ t }`,以及 slot 提供的标准 props。
160
+ * @returns {object} React 元素。
161
+ */
162
+ function AghrSection(props) {
163
+ const t = props.t;
164
+ return h(
165
+ "div",
166
+ { className: "dsh-aghr-section" },
167
+ h(
168
+ "header",
169
+ { className: "dsh-aghr-head" },
170
+ h("h2", { className: "dsh-aghr-title" }, t("title")),
171
+ h("p", { className: "dsh-aghr-lede" }, t("lede")),
172
+ ),
173
+ h(Block, { title: t("whatTitle") }, h("p", { className: "dsh-aghr-muted" }, t("whatBody"))),
174
+ h(
175
+ Block,
176
+ { title: t("contentsTitle") },
177
+ h(
178
+ "ul",
179
+ { className: "dsh-aghr-list" },
180
+ h("li", null, h("strong", null, t("contentPresetTitle")), " — ", t("contentPresetBody")),
181
+ h("li", null, h("strong", null, t("contentSkillTitle")), " — ", t("contentSkillBody")),
182
+ h("li", null, h("strong", null, t("contentUiTitle")), " — ", t("contentUiBody")),
183
+ ),
184
+ ),
185
+ h(
186
+ Block,
187
+ { title: t("usageTitle") },
188
+ h(
189
+ "ol",
190
+ { className: "dsh-aghr-steps" },
191
+ h("li", null, t("usageStep1")),
192
+ h("li", null, t("usageStep2")),
193
+ h("li", null, t("usageStep3")),
194
+ h("li", null, t("usageStep4")),
195
+ ),
196
+ ),
197
+ h(
198
+ Block,
199
+ { title: t("factsTitle") },
200
+ h(
201
+ "dl",
202
+ { className: "dsh-aghr-facts" },
203
+ h(Fact, { label: t("factPackage"), value: h("code", { className: "dsh-aghr-code" }, "dsh-plugin-aghr") }),
204
+ // 版本号要与 package.json 一致(浏览器半读不到 manifest,只能写在这里)。
205
+ h(Fact, { label: t("factVersion"), value: "0.6.1" }),
206
+ h(Fact, { label: t("factPreset"), value: h("code", { className: "dsh-aghr-code" }, "aghr") }),
207
+ h(Fact, { label: t("factSkill"), value: h("code", { className: "dsh-aghr-code" }, "aghr") }),
208
+ h(
209
+ Fact,
210
+ {
211
+ label: t("factSlots"),
212
+ value: t("factSlotsValue"),
213
+ },
214
+ ),
215
+ ),
216
+ ),
217
+ h(
218
+ Block,
219
+ { title: t("noteTitle") },
220
+ h("p", { className: "dsh-aghr-note" }, t("noteDuplicate")),
221
+ h("p", { className: "dsh-aghr-note" }, t("noteShadow")),
222
+ ),
223
+ );
224
+ }
225
+
226
+ /**
227
+ * 本包详情页上的补充段。
228
+ *
229
+ * 只在打开本包的页面时渲染;其它 bundle / 行 / 官方插件一律返回 null。
230
+ *
231
+ * @param {object} props - `{ t, subject }`。
232
+ * @returns {object|null} React 元素。
233
+ */
234
+ function AghrDetailSection(props) {
235
+ const subject = props.subject;
236
+ const name = subject !== undefined && subject.kind === "bundle" ? subject.pkg.name : undefined;
237
+ if (name !== "dsh-plugin-aghr") return null;
238
+ const t = props.t;
239
+ return h(
240
+ "section",
241
+ { className: "dsh-aghr-block" },
242
+ h("h3", { className: "dsh-aghr-block-title" }, t("detailTitle")),
243
+ h("p", { className: "dsh-aghr-muted" }, t("detailBody")),
244
+ );
245
+ }
246
+ //#endregion
247
+
248
+ /** 依赖的客户端服务。 */
249
+ const inject = ["slots", "locale"];
250
+
251
+ /**
252
+ * 注册文案与两个槽位。
253
+ *
254
+ * @param {import("@deepseek-ai/cordis").Context} ctx - 浏览器插件上下文。
255
+ */
256
+ function apply(ctx) {
257
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-plugin-aghr: dictionaries");
258
+ const t = ctx.locale.bind(NS);
259
+
260
+ // Plugins 页顶部的「AGHR」标签页:说明这个包干什么、装了什么、怎么用。
261
+ // 参数形状照 @deepseek-ai/dsh-client-ui-plugin-market/lib/client.js 的「发现」标签页。
262
+ ctx.slots.inject("plugins.tab", () =>
263
+ ctx.slots.register(
264
+ {
265
+ name: "plugins.tab",
266
+ id: "aghr",
267
+ order: 60,
268
+ label: () => t("tabLabel"),
269
+ locale: NS,
270
+ inject: () => ({ t }),
271
+ },
272
+ AghrSection,
273
+ ),
274
+ );
275
+
276
+ // 本包自己的详情页:只对 subject.kind === "bundle" 且 pkg.name 为本包名的页面渲染。
277
+ ctx.slots.inject("plugins.detail.section", () =>
278
+ ctx.slots.register(
279
+ {
280
+ name: "plugins.detail.section",
281
+ id: "aghr-detail",
282
+ order: 60,
283
+ inject: () => ({ t }),
284
+ },
285
+ AghrDetailSection,
286
+ ),
287
+ );
288
+ }
289
+
290
+ exports.apply = apply;
291
+ exports.inject = inject;
292
+ return module.exports;
293
+ },
294
+ });
package/lib/index.js ADDED
@@ -0,0 +1,304 @@
1
+ /**
2
+ * dsh-plugin-aghr —— Host 半。
3
+ *
4
+ * 做两件事,都走运行时 API,不往 profile 里贴预设:
5
+ * 1) 把包内 `skill/SKILL.md` 注册成名为 `aghr` 的技能;
6
+ * 2) 用 `ctx.agentPresets.register()` 注册预设 `aghr`(四职责位团队版)。
7
+ * 另外它让本包出现在 host Loader 的行表里:`@deepseek-ai/dsh-client-modules`
8
+ * 按行名解析到本包的 package.json、读 `dsh.client`,才会加载浏览器半 `./client`。
9
+ *
10
+ * 为什么不把预设写进 cordis.patch.yml:那样写出来的是一份**静态复制的插件表**,
11
+ * 换一台机器、换一个版本就得整份重抄,而且会把别人 profile 里的本机配置(绝对路径、
12
+ * 私有 MCP)一起带出去。`ctx.agentPresets.register()` 接受 `{ id, name, description,
13
+ * order, plugins }`,`plugins` 是普通的 Cordis 行表,所以在代码里按需挑行、按需给
14
+ * 配置即可:这份包只声明自己真正需要的行。
15
+ *
16
+ * 为什么不给 `@deepseek-ai/dsh-skill-filesystem` 配 `customSkillDirs` / `bundledSkillDir`:
17
+ * 这两个配置项只有"目录路径"一种表达方式,前者按 dsh 进程 cwd 解析,后者是单值且默认
18
+ * 取环境变量 `DSH_BUNDLED_SKILL_DIR`,第三方包没法指向自己包内目录,只能写死绝对路径。
19
+ * `ctx.skills.register()` 不需要任何路径配置。
20
+ *
21
+ * @module dsh-plugin-aghr
22
+ */
23
+
24
+ import { readFileSync } from "node:fs";
25
+ import { fileURLToPath } from "node:url";
26
+
27
+ /** 包内技能正文的位置;用 import.meta.url 定位,不写死绝对路径。 */
28
+ const SKILL_URL = new URL("../skill/SKILL.md", import.meta.url);
29
+
30
+ /** 注册到 `ctx.skills` 的技能名,与 SKILL.md frontmatter 的 name 一致。 */
31
+ const SKILL_NAME = "aghr";
32
+
33
+ /** 预设的治理正文,随包分发,改文本不用改代码。 */
34
+ const PERSONA_URL = new URL("../assets/persona-aghr.md", import.meta.url);
35
+
36
+ /** 技能的资源根:模型提到 skill 里的相对路径时按这里解析。 */
37
+ const RESOURCE_BASE = {
38
+ kind: "directory",
39
+ path: fileURLToPath(new URL("../assets/", import.meta.url))
40
+ };
41
+
42
+ /** 随包技能在目录里的排序权重,与 skill-filesystem 的随包根同级。 */
43
+ const BUNDLED_SKILL_RANK = 600;
44
+
45
+ /**
46
+ * frontmatter 缺失或解析失败时的兜底描述。
47
+ *
48
+ * `ctx.skills.register()` 要求 description 是非空字符串(dsh-skill 的
49
+ * validateRuntimeSkill 会直接读 `skill.description.length`),所以这条兜底不能省。
50
+ */
51
+ const FALLBACK_DESCRIPTION =
52
+ "自适应粒度人机协同求解(AGHR):先探测再决定要不要拆、怎么拆,用可验证性分类与验证状态控制错误传播。";
53
+
54
+ /**
55
+ * 拆出 YAML frontmatter 与正文。
56
+ *
57
+ * 只实现本技能需要的极小子集(`---` 包裹、单行 `key: value` 标量),不引入 YAML 依赖:
58
+ * 本包要能被任意 profile 直接 link,多一个依赖就多一处版本冲突。
59
+ *
60
+ * 解析不出 description 时保留兜底描述、仍把正文交给技能服务——宁可退化,不要让技能消失。
61
+ *
62
+ * @param {string} raw - SKILL.md 的完整内容。
63
+ * @returns {{ description: string, content: string }} 技能定义所需的描述与正文。
64
+ */
65
+ function parseSkill(raw) {
66
+ const text = raw.replace(/^\uFEFF/, "");
67
+ const opening = /^---[ \t]*\r?\n/.exec(text);
68
+ if (opening === null) return { description: FALLBACK_DESCRIPTION, content: text };
69
+
70
+ const rest = text.slice(opening[0].length);
71
+ const closing = /^(?:---|\.\.\.)[ \t]*\r?\n/m.exec(rest);
72
+ if (closing === null) return { description: FALLBACK_DESCRIPTION, content: text };
73
+
74
+ const frontmatter = rest.slice(0, closing.index);
75
+ const content = rest.slice(closing.index + closing[0].length);
76
+ if (content.trim() === "") return { description: FALLBACK_DESCRIPTION, content: text };
77
+
78
+ let description = "";
79
+ for (const line of frontmatter.split(/\r?\n/)) {
80
+ const match = /^description[ \t]*:[ \t]*(.*)$/.exec(line);
81
+ if (match === null) continue;
82
+ let value = match[1].trim();
83
+ // 去掉可能存在的成对引号;本技能的 description 是裸标量,这里只是容错。
84
+ if (value.length >= 2 && (value[0] === '"' || value[0] === "'") && value.at(-1) === value[0]) {
85
+ value = value.slice(1, -1);
86
+ }
87
+ description = value;
88
+ break;
89
+ }
90
+
91
+ return { description: description === "" ? FALLBACK_DESCRIPTION : description, content };
92
+ }
93
+
94
+ // ── 预设行表 ────────────────────────────────────────────────────────────────
95
+ // 每一行都是本包自己挑的,不照抄任何一台机器的 profile。
96
+ // 需要什么能力就放什么行;放不进去的能力(比如本机私有 MCP)交给使用者自己加。
97
+
98
+ const PREFIX = "“如果你忘记了自己是谁,回看工作区 AGENTS.md——那里有我给你的信件”。--tigrisheep";
99
+
100
+ const personaRow = (suffix) => ({
101
+ id: "persona",
102
+ name: "@deepseek-ai/dsh-persona",
103
+ config: { prefix: PREFIX, suffix }
104
+ });
105
+
106
+ const ROW = {
107
+ instructions: { id: "agent-instructions", name: "@deepseek-ai/dsh-agent-instructions", config: { maxBytes: 65536 } },
108
+ time: { id: "time-context", name: "@deepseek-ai/dsh-time-context" },
109
+ fs: { id: "tool-fs", name: "@deepseek-ai/dsh-tool-fs" },
110
+ fsSearch: {
111
+ id: "tool-fs-search",
112
+ name: "@deepseek-ai/dsh-tool-fs-search",
113
+ config: { sampleOverCapGlobResults: false }
114
+ },
115
+ bash: { id: "tool-bash", name: "@deepseek-ai/dsh-tool-bash", disabled: process.platform === "win32" },
116
+ pwsh: { id: "tool-pwsh", name: "@deepseek-ai/dsh-tool-pwsh", disabled: process.platform !== "win32" },
117
+ jobs: { id: "tool-jobs", name: "@deepseek-ai/dsh-tool-jobs" },
118
+ web: { id: "tool-web", name: "@deepseek-ai/dsh-tool-web", config: { fetch: true, searchTimeoutMs: 60000 } },
119
+ skillFiles: { id: "skill-filesystem", name: "@deepseek-ai/dsh-skill-filesystem" },
120
+ skillTool: { id: "tool-skill", name: "@deepseek-ai/dsh-tool-skill" },
121
+ todo: { id: "tool-todo", name: "@deepseek-ai/dsh-tool-todo", config: { allowParallelInProgress: true } },
122
+ ask: { id: "tool-ask-user", name: "@deepseek-ai/dsh-tool-ask-user" },
123
+ present: { id: "present", name: "@deepseek-ai/dsh-tool-present" },
124
+ subagent: {
125
+ id: "tool-subagent",
126
+ name: "@deepseek-ai/dsh-tool-subagent",
127
+ config: { provider: "spawn", toolName: "subagent", modelSelectionSettings: true }
128
+ },
129
+ subagentControl: { id: "tool-subagent-control", name: "@deepseek-ai/dsh-tool-subagent-control" },
130
+ subagentList: { id: "tool-subagent-list-agents", name: "@deepseek-ai/dsh-tool-subagent-control/list-agents" }
131
+ };
132
+
133
+ /**
134
+ * 压缩:必须整组装进同一个 isolate 隔离域。
135
+ *
136
+ * `command-compact` 注入 `compaction` 服务;如果提供者 `compaction-basic` 单独加
137
+ * `isolate: { compaction: true }`,它就在自己的隔离域里,域外的 `command-compact`
138
+ * 永远等不到这个服务,装配会以 `waiting for compaction` 报错。所以这里用 group 行
139
+ * 承载 isolate,让提供者与消费者落在同一域内。
140
+ *
141
+ * `tool-result-pruner` 同样提供域内服务 `toolResultPruner`(`compaction-basic` 用
142
+ * `ctx.get("toolResultPruner")` 按需取用,取不到就退回不裁剪),所以它的服务名也必须
143
+ * 写进 isolate 映射——漏写会让该实现登记到 root realm,预设挂载直接失败:
144
+ * `Preset services require isolate realms: toolResultPruner`。
145
+ *
146
+ * **相对 0.4.1,这一行是新增的,属行为变化而不是纯修复**:装了本预设的会话里,`bash` / `read`
147
+ * 超过阈值的长输出会被裁掉中间一段(头尾保留),上下文增长更慢、更晚触发压缩,模型看到的工具
148
+ * 结果也随之变短。这条不写 config,用插件自身默认 8192/4096/1024。
149
+ *
150
+ * 不设 `compaction-basic.config.modelPolicies`:那是「本机 provider/model 组合 + 每档
151
+ * 预算」的机器相关配置,写进包里等于把别人的模型清单当默认值;`thresholdRatio` 等
152
+ * 通用预算也交给插件自身默认值,避免和上游默认值漂移。`tool-result-pruner` 同理不传
153
+ * config,用包内默认 8192/4096/1024。
154
+ *
155
+ * 这份常量对外导出,仅作可复核的测试缝:验证脚本可以拿它单独装配一次压缩域。
156
+ */
157
+ const COMPACTION_ROWS = [
158
+ {
159
+ id: "compaction",
160
+ name: "cordis:group",
161
+ group: true,
162
+ isolate: { compaction: true, toolResultPruner: true },
163
+ config: [
164
+ { id: "compaction-basic", name: "@deepseek-ai/dsh-compaction-basic" },
165
+ { id: "command-compact", name: "@deepseek-ai/dsh-command-compact" },
166
+ { id: "tool-result-pruner", name: "@deepseek-ai/dsh-compaction-tool-result-pruner" }
167
+ ]
168
+ }
169
+ ];
170
+
171
+ /**
172
+ * 团队版行表:四职责位要能读、能算、能检索、能派活、能计划。
173
+ *
174
+ * 刻意不含的东西:本机私有 MCP、run_code 之外的自定义工具、任何绝对路径。
175
+ */
176
+ function teamRows(suffix) {
177
+ return [
178
+ personaRow(suffix),
179
+ ROW.instructions,
180
+ ROW.time,
181
+ ROW.bash,
182
+ ROW.pwsh,
183
+ ROW.fs,
184
+ ROW.fsSearch,
185
+ ROW.jobs,
186
+ ROW.skillFiles,
187
+ ROW.skillTool,
188
+ ROW.web,
189
+ ROW.subagent,
190
+ ROW.subagentControl,
191
+ ROW.subagentList,
192
+ ROW.ask,
193
+ ROW.todo,
194
+ ROW.present,
195
+ ...COMPACTION_ROWS
196
+ ];
197
+ }
198
+
199
+ const PRESET = {
200
+ id: "aghr",
201
+ name: "AGHR 团队(中文)",
202
+ description: "四职责位:Leader 管用户与交付,Planner 管规划与事后决策,Operator 管执行,Validator 管事后审计。",
203
+ order: 6,
204
+ rows: teamRows
205
+ };
206
+
207
+ function readText(url) {
208
+ return readFileSync(url, "utf8");
209
+ }
210
+
211
+ /** Cordis 插件名。 */
212
+ const name = "dsh-plugin-aghr";
213
+
214
+ /**
215
+ * 依赖的服务:`skills` 负责技能注册,`agentPresets` 负责预设注册。
216
+ *
217
+ * 缺任一服务的 profile 里本行不会启用(技能与预设自然不可用),但浏览器半照样加载:
218
+ * 客户端发现看的是 Loader 行表,不看这一行是否 apply 成功。
219
+ */
220
+ const inject = ["skills", "agentPresets"];
221
+
222
+ /**
223
+ * 注册预设,并把它自己的注销函数交给本插件的 fiber。
224
+ *
225
+ * `ctx.agentPresets.register()` 是 **async**,成功时返回这份定义自己的注销函数。
226
+ * 那个返回值必须挂到 `ctx.effect` 上:热重载时 Cordis 先 dispose 旧 fiber、再重新执行
227
+ * 本插件的 `apply`,没有注销动作,旧定义就留在 `registry.definitions` 里,第二次
228
+ * `register()` 直接抛 `Duplicate agent preset: aghr`(web.log 里表现为
229
+ * `dsh: fatal load failure`)。
230
+ *
231
+ * 注意两件容易写错的事:
232
+ *
233
+ * 1. **必须 await**。`register()` 拒绝时(id 重复、id 为空字符串)异常是走 promise
234
+ * 拒绝的,不 await 就变成未捕获拒绝,绕开下面这个 try/catch,还会打断整条 Loader 行。
235
+ * 2. **挂 disposer 只挂一份**。effect 的回调立即执行、返回的就是注销函数,不要在回调里
236
+ * 再调一次 `register()`:那会在每次重载时多注册一份,反过来杀掉新实例。
237
+ *
238
+ * 装配期(mountPreset)的失败不会让 promise 拒绝——registry 自己把诊断记成 `broken`
239
+ * 并写一条 warn,所以这里 catch 到的都是「定义没能登记」这一层的问题。
240
+ *
241
+ * @param {import("@deepseek-ai/cordis").Context} ctx - Host 插件上下文。
242
+ */
243
+ async function registerPreset(ctx) {
244
+ let unregister;
245
+ try {
246
+ const suffix = readText(PERSONA_URL);
247
+ unregister = await ctx.agentPresets.register({
248
+ id: PRESET.id,
249
+ name: PRESET.name,
250
+ description: PRESET.description,
251
+ order: PRESET.order,
252
+ plugins: PRESET.rows(suffix)
253
+ });
254
+ } catch (error) {
255
+ ctx.logger.error(`dsh-plugin-aghr: 注册预设 ${PRESET.id} 失败:${String(error)}`);
256
+ return;
257
+ }
258
+
259
+ try {
260
+ ctx.effect(() => () => unregister(), "dsh-plugin-aghr: agent preset");
261
+ } catch (error) {
262
+ // 注册期间本插件的 fiber 已被卸载(热重载的竞态窗口):effect 挂不上去,
263
+ // 就得当场自己注销,否则这份定义会永久留在 registry 里。
264
+ //
265
+ // 注销动作自身也要包住:它若再抛,异常会从 `apply` 冒出去,把整条 Loader 行打成
266
+ // FAILED —— 正是本次修复要消灭的形态。故失败只记日志、照常返回;代价是那份定义
267
+ // 残留在 registry 里,随进程重启消失。成功路径的顺序未变(先 await,后 warn)。
268
+ try {
269
+ await unregister();
270
+ ctx.logger.warn(`dsh-plugin-aghr: 预设 ${PRESET.id} 登记期间插件已卸载,已当场注销:${String(error)}`);
271
+ } catch (failure) {
272
+ ctx.logger.error(`dsh-plugin-aghr: 预设 ${PRESET.id} 登记期间插件已卸载,且当场注销失败(定义残留至重启):${String(failure)};原始错误:${String(error)}`);
273
+ }
274
+ }
275
+ }
276
+
277
+ /**
278
+ * 注册技能与预设。
279
+ *
280
+ * 两件事各自 try/catch:技能失败不该连带预设消失,预设失败也不该让技能掉线。
281
+ * `ctx.skills.register()` 自己把清理动作挂到调用方 fiber 上(`skills.register()`
282
+ * effect),本函数不需要额外处理;`ctx.agentPresets.register()` 则相反,清理函数由
283
+ * 调用方负责,见 {@link registerPreset}。
284
+ *
285
+ * 返回的 promise 不会拒绝:本函数内部已吞掉全部失败,只写日志。这样包内资产缺失或
286
+ * 注册失败都不会把整条 Loader 行打成 FAILED,浏览器半照常加载。
287
+ *
288
+ * @param {import("@deepseek-ai/cordis").Context} ctx - Host 插件上下文。
289
+ */
290
+ async function apply(ctx) {
291
+ let content = "";
292
+ let description = FALLBACK_DESCRIPTION;
293
+ try {
294
+ ({ content, description } = parseSkill(readText(SKILL_URL)));
295
+ ctx.skills.register({ name: SKILL_NAME, description, content, resourceBase: RESOURCE_BASE, rank: BUNDLED_SKILL_RANK });
296
+ } catch (error) {
297
+ // 包内资产缺失是打包错误。记一条错误就退出,不要抛出把整条 Loader 行打成 FAILED。
298
+ ctx.logger.error(`dsh-plugin-aghr: 注册技能 aghr 失败:${String(error)}`);
299
+ }
300
+
301
+ await registerPreset(ctx);
302
+ }
303
+
304
+ export { COMPACTION_ROWS, apply, inject, name, parseSkill };
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "AGHR team (Chinese)",
4
+ "description": "For anyone who needs a model to get hard tasks right: a Chinese preset plus a method skill that probes before splitting and says what can be verified."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "AGHR 团队(中文)",
4
+ "description": "给需要模型把复杂任务做对的人:一个中文预设配一份方法技能,先探清再决定拆不拆,每个结论都标出能不能验。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "dsh-plugin-aghr",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.6.1",
4
+ "private": false,
5
+ "description": "AGHR(自适应粒度人机协同求解):一个四职责位中文 agent preset、一份 AGHR skill,以及 Plugins 页上的说明页。",
6
+ "author": "tigrisheep",
7
+ "icon": "./icon.svg",
8
+ "type": "module",
9
+ "main": "lib/index.js",
10
+ "exports": {
11
+ ".": "./lib/index.js",
12
+ "./client": "./lib/client.js",
13
+ "./assets/persona-aghr.md": "./assets/persona-aghr.md",
14
+ "./CHANGELOG.md": "./CHANGELOG.md",
15
+ "./cordis.patch.yml": "./cordis.patch.yml",
16
+ "./locale/*.json": "./locale/*.json",
17
+ "./skill/SKILL.md": "./skill/SKILL.md",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "LICENSE",
22
+ "README.md",
23
+ "CHANGELOG.md",
24
+ "assets/*.md",
25
+ "cordis.patch.yml",
26
+ "icon.svg",
27
+ "lib/client.js",
28
+ "lib/index.js",
29
+ "locale/*.json",
30
+ "skill/SKILL.md"
31
+ ],
32
+ "keywords": [
33
+ "dsh",
34
+ "dsh-plugin",
35
+ "aghr",
36
+ "agent-preset",
37
+ "skill"
38
+ ],
39
+ "engines": {
40
+ "node": ">=20"
41
+ },
42
+ "dsh": {
43
+ "bundle": {
44
+ "patch": "./cordis.patch.yml"
45
+ },
46
+ "client": {
47
+ "platform": "web",
48
+ "inject": [
49
+ "@deepseek-ai/dsh-client-locale",
50
+ "@deepseek-ai/dsh-client-ui-plugin-manager"
51
+ ]
52
+ }
53
+ },
54
+ "license": "MIT",
55
+ "homepage": "https://github.com/tigrisheep/dsh-plugin-aghr#readme",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/tigrisheep/dsh-plugin-aghr.git"
59
+ },
60
+ "bugs": {
61
+ "url": "https://github.com/tigrisheep/dsh-plugin-aghr/issues"
62
+ }
63
+ }