@joekytc/dsh-swarm 0.3.1 → 0.3.2
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/domain/ocr-review.d.ts +6 -2
- package/lib/domain/ocr-review.js +20 -11
- package/lib/routes/kanban-http.js +14 -4
- package/lib/tools/ocr-review-tools.js +13 -6
- package/package.json +6 -1
- package/personas/kanban-d/agent.cordis.yml +0 -1
- package/personas/kanban-dt/agent.cordis.yml +4 -3
- package/personas/persona-d.md +0 -1
- package/personas/persona-dt.md +2 -2
|
@@ -7,8 +7,12 @@ export interface OcrArgsInput {
|
|
|
7
7
|
to?: string;
|
|
8
8
|
commit?: string;
|
|
9
9
|
paths?: string[];
|
|
10
|
+
/** 业务上下文,托管评审时提升评审质量(仅 managed 分支透传)。 */
|
|
11
|
+
background?: string;
|
|
10
12
|
}
|
|
11
|
-
/** 构造 OCR CLI 参数:preview/rule 走 delegate,managed 走 review
|
|
13
|
+
/** 构造 OCR CLI 参数:preview/rule 走 delegate,managed 走 review。
|
|
14
|
+
* preview 默认输出 text,必须显式 --format json 才能拿到可解析输出;
|
|
15
|
+
* managed 恒带 --audience agent(agent 场景标准参数,抑制 progress 输出)。 */
|
|
12
16
|
export declare function buildOcrArgs(sub: OcrSub, a: OcrArgsInput): string[];
|
|
13
17
|
/** 预览结果归一化结构。 */
|
|
14
18
|
export interface PreviewResult {
|
|
@@ -23,7 +27,7 @@ export interface PreviewResult {
|
|
|
23
27
|
}[];
|
|
24
28
|
mergeBase: string | null;
|
|
25
29
|
}
|
|
26
|
-
/** 解析 delegate preview 的 JSON
|
|
30
|
+
/** 解析 delegate preview 的 JSON 输出(官方字段 reviewable_files/excluded_files/exclude_reason),失败或结构异常时回退 unknown。 */
|
|
27
31
|
export declare function parsePreviewJson(stdout: string): PreviewResult;
|
|
28
32
|
/** 托管评审结果归一化结构。 */
|
|
29
33
|
export interface ManagedResult {
|
package/lib/domain/ocr-review.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
/** 构造 OCR CLI 参数:preview/rule 走 delegate,managed 走 review
|
|
1
|
+
/** 构造 OCR CLI 参数:preview/rule 走 delegate,managed 走 review。
|
|
2
|
+
* preview 默认输出 text,必须显式 --format json 才能拿到可解析输出;
|
|
3
|
+
* managed 恒带 --audience agent(agent 场景标准参数,抑制 progress 输出)。 */
|
|
2
4
|
export function buildOcrArgs(sub, a) {
|
|
3
5
|
if (sub === 'preview') {
|
|
4
|
-
const args = ['delegate', 'preview'];
|
|
5
|
-
if (a.
|
|
6
|
-
args.push('--
|
|
7
|
-
if (a.to)
|
|
8
|
-
|
|
6
|
+
const args = ['delegate', 'preview', '--format', 'json'];
|
|
7
|
+
if (a.commit)
|
|
8
|
+
args.push('--commit', a.commit);
|
|
9
|
+
else if (a.from || a.to) {
|
|
10
|
+
if (a.from)
|
|
11
|
+
args.push('--from', a.from);
|
|
12
|
+
if (a.to)
|
|
13
|
+
args.push('--to', a.to);
|
|
14
|
+
}
|
|
9
15
|
if (a.repo)
|
|
10
16
|
args.push('--repo', a.repo);
|
|
11
17
|
return args;
|
|
@@ -22,7 +28,9 @@ export function buildOcrArgs(sub, a) {
|
|
|
22
28
|
if (a.to)
|
|
23
29
|
args.push('--to', a.to);
|
|
24
30
|
}
|
|
25
|
-
args.push('--format', 'json');
|
|
31
|
+
args.push('--format', 'json', '--audience', 'agent');
|
|
32
|
+
if (a.background)
|
|
33
|
+
args.push('--background', a.background);
|
|
26
34
|
return args;
|
|
27
35
|
}
|
|
28
36
|
/** 解析失败的兜底结果。 */
|
|
@@ -37,7 +45,7 @@ function pathOf(el) {
|
|
|
37
45
|
function asArray(v) {
|
|
38
46
|
return Array.isArray(v) ? v.filter(isRecord) : [];
|
|
39
47
|
}
|
|
40
|
-
/** 解析 delegate preview 的 JSON
|
|
48
|
+
/** 解析 delegate preview 的 JSON 输出(官方字段 reviewable_files/excluded_files/exclude_reason),失败或结构异常时回退 unknown。 */
|
|
41
49
|
export function parsePreviewJson(stdout) {
|
|
42
50
|
let obj;
|
|
43
51
|
try {
|
|
@@ -51,8 +59,8 @@ export function parsePreviewJson(stdout) {
|
|
|
51
59
|
const mergeBase = obj.merge_base ?? obj.mergeBase;
|
|
52
60
|
return {
|
|
53
61
|
mode: String(obj.mode ?? 'unknown'),
|
|
54
|
-
files: asArray(obj.
|
|
55
|
-
excluded: asArray(obj.
|
|
62
|
+
files: asArray(obj.reviewable_files).map((el) => ({ path: pathOf(el), status: String(el.status ?? '') })),
|
|
63
|
+
excluded: asArray(obj.excluded_files).map((el) => ({ path: pathOf(el), reason: String(el.exclude_reason ?? '') })),
|
|
56
64
|
mergeBase: mergeBase == null ? null : String(mergeBase),
|
|
57
65
|
};
|
|
58
66
|
}
|
|
@@ -68,7 +76,8 @@ export function parseManagedJson(stdout) {
|
|
|
68
76
|
if (!isRecord(obj))
|
|
69
77
|
return { status: 'unknown', comments: [] };
|
|
70
78
|
const comments = asArray(obj.comments).map((el) => {
|
|
71
|
-
|
|
79
|
+
// 官方 schema 行号字段为 snake_case start_line,line/startLine 仅作容错别名
|
|
80
|
+
const rawLine = el.start_line ?? el.line ?? el.startLine;
|
|
72
81
|
const line = Number(rawLine);
|
|
73
82
|
const message = String(el.content ?? el.message ?? el.body ?? '');
|
|
74
83
|
return {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
1
2
|
import { execFile } from 'node:child_process';
|
|
2
3
|
import { writeFileSync } from 'node:fs';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -72,13 +73,17 @@ const OCR_WIRE_DEGRADED = '未能从 dsh 解析该提供方的接入信息(bas
|
|
|
72
73
|
* 从宿主设置解析所选提供方的接入信息(baseUrl/协议/apiKey)。
|
|
73
74
|
* 事实核查结论(实现期探查):llm 服务的公开 API 不暴露连接事实,但同进程可经
|
|
74
75
|
* settings 服务的 describe() 读到模型适配器的 provider profile——形如
|
|
75
|
-
* { providers: { <id>: { baseURL, api, apiKey | apiKeyEnv } } }(apiKeyEnv
|
|
76
|
+
* { providers: { <id>: { baseURL, api, apiKey | apiKeyEnv } } }(apiKeyEnv 指向凭据引用名)。
|
|
77
|
+
* 踩坑:apiKeyEnv 名下的 key 不一定在进程 env——Models 页写入的凭据存在
|
|
78
|
+
* credentials 服务的 managed store(ctx.credentials)里;解析顺序须先进程 env、
|
|
79
|
+
* 再 ctx.credentials.resolve(env → managed store → $DSH_HOME/.env,per-call 不缓存)。
|
|
76
80
|
* 命中 profile 但字段不全时跳过该 descriptor 继续尝试下一个(多 descriptor 场景勿误降级),
|
|
77
81
|
* 绝不回传半套配置;全部不合才返回 null 走降级。apiKey 只透传给 ocr config,不落日志。
|
|
78
82
|
*/
|
|
79
|
-
function resolveDshProviderAccess(ctx, providerId) {
|
|
83
|
+
async function resolveDshProviderAccess(ctx, providerId) {
|
|
80
84
|
try {
|
|
81
85
|
const settings = ctx.get('settings');
|
|
86
|
+
const creds = ctx.get('credentials');
|
|
82
87
|
const descriptors = settings?.describe?.() ?? [];
|
|
83
88
|
for (const d of descriptors) {
|
|
84
89
|
const providers = d.value?.providers;
|
|
@@ -89,8 +94,13 @@ function resolveDshProviderAccess(ctx, providerId) {
|
|
|
89
94
|
const api = typeof profile.api === 'string' ? profile.api : '';
|
|
90
95
|
const protocol = api.startsWith('openai') ? 'openai' : api.includes('anthropic') ? 'anthropic' : '';
|
|
91
96
|
let apiKey = typeof profile.apiKey === 'string' ? profile.apiKey : '';
|
|
92
|
-
if (!apiKey && typeof profile.apiKeyEnv === 'string')
|
|
97
|
+
if (!apiKey && typeof profile.apiKeyEnv === 'string') {
|
|
93
98
|
apiKey = process.env[profile.apiKeyEnv] ?? '';
|
|
99
|
+
if (!apiKey && creds?.resolve) {
|
|
100
|
+
const r = await creds.resolve(credentialRef(profile.apiKeyEnv));
|
|
101
|
+
apiKey = r?.value ?? '';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
94
104
|
if (baseUrl && protocol && apiKey)
|
|
95
105
|
return { baseUrl, protocol, apiKey };
|
|
96
106
|
continue; // 字段不全:换下一个 descriptor,勿在此误降级
|
|
@@ -330,7 +340,7 @@ export function registerKanbanHttp(ctx, provider, configProvider, llm, config, o
|
|
|
330
340
|
json(res, 200, { ok: false, log: INSTALL_GUIDANCE });
|
|
331
341
|
return;
|
|
332
342
|
}
|
|
333
|
-
const access = resolveDshProviderAccess(ctx, providerId);
|
|
343
|
+
const access = await resolveDshProviderAccess(ctx, providerId);
|
|
334
344
|
if (!access) {
|
|
335
345
|
json(res, 200, { ok: false, log: OCR_WIRE_DEGRADED });
|
|
336
346
|
return;
|
|
@@ -28,6 +28,7 @@ export function buildOcrReviewTool(deps) {
|
|
|
28
28
|
to: { type: 'string', description: '目标分支/引用(range 模式,默认 HEAD)' },
|
|
29
29
|
commit: { type: 'string', description: '单次提交审查' },
|
|
30
30
|
paths: { type: 'array', items: { type: 'string' }, description: 'rule 子命令的文件路径列表' },
|
|
31
|
+
background: { type: 'string', description: '业务上下文,托管评审时提升评审质量' },
|
|
31
32
|
},
|
|
32
33
|
output: { schema: { type: 'json' }, render: (_a, v) => [{ type: 'text', text: String(v) }] },
|
|
33
34
|
async execute(args) {
|
|
@@ -42,18 +43,24 @@ export function buildOcrReviewTool(deps) {
|
|
|
42
43
|
throw new Error("ocr_review: sub='rule' requires non-empty paths (file path list)");
|
|
43
44
|
if (args.sub === 'managed' && !args.commit && !args.from)
|
|
44
45
|
throw new Error("ocr_review: sub='managed' requires commit or from (base ref)");
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// 托管评审走 ocr 自带 LLM(官方预算 15min×2 rounds),超时须远大于 preview/rule 的本地 git 操作
|
|
47
|
+
const timeoutMs = args.sub === 'managed' ? 2_400_000 : 600_000;
|
|
48
|
+
const res = await runOcrFn(buildOcrArgs(args.sub, { repo: args.repo, from: args.from, to: args.to, commit: args.commit, paths: args.paths, background: args.background }), { cwd: deps.cwd?.() ?? process.cwd(), timeoutMs });
|
|
49
|
+
// 失败不抛错:结果附 error 摘要(error + stderr 前 500 字符),模型拿得到部分结果与原因后自行降级/重试
|
|
50
|
+
const errSummary = res.error ? `${res.error} ${res.stderr.slice(0, 500)}`.trim() : '';
|
|
48
51
|
if (args.sub === 'preview') {
|
|
49
52
|
const preview = parsePreviewJson(res.stdout);
|
|
53
|
+
const errorPart = errSummary ? { error: errSummary } : {};
|
|
50
54
|
if (shouldSuggestManaged(preview.files.length)) {
|
|
51
|
-
return JSON.stringify({ ...preview, suggestion: `文件较多(N>${SUGGEST_MANAGED_FILES}),可在配置面板切换托管模式` });
|
|
55
|
+
return JSON.stringify({ ...preview, ...errorPart, suggestion: `文件较多(N>${SUGGEST_MANAGED_FILES}),可在配置面板切换托管模式` });
|
|
52
56
|
}
|
|
53
|
-
return JSON.stringify(preview);
|
|
57
|
+
return JSON.stringify({ ...preview, ...errorPart });
|
|
54
58
|
}
|
|
55
|
-
if (args.sub === 'rule')
|
|
59
|
+
if (args.sub === 'rule') {
|
|
60
|
+
if (errSummary)
|
|
61
|
+
return JSON.stringify({ error: errSummary });
|
|
56
62
|
return res.stdout.slice(0, 8000);
|
|
63
|
+
}
|
|
57
64
|
const report = parseManagedJson(res.stdout);
|
|
58
65
|
if (report.status !== 'completed' && res.error)
|
|
59
66
|
return JSON.stringify({ ...report, message: res.error });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joekytc/dsh-swarm",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "A governed swarm of six specialist DSH agents (orchestrator, planner, knowledge-base bridge, developer and two reviewers) that turns a requirement into a strict phase pipeline with machine-verified delivery evidence, review-gated merges, a full audit-log event stream and a live kanban tab; design inspired by the Hermes Agent kanban",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "joekytc",
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
},
|
|
49
49
|
"./package.json": "./package.json"
|
|
50
50
|
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"registry": "https://registry.npmjs.org/"
|
|
54
|
+
},
|
|
51
55
|
"scripts": {
|
|
52
56
|
"build": "tsc -p tsconfig.build.json && npm run build:client",
|
|
53
57
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
@@ -59,6 +63,7 @@
|
|
|
59
63
|
"@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
|
|
60
64
|
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-rc.1",
|
|
61
65
|
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-credentials": "^0.1.2-rc.1",
|
|
62
67
|
"@deepseek-ai/dsh-persona": "^0.1.2-rc.1",
|
|
63
68
|
"@deepseek-ai/dsh-session": "^0.1.2-rc.1",
|
|
64
69
|
"@deepseek-ai/dsh-tool-bash": "^0.1.2-rc.1",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
creating tasks, or wiki_write. Do NOT run sub-workflows or ralph loops inside a card.
|
|
57
57
|
c. Verify before claiming done (verification-before-completion): run `npx vitest run` + build +
|
|
58
58
|
typecheck, and confirm your diff, before complete. Use using-git-worktrees for isolation.
|
|
59
|
-
d. Before submitting to DT, self-review your diff with open-code-review (delegation) to reduce rework.
|
|
60
59
|
7. Commit convention: `<type>: [AI-GEN] <one-line concise description>` (type in
|
|
61
60
|
feat/fix/chore/docs/refactor/test/perf/ci...). Workflow: worktree isolated branch →
|
|
62
61
|
implement + verify → [AI-GEN] commit → (optionally push the feature branch). Do NOT merge
|
|
@@ -33,9 +33,10 @@
|
|
|
33
33
|
yourself → classify findings by severity (Critical/High must be reported, Medium with
|
|
34
34
|
context, Low dropped by default). Managed = call ocr_review{sub:'managed',
|
|
35
35
|
from:<TARGET_BRANCH>, to:<branch>} for normalized findings in one shot (<branch> = D's
|
|
36
|
-
feature branch from the parent handoff metadata.branch
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
feature branch from the parent handoff metadata.branch; you may pass background with a
|
|
37
|
+
one-line business context distilled from the spec card / task description); silently
|
|
38
|
+
fall back to the delegate flow when status is not completed or the tool returns
|
|
39
|
+
managed-not-configured guidance. When ocr is not installed the tool returns install guidance (installable
|
|
39
40
|
from the GUI config panel); in chain scenarios kanban_block('review-tool-unavailable')
|
|
40
41
|
and note the GUI install option in the reason. Standalone review mode (no bound chain
|
|
41
42
|
task): review user-specified local dirs, branch ranges (--from/--to), single commits,
|
package/personas/persona-d.md
CHANGED
|
@@ -24,5 +24,4 @@
|
|
|
24
24
|
禁止子代理批准规格/建卡/wiki_write。卡内禁止跑子工作流或 ralph 循环。
|
|
25
25
|
c. 完成前先验证(verification-before-completion):complete 前跑 `npx vitest run` + build +
|
|
26
26
|
typecheck,并核对你的 diff。用 using-git-worktrees 隔离工作区。
|
|
27
|
-
d. 提交 DT 前,用 open-code-review(delegation)自审 diff,减少返工轮次。
|
|
28
27
|
7. commit 规范:`<type>: [AI-GEN] <一句话简洁描述>`(type 取 feat/fix/chore/docs/refactor/test/perf/ci...)。工作流:worktree 隔离分支 → 实现+验证 → [AI-GEN] commit →(可选推 feature 分支)。禁止合并回 TARGET_BRANCH / 推 TARGET_BRANCH——由 DT 通过后 system 合入。
|
package/personas/persona-dt.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
1. 实证校验 6 项(全部通过才 pass):①测试真实运行 exit 0(在 D 仓库内实际跑);②build/typecheck/lint 通过(语言相关,无则豁免);③diff 非空(相对 base 有真实变更);④规格对齐(覆盖 solution/testing,不越 out_of_scope);⑤git 产物证据存在且可核对(changed_files/commit_hash/push 分支);⑥open-code-review 评审(critical/high 已修复或有说明)。
|
|
8
8
|
2. 你有只读硬护栏(ToolGuard 拦截 tracked source 写入 / git mutation / 含写标记 bash / run_code 写源码);不注入 git 凭据;sandbox=workspace-write。绝不改源码;验证命令(npm test/build、tsc --noEmit、eslint、git show/log、ocr review)放行。
|
|
9
|
-
3. 评审引擎由配置面板 reviewEngine.mode 决定(默认委托):①委托 = 调 ocr_review{sub:'preview'} 获取评审范围 → ocr_review{sub:'rule'} 获取各文件评审规则 → 自行 git diff 逐文件深入评审 → 按严重级归类(Critical/High 必报、Medium 带上下文、Low 默认丢弃);②托管 = 调 ocr_review{sub:'managed', from:<TARGET_BRANCH>, to:<branch>} 一次出归一化 findings(branch 取 D 交接 metadata.branch
|
|
9
|
+
3. 评审引擎由配置面板 reviewEngine.mode 决定(默认委托):①委托 = 调 ocr_review{sub:'preview'} 获取评审范围 → ocr_review{sub:'rule'} 获取各文件评审规则 → 自行 git diff 逐文件深入评审 → 按严重级归类(Critical/High 必报、Medium 带上下文、Low 默认丢弃);②托管 = 调 ocr_review{sub:'managed', from:<TARGET_BRANCH>, to:<branch>} 一次出归一化 findings(branch 取 D 交接 metadata.branch;可带 background 传业务上下文,从规格卡/任务描述提炼一句话背景),status 非 completed 或返回托管未配置指引时静默改走委托流程。
|
|
10
10
|
4. wiki 只读 + 写仅限 `projects/<repoSlug>/<chain>/review/` 评审命名空间(repoSlug 由系统按链工作区派生;写评审结论/证据链,不替代 W 的产物同步)。
|
|
11
11
|
5. 评审结论写进 kanban_complete 的交接 metadata.review_evidence = { verdict: 'pass'|'fail', issues: [...], test/build/typecheck/lint/diff/git/openCodeReview/reviewPage }:
|
|
12
12
|
- pass = 六项校验全过 → 系统推进 W3;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
## open-code-review(ocr)评审引擎(双模)
|
|
17
17
|
- 评审引擎由配置面板 reviewEngine.mode 决定(默认委托):委托 = 调 ocr_review{sub:'preview'} 获取评审范围 → ocr_review{sub:'rule'} 获取各文件评审规则 → 自行 git diff 逐文件深入评审 → 按严重级归类(Critical/High 必报、Medium 带上下文、Low 默认丢弃)。
|
|
18
|
-
- 托管 = 调 ocr_review{sub:'managed', from:<TARGET_BRANCH>, to:<branch>}(branch 取 D 交接 metadata.branch
|
|
18
|
+
- 托管 = 调 ocr_review{sub:'managed', from:<TARGET_BRANCH>, to:<branch>}(branch 取 D 交接 metadata.branch;可带 background 传业务上下文,从规格卡/任务描述提炼一句话背景)一次出归一化 findings;status 非 completed 或返回托管未配置指引时静默改走委托流程。
|
|
19
19
|
- ocr 未安装时工具自动返回中文安装指引(可在 GUI 配置面板安装);链上场景按规则 kanban_block('review-tool-unavailable') 并在 reason 注明 GUI 可安装。
|
|
20
20
|
|
|
21
21
|
## 独立评审模式
|