@joekytc/dsh-swarm 0.3.12 → 0.5.0
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/client/index.ts +8 -4
- package/client/session-bridge.ts +26 -2
- package/lib/client.js +22 -2
- package/lib/dispatcher/agent-runner.js +13 -13
- package/lib/dispatcher/chain-auditor.d.ts +3 -2
- package/lib/dispatcher/chain-auditor.js +2 -2
- package/lib/dispatcher/session-events.d.ts +7 -0
- package/lib/dispatcher/session-events.js +15 -0
- package/lib/dispatcher/v-orchestrator.d.ts +2 -0
- package/lib/dispatcher/v-orchestrator.js +9 -7
- package/lib/index.js +6 -3
- package/lib/roles/preset-installer.d.ts +50 -5
- package/lib/roles/preset-installer.js +154 -20
- package/package.json +22 -16
- package/personas/kanban-d/agent.cordis.yml +3 -1
- package/personas/kanban-dt/agent.cordis.yml +2 -1
- package/personas/kanban-p/agent.cordis.yml +1 -1
- package/personas/kanban-pt/agent.cordis.yml +1 -1
- package/personas/kanban-v/agent.cordis.yml +1 -1
- package/personas/kanban-w/agent.cordis.yml +1 -1
- package/personas/swarm/agent.cordis.yml +1 -1
package/client/index.ts
CHANGED
|
@@ -15,14 +15,15 @@ declare module '@deepseek-ai/cordis' {
|
|
|
15
15
|
}
|
|
16
16
|
import { ConfigSection } from './ConfigSection.js';
|
|
17
17
|
import { SWARM_CONFIG_NS } from './config-store.js';
|
|
18
|
-
import { setSessionsService } from './session-bridge.js';
|
|
18
|
+
import { setSessionsService, setSessionNavigator } from './session-bridge.js';
|
|
19
19
|
import css from './kanban.css';
|
|
20
20
|
import configCss from './config.css';
|
|
21
21
|
|
|
22
22
|
export const name = 'kanban-board';
|
|
23
23
|
|
|
24
|
-
/** 所需 client 服务(cordis fiber inject——loader 把模块导出当作对象插件传入)。
|
|
25
|
-
|
|
24
|
+
/** 所需 client 服务(cordis fiber inject——loader 把模块导出当作对象插件传入)。
|
|
25
|
+
* uiWorkspace:0.1.7 起宿主内置的视图所有者导航(会话跳转新接缝),fiber inject 会等待其就绪。 */
|
|
26
|
+
export const inject = ['slots', 'sessions', 'uiWorkspace'];
|
|
26
27
|
|
|
27
28
|
/** 浏览器半入口(roster 行 id: kanban-board):把看板挂到 conversation.view(会话中心 tab,additive)。
|
|
28
29
|
* 对齐 DSH 原生注册:对话(id=chat, order=0) → 轨迹(id=trajectory, order=10) → 看板(id=kanban, order=20)。
|
|
@@ -32,6 +33,9 @@ export function apply(ctx: Context): (() => void) | void {
|
|
|
32
33
|
// cast:@deepseek-ai/dsh-session(服务端包)也 merge 了 cordis Context.sessions: SessionStore,
|
|
33
34
|
// 类型面被其覆盖;浏览器半运行时注入的实为 dsh-api-session-controller 的 ISessions。
|
|
34
35
|
setSessionsService(ctx.sessions as unknown as ISessions);
|
|
36
|
+
// 会话跳转导航:0.2.0+ 宿主提供 uiWorkspace(视图所有者),但注册晚于 client apply——
|
|
37
|
+
// 只传惰性 getter(点击时解析+try/catch 降级),apply 期绝不触碰属性,防 cordis 未就绪 throw 炸掉入口注册。
|
|
38
|
+
setSessionNavigator(() => (ctx as { uiWorkspace?: { openSession(target: string): void } }).uiWorkspace ?? null);
|
|
35
39
|
let style: HTMLStyleElement | null = null;
|
|
36
40
|
let configStyle: HTMLStyleElement | null = null;
|
|
37
41
|
if (typeof document !== 'undefined') {
|
|
@@ -62,5 +66,5 @@ export function apply(ctx: Context): (() => void) | void {
|
|
|
62
66
|
ConfigSection as never,
|
|
63
67
|
),
|
|
64
68
|
);
|
|
65
|
-
return () => { if (style) style.remove(); if (configStyle) configStyle.remove(); setSessionsService(null); };
|
|
69
|
+
return () => { if (style) style.remove(); if (configStyle) configStyle.remove(); setSessionsService(null); setSessionNavigator(null); };
|
|
66
70
|
}
|
package/client/session-bridge.ts
CHANGED
|
@@ -32,8 +32,32 @@ export function useSessionIds(): ReadonlySet<string> {
|
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/**
|
|
35
|
+
/** 新宿主导航接缝:dsh 0.2.0 起 ISessions.open 移除,会话导航归视图所有者(uiWorkspace.openSession)。
|
|
36
|
+
* 宿主以 getter 惰性供给(uiWorkspace 注册晚于 client apply,apply 期属性访问会在 cordis 上 throw);
|
|
37
|
+
* 0.1.7 底线宿主无此服务,getter 返回 null 走旧 open 转发。 */
|
|
38
|
+
let navigator: (() => { openSession(target: string): void } | null) | null = null;
|
|
39
|
+
|
|
40
|
+
export function setSessionNavigator(next: (() => { openSession(target: string): void } | null) | null): void {
|
|
41
|
+
navigator = next;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 应用内跳转到指定会话(调用方须先经 useSessionIds 门控,宿主对不在列表的 id fail loud)。 */
|
|
36
45
|
export function openSession(id: string): void {
|
|
46
|
+
// 优先 0.2.0+ 视图所有者导航;解析失败视同服务缺席;旧宿主退回 ISessions.open 转发;两者皆缺则响亮报错。
|
|
47
|
+
if (navigator) {
|
|
48
|
+
let viaWorkspace: { openSession(target: string): void } | null = null;
|
|
49
|
+
try {
|
|
50
|
+
viaWorkspace = navigator();
|
|
51
|
+
} catch {
|
|
52
|
+
viaWorkspace = null;
|
|
53
|
+
}
|
|
54
|
+
if (viaWorkspace) {
|
|
55
|
+
viaWorkspace.openSession(id);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
37
59
|
if (!service) throw new Error('sessions service unavailable');
|
|
38
|
-
|
|
60
|
+
const open = (service as { open?: (target: string) => void }).open;
|
|
61
|
+
if (!open) throw new Error('host exposes neither uiWorkspace nor sessions.open; session jump unavailable');
|
|
62
|
+
open(id);
|
|
39
63
|
}
|
package/lib/client.js
CHANGED
|
@@ -560,9 +560,27 @@ window.__ModuleLoader__.load({
|
|
|
560
560
|
() => EMPTY
|
|
561
561
|
);
|
|
562
562
|
}
|
|
563
|
+
var navigator = null;
|
|
564
|
+
function setSessionNavigator(next) {
|
|
565
|
+
navigator = next;
|
|
566
|
+
}
|
|
563
567
|
function openSession(id) {
|
|
568
|
+
if (navigator) {
|
|
569
|
+
let viaWorkspace = null;
|
|
570
|
+
try {
|
|
571
|
+
viaWorkspace = navigator();
|
|
572
|
+
} catch {
|
|
573
|
+
viaWorkspace = null;
|
|
574
|
+
}
|
|
575
|
+
if (viaWorkspace) {
|
|
576
|
+
viaWorkspace.openSession(id);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
564
580
|
if (!service) throw new Error("sessions service unavailable");
|
|
565
|
-
service.open
|
|
581
|
+
const open = service.open;
|
|
582
|
+
if (!open) throw new Error("host exposes neither uiWorkspace nor sessions.open; session jump unavailable");
|
|
583
|
+
open(id);
|
|
566
584
|
}
|
|
567
585
|
|
|
568
586
|
// client/BoardCard.tsx
|
|
@@ -2897,9 +2915,10 @@ window.__ModuleLoader__.load({
|
|
|
2897
2915
|
|
|
2898
2916
|
// client/index.ts
|
|
2899
2917
|
var name = "kanban-board";
|
|
2900
|
-
var inject = ["slots", "sessions"];
|
|
2918
|
+
var inject = ["slots", "sessions", "uiWorkspace"];
|
|
2901
2919
|
function apply(ctx) {
|
|
2902
2920
|
setSessionsService(ctx.sessions);
|
|
2921
|
+
setSessionNavigator(() => ctx.uiWorkspace ?? null);
|
|
2903
2922
|
let style = null;
|
|
2904
2923
|
let configStyle = null;
|
|
2905
2924
|
if (typeof document !== "undefined") {
|
|
@@ -2936,6 +2955,7 @@ window.__ModuleLoader__.load({
|
|
|
2936
2955
|
if (style) style.remove();
|
|
2937
2956
|
if (configStyle) configStyle.remove();
|
|
2938
2957
|
setSessionsService(null);
|
|
2958
|
+
setSessionNavigator(null);
|
|
2939
2959
|
};
|
|
2940
2960
|
}
|
|
2941
2961
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
2
2
|
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
import { probeOcr } from '../services/ocr-cli.js';
|
|
5
6
|
import { installRoleTools, buildReadOnlyWriteGuard, buildDTWriteGuard, buildPlanWriteGuard, buildKbWriteGuard, registerDtTaskChain, unregisterDtTaskChain } from '../roles/toolsets.js';
|
|
@@ -266,7 +267,7 @@ ${task.body}`);
|
|
|
266
267
|
(task.attempts > 0 || state.events.some((e) => e.taskId === taskId && e.kind === 'task/claimed'));
|
|
267
268
|
let agent;
|
|
268
269
|
let context = '';
|
|
269
|
-
const setup = async (agentCtx) => {
|
|
270
|
+
const setup = async (agentCtx, agent) => {
|
|
270
271
|
// 思考等级强制(waterfall):宿主 selection 无 create-options 覆盖层(dsh-host-apiproxy 的
|
|
271
272
|
// selectionFor 不消费 agentOptions),AgentOptions 也仅有 provider/model/maxTokens——agentOptions
|
|
272
273
|
// 里的 reasoningEffort 不被宿主消费,新建角色会话思考等级会落回宿主默认。改走 DSH agent/request
|
|
@@ -283,7 +284,8 @@ ${task.body}`);
|
|
|
283
284
|
// P/W/D = full access(跨目录读:P 读仓库/外部实证、W 读计划、D 执行);
|
|
284
285
|
// 但 P 挂 plan 写护栏、W 挂只读护栏(写边界由工具级强制,防"改动源码",不靠 prompt 软约束);
|
|
285
286
|
// PT/DT/V → workspace-write(评审/编排最小权限,PT/DT 只读护栏由 ToolGuard 独立保证)。
|
|
286
|
-
|
|
287
|
+
// approval/sandbox 种子事件经 AgentSetup 第二参的 agent 显式直写(0.2.0 起 ctx.agent 挂载已移除)。
|
|
288
|
+
const session = agent?.session;
|
|
287
289
|
session?.append?.('approval/policy', { policy: 'never', source: 'delegation' });
|
|
288
290
|
const fullAccess = isDExecute || task.assignee === 'p' || task.assignee === 'w';
|
|
289
291
|
session?.append?.('sandbox/mode', fullAccess ? { mode: 'danger-full-access', source: 'delegation' } : { mode: 'workspace-write', source: 'delegation' });
|
|
@@ -318,16 +320,13 @@ ${task.body}`);
|
|
|
318
320
|
// PT/DT 是 workspace-write(sandbox_permissions 扩权到天花板是合法功能,剥了破坏官方能力);
|
|
319
321
|
// W 虽 fullAccess 但只读护栏全拒 write,shadow 无意义(最小挂载面,V 无执行工具不涉及)。
|
|
320
322
|
if (task.assignee === 'p' || isDExecute) {
|
|
321
|
-
installCleanFsTools(agentCtx,
|
|
323
|
+
installCleanFsTools(agentCtx, agent);
|
|
322
324
|
}
|
|
323
325
|
// 组合标记——角色工具面成功组合后,把 { role, taskId } 记到进程内 WeakMap(键=Agent 实例)。
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const liveAgent = agentCtx.agent;
|
|
329
|
-
if (liveAgent)
|
|
330
|
-
markRoleComposition(liveAgent, { role: task.assignee, taskId: task.id });
|
|
326
|
+
// AgentSetup 第二参收到的 Agent 与发布后 agents.get(id) 返回的是同一实例,故此处键入的实例
|
|
327
|
+
// 即 resumeOrReuse 里 agents.get 命中的实例(live 复用走标记匹配,能力无损)。
|
|
328
|
+
if (agent)
|
|
329
|
+
markRoleComposition(agent, { role: task.assignee, taskId: task.id });
|
|
331
330
|
// 只读评审角色(PT/DT)注册 ToolGuard:拦截 tracked source 写入 / git mutation / 含写标记 bash。
|
|
332
331
|
// 以 dsh-tools 类型为准:tools.guard(execution => reason|undefined),execution.name/arguments 为实际字段。
|
|
333
332
|
if (task.assignee === 'pt' || task.assignee === 'dt') {
|
|
@@ -492,7 +491,7 @@ ${task.body}`);
|
|
|
492
491
|
const attachId = task.resumeSessionId ?? `kbn-${task.id}`;
|
|
493
492
|
await attachSessionToWorkspace(this.ctx, attachId, sessionCwd, 'task ' + task.id + ' ' + task.assignee + '/' + task.mode);
|
|
494
493
|
try {
|
|
495
|
-
agent.followup({ content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
494
|
+
agent.followup({ id: randomUUID(), content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
496
495
|
console.error('[dsh-swarm][debug] runner followup sent ' + taskId);
|
|
497
496
|
await agent.whenIdle();
|
|
498
497
|
console.error('[dsh-swarm][debug] runner whenIdle resolved ' + taskId);
|
|
@@ -510,7 +509,7 @@ ${task.body}`);
|
|
|
510
509
|
: (sessionLike.events ?? []);
|
|
511
510
|
if (turnEvents.filter((e) => (Number(e?.seq) || 0) > eventsBase).some(isGuardSynthesizedReply)) {
|
|
512
511
|
console.error('[dsh-swarm][debug] runner guard-synthesized reply detected (from-cache/from-security-guard) ' + taskId + ' — auto followup retry once');
|
|
513
|
-
agent.followup({ content: [{ type: 'text', text: '继续未完成的任务:上一条回复是网关安全护栏/语义缓存误拦的合成拒答,并非你的真实结论。请忽略它,从中断处继续执行任务,直至按协议收敛终态(kanban_complete 或 kanban_block)。' }], source: { kind: 'user' } });
|
|
512
|
+
agent.followup({ id: randomUUID(), content: [{ type: 'text', text: '继续未完成的任务:上一条回复是网关安全护栏/语义缓存误拦的合成拒答,并非你的真实结论。请忽略它,从中断处继续执行任务,直至按协议收敛终态(kanban_complete 或 kanban_block)。' }], source: { kind: 'user' } });
|
|
514
513
|
console.error('[dsh-swarm][debug] runner guard-retry followup sent ' + taskId);
|
|
515
514
|
await agent.whenIdle();
|
|
516
515
|
console.error('[dsh-swarm][debug] runner guard-retry whenIdle resolved ' + taskId);
|
|
@@ -650,7 +649,8 @@ ${task.body}`);
|
|
|
650
649
|
// GUI 默认组合 incarnation:缺 kanban_complete → 重跑 setup 幂等补挂后复用。
|
|
651
650
|
// setup 对 live ctx 的各步均可加:effort waterfall/approval+sandbox append(known 事件类型、
|
|
652
651
|
// latest-wins)/preset mount(bindings 覆盖)/角色工具(此前为默认组合,无同名冲突)/护栏。
|
|
653
|
-
|
|
652
|
+
// 双参签名(0.2.0):repair 重跑 setup 时显式传 live agent 实例(标记随修复自愈)。
|
|
653
|
+
await opts.setup(liveCtx, live);
|
|
654
654
|
if (toolsSvc.get('kanban_complete', live))
|
|
655
655
|
return live; // 修复后必须验证到位,防静默半修复
|
|
656
656
|
throw new Error('live session composition repair failed: kanban_complete still missing after re-setup (session ' + sessionId + ')');
|
|
@@ -11,8 +11,9 @@ import type { AuditEvidence, Task } from '../domain/types.js';
|
|
|
11
11
|
* a. 作用域收窄:仅扫描会话工作区(session.header.cwd)位于本链发起工作区
|
|
12
12
|
* (Chain.workspaceDir = /plan: 主 agent 所在工作空间)内的会话,
|
|
13
13
|
* 排除其他项目的主会话(如 评估 项目里调试 dsh-swarm 的会话);
|
|
14
|
-
* b. 行为判定:run_code
|
|
15
|
-
*
|
|
14
|
+
* b. 行为判定:run_code 按实际派发子调用(新旧宿主双名 tool/code-dispatch[-start] /
|
|
15
|
+
* tool/ptc-dispatch[-start],经 rootCallId 关联外层调用)判定是否真的发生写,而非把
|
|
16
|
+
* run_code 一律视为写;
|
|
16
17
|
* c. 只读排除:bash 命令 / 兜底 code 字符串仅当含写操作标记(BASH_WRITE_RE)且
|
|
17
18
|
* 含 workspacesRoot 路径时才计为写证据;纯只读排查(ls/cat/glob/read/grep)不产生证据。
|
|
18
19
|
* 2. 产物归属核对(fallback,机械可测):枚举 workspaces/<chainId>/ 下条目;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/dispatcher/chain-auditor.ts
|
|
2
2
|
import { readdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { eventType, toolArgs, toolName } from './session-events.js';
|
|
4
|
+
import { eventType, isRunCodeDispatchEvent, toolArgs, toolName } from './session-events.js';
|
|
5
5
|
import { isPathInside } from './target-repo.js';
|
|
6
6
|
import { headerPresetOf } from './session-preset.js';
|
|
7
7
|
/** 直接写工具(无条件视为写能力;只读工具如 read/glob/grep 不算)。 */
|
|
@@ -113,7 +113,7 @@ function sessionWriteEvidence(id, workspacesRoot, events) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
else if (
|
|
116
|
+
else if (isRunCodeDispatchEvent(ev)) {
|
|
117
117
|
const rootCallId = String(dataField(ev, 'rootCallId') ?? '');
|
|
118
118
|
const name = String(dataField(ev, 'name') ?? '');
|
|
119
119
|
if (!rootCallId || !name)
|
|
@@ -18,6 +18,13 @@ export declare function eventType(e: {
|
|
|
18
18
|
export declare function toolName(e: unknown): string | undefined;
|
|
19
19
|
/** 取工具调用参数对象(兼容 JSON 字符串 / 对象两种落盘形态)。 */
|
|
20
20
|
export declare function toolArgs(e: unknown): Record<string, unknown>;
|
|
21
|
+
/** run_code 派发子调用事件名(start 与完成两种)新旧宿主双名集合:
|
|
22
|
+
* 旧宿主会话落盘为 tool/code-dispatch-start / tool/code-dispatch;
|
|
23
|
+
* 宿主 V3+ 会话格式改名为 tool/ptc-dispatch-start / tool/ptc-dispatch。
|
|
24
|
+
* 消费方(链审计器按派发子调用判定 run_code 实际写行为)双名等价命中,勿散落硬编码。 */
|
|
25
|
+
export declare const RUN_CODE_DISPATCH_EVENT_TYPES: ReadonlySet<string>;
|
|
26
|
+
/** 该事件是否为 run_code 派发子调用事件(start 或完成,新旧事件名皆认)。 */
|
|
27
|
+
export declare function isRunCodeDispatchEvent(e: unknown): boolean;
|
|
21
28
|
/** 取 assistant 消息的网关缓存回放标记(replayState.response.responseModel,如 'from-cache')。
|
|
22
29
|
* 非 assistant/message 或无标记返回 null。2026-09-04:远程网关回复缓存整包回放时由 SSE
|
|
23
30
|
* id/model 字段透传(dsh 纯透传),插件侧据此识别缓存劫持零产出。兼容落盘(data.message)
|
|
@@ -35,6 +35,21 @@ export function toolArgs(e) {
|
|
|
35
35
|
}
|
|
36
36
|
return {};
|
|
37
37
|
}
|
|
38
|
+
/** run_code 派发子调用事件名(start 与完成两种)新旧宿主双名集合:
|
|
39
|
+
* 旧宿主会话落盘为 tool/code-dispatch-start / tool/code-dispatch;
|
|
40
|
+
* 宿主 V3+ 会话格式改名为 tool/ptc-dispatch-start / tool/ptc-dispatch。
|
|
41
|
+
* 消费方(链审计器按派发子调用判定 run_code 实际写行为)双名等价命中,勿散落硬编码。 */
|
|
42
|
+
export const RUN_CODE_DISPATCH_EVENT_TYPES = new Set([
|
|
43
|
+
'tool/code-dispatch-start',
|
|
44
|
+
'tool/code-dispatch',
|
|
45
|
+
'tool/ptc-dispatch-start',
|
|
46
|
+
'tool/ptc-dispatch',
|
|
47
|
+
]);
|
|
48
|
+
/** 该事件是否为 run_code 派发子调用事件(start 或完成,新旧事件名皆认)。 */
|
|
49
|
+
export function isRunCodeDispatchEvent(e) {
|
|
50
|
+
const t = eventType(e);
|
|
51
|
+
return t !== undefined && RUN_CODE_DISPATCH_EVENT_TYPES.has(t);
|
|
52
|
+
}
|
|
38
53
|
/** 取 assistant 消息的网关缓存回放标记(replayState.response.responseModel,如 'from-cache')。
|
|
39
54
|
* 非 assistant/message 或无标记返回 null。2026-09-04:远程网关回复缓存整包回放时由 SSE
|
|
40
55
|
* id/model 字段透传(dsh 纯透传),插件侧据此识别缓存劫持零产出。兼容落盘(data.message)
|
|
@@ -34,7 +34,9 @@ export declare function buildPhaseInstruction(phase: VPhase, ctx: {
|
|
|
34
34
|
taskId?: string;
|
|
35
35
|
}, kbMode: 'remote' | 'local'): string;
|
|
36
36
|
interface AgentLike {
|
|
37
|
+
/** 宿主 UserMessage 要求 identified(id 必填):缺 id 的消息会被 v4 会话校验拒读。 */
|
|
37
38
|
followup(msg: {
|
|
39
|
+
id: string;
|
|
38
40
|
content: {
|
|
39
41
|
type: string;
|
|
40
42
|
text: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { installRoleTools } from '../roles/toolsets.js';
|
|
2
3
|
import { mountOrRecompose } from '../roles/preset-installer.js';
|
|
3
4
|
import { resolveTaskParents } from '../domain/task-parents.js';
|
|
@@ -266,7 +267,7 @@ export class VOrchestrator {
|
|
|
266
267
|
'gave_up 任务说明链路已停止,建议查看对应 [blocked-final] 证据链(block 时间线 + 复核/评论时间线 + 最终原因),给出终态解释。',
|
|
267
268
|
'规则:只评论、不建卡、不改任务状态;已有 [blocked-review] 评论的任务不要重复评论。',
|
|
268
269
|
].join('\n\n');
|
|
269
|
-
agent.followup({ content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
270
|
+
agent.followup({ id: randomUUID(), content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
270
271
|
await agent.whenIdle();
|
|
271
272
|
return; // 本轮 V 唯一动作是阻塞复核,不再推进阶段
|
|
272
273
|
}
|
|
@@ -411,7 +412,7 @@ export class VOrchestrator {
|
|
|
411
412
|
let turnError = null;
|
|
412
413
|
try {
|
|
413
414
|
agent = await this.getVAgent(orch);
|
|
414
|
-
agent.followup({ content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
415
|
+
agent.followup({ id: randomUUID(), content: [{ type: 'text', text: context }], source: { kind: 'user' } });
|
|
415
416
|
await agent.whenIdle();
|
|
416
417
|
}
|
|
417
418
|
catch (err) {
|
|
@@ -619,7 +620,7 @@ export class VOrchestrator {
|
|
|
619
620
|
// 与 agent-runner.ts 的 installRoleTools 用法一致。
|
|
620
621
|
// V 后台编排会话与 P/W/D 一致,显式设置 approval=never + sandbox=workspace-write,
|
|
621
622
|
// 避免在无 preset 装配时因默认审批策略在后台无应答者而挂起(卡死调度器首轮 tick)。
|
|
622
|
-
const setup = async (agentCtx) => {
|
|
623
|
+
const setup = async (agentCtx, agent) => {
|
|
623
624
|
// 思考等级强制(waterfall):与 agent-runner 同缺陷——宿主 selection 无 create-options 覆盖层,
|
|
624
625
|
// agentOptions.reasoningEffort 不被消费,V 编排会话思考等级会落回宿主默认。走 DSH agent/request
|
|
625
626
|
// waterfall 逐请求强制 'high'(宿主 installModelSelection 同机制),作用域仅本 V 会话。
|
|
@@ -629,7 +630,8 @@ export class VOrchestrator {
|
|
|
629
630
|
const resolved = await next();
|
|
630
631
|
return { ...resolved, reasoningEffort: 'high' };
|
|
631
632
|
});
|
|
632
|
-
|
|
633
|
+
// approval/sandbox 种子事件经 AgentSetup 第二参的 agent 显式直写(0.2.0 起 ctx.agent 挂载已移除)。
|
|
634
|
+
const session = agent?.session;
|
|
633
635
|
session?.append?.('approval/policy', { policy: 'never', source: 'delegation' });
|
|
634
636
|
session?.append?.('sandbox/mode', { mode: 'workspace-write', source: 'delegation' });
|
|
635
637
|
// 对齐(2026-08-17):V=butler·orchestrator 零执行能力——先挂 kanban-v 裁剪 preset
|
|
@@ -653,9 +655,9 @@ export class VOrchestrator {
|
|
|
653
655
|
}
|
|
654
656
|
await installRoleTools(agentCtx, 'v', { kanban: this.kanban, wiki: this.wiki });
|
|
655
657
|
// setup 完整成功(mount + 工具面)后写身份标记——live 复用校验依据(同 agent-runner 组合标记机制)。
|
|
656
|
-
|
|
657
|
-
if (
|
|
658
|
-
vSessionCompositions.set(
|
|
658
|
+
// 键 = AgentSetup 第二参的 Agent 实例(与发布后 agents.get(id) 返回同一实例)。
|
|
659
|
+
if (agent)
|
|
660
|
+
vSessionCompositions.set(agent, V_SESSION_PRESET_ID);
|
|
659
661
|
};
|
|
660
662
|
// 模型候选链:V 会话 create/resume 按 primary→fallbacks 静默切换;
|
|
661
663
|
// V 无任务卡可 block——全候选不可用抛最后错误(wakeV 调用方按既有错误路径处理)。
|
package/lib/index.js
CHANGED
|
@@ -36,10 +36,13 @@ export function apply(ctx, config) {
|
|
|
36
36
|
const configProvider = new ConfigProvider(ctx, config, storageDir);
|
|
37
37
|
// KanbanProvider 持 ConfigProvider 引用,kb_url base 经 getter 热读取(配置面板改后无需重建)。
|
|
38
38
|
const provider = new KanbanProvider(ctx, config, configProvider);
|
|
39
|
-
//
|
|
40
|
-
|
|
39
|
+
// 双路安装角色裁剪 preset:目录写 $DSH_HOME/.agent-presets/(0.1.7-rc.x 宿主发现根)+
|
|
40
|
+
// 运行时注册 agentPresets.register(0.2.0 宿主唯一生效路径)。注册等服务就绪后完成,
|
|
41
|
+
// 由 installer 如实记录「目录写 / 运行时注册」各自清单,不假阳性。
|
|
42
|
+
void installRolePresets(ctx).catch((err) => {
|
|
43
|
+
console.warn('[dsh-swarm] role preset install error: ' + String(err));
|
|
44
|
+
});
|
|
41
45
|
installLlWikiSkill(); // 尽力而为:失败仅告警不阻断
|
|
42
|
-
console.info('[dsh-swarm] role presets installed: ' + (installed.length ? installed.join(',') : 'none'));
|
|
43
46
|
// IM 投递(企微,2026-09-07 评审决议):W3 收尾/链阻塞时经 dsh-im 投群。
|
|
44
47
|
// imDelivery.enabled=false(默认)时事件处理器零开销早退;dshIm 服务缺失显式降级留痕。
|
|
45
48
|
const disposeImDelivery = wireImDelivery(ctx, provider.service, configProvider);
|
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
/** 包内组合目录(随包分发)。src/roles/ 与 lib/roles/ 深度一致,均经 ../../ 回到包根。 */
|
|
2
2
|
export declare function packagePresetsDir(): string;
|
|
3
|
-
/** $DSH_HOME/.agent-presets(
|
|
3
|
+
/** $DSH_HOME/.agent-presets(0.1.7-rc.x 宿主用户预设根;DSH_HOME 缺省 ~/.dsh)。 */
|
|
4
4
|
export declare function userPresetsRoot(): string;
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/** 单条 preset 组合行(官方 PresetDefinition.plugins 行的结构面:cordis entry list)。 */
|
|
6
|
+
export interface PresetPluginRow {
|
|
7
|
+
readonly id?: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly config?: unknown;
|
|
10
|
+
readonly disabled?: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** 注册给宿主 registry 的 preset 定义(官方 PresetDefinition 的结构面)。 */
|
|
13
|
+
export interface PresetDefinitionLike {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly name?: string;
|
|
16
|
+
readonly description?: string;
|
|
17
|
+
readonly order?: number;
|
|
18
|
+
readonly plugins: readonly PresetPluginRow[];
|
|
19
|
+
}
|
|
20
|
+
/** 宿主 agentPresets registry 的结构面(本插件只消费 register)。 */
|
|
21
|
+
export interface PresetRegistryLike {
|
|
22
|
+
register(definition: PresetDefinitionLike): Promise<() => Promise<void>>;
|
|
23
|
+
}
|
|
24
|
+
/** 安装期 ctx 的最小面(只需可选 get)。 */
|
|
25
|
+
export interface PresetCtxLike {
|
|
26
|
+
get?(name: string): unknown;
|
|
27
|
+
}
|
|
28
|
+
/** 解析包内单个 preset 的组合与元数据为宿主 registry 定义;组合缺失返回 undefined。 */
|
|
29
|
+
export declare function readPresetDefinition(pkgDir: string, id: string): PresetDefinitionLike | undefined;
|
|
30
|
+
/** 路径二:运行时注册全部包内 preset;返回注册成功/失败 id 列表与成功项的撤销句柄(组合缺失的 id 不计)。 */
|
|
31
|
+
export declare function registerRolePresets(registry: PresetRegistryLike, ids: readonly string[], pkgDir?: string): Promise<{
|
|
32
|
+
registered: string[];
|
|
33
|
+
failed: string[];
|
|
34
|
+
disposers: Array<() => Promise<void>>;
|
|
35
|
+
}>;
|
|
36
|
+
/** 安装结果(如实区分两条路径,不假阳性)。 */
|
|
37
|
+
export interface RolePresetInstallReport {
|
|
38
|
+
/** 目录写成功的 preset id(路径一)。 */
|
|
39
|
+
dirWritten: string[];
|
|
40
|
+
/** 运行时注册成功的 preset id(路径二;服务不可用时为空)。 */
|
|
41
|
+
runtimeRegistered: string[];
|
|
42
|
+
/** 运行时注册失败的 preset id。 */
|
|
43
|
+
runtimeFailed: string[];
|
|
44
|
+
/** 路径二实际形态:runtime=已注册;unavailable=宿主无 agentPresets 服务(仅目录写生效)。 */
|
|
45
|
+
runtimeMode: 'runtime' | 'unavailable';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 双路安装入口。目录写同步完成并即时留痕;运行时注册等服务就绪后完成并留痕。
|
|
49
|
+
* 返回前目录写已落盘;注册部分异步收敛(fire-and-forget 调用方须 catch)。
|
|
50
|
+
*/
|
|
51
|
+
export declare function installRolePresets(ctx: PresetCtxLike, opts?: {
|
|
52
|
+
registerWaitMs?: number;
|
|
53
|
+
}): Promise<RolePresetInstallReport>;
|
|
9
54
|
/** 角色 preset 挂载的容器面(dsh-agent-presets roster 的最小结构类型)。 */
|
|
10
55
|
export interface PresetMountLike {
|
|
11
56
|
mount(ctx: unknown, id: string): Promise<unknown>;
|
|
@@ -1,36 +1,103 @@
|
|
|
1
1
|
// src/roles/preset-installer.ts
|
|
2
|
-
import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { JSON_SCHEMA, load as loadYaml, Type } from 'js-yaml';
|
|
6
7
|
/**
|
|
7
|
-
* 角色裁剪 preset
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
8
|
+
* 角色裁剪 preset 的双路安装:
|
|
9
|
+
*
|
|
10
|
+
* 路径一 · 目录写(0.1.7-rc.x 宿主的底线):把包内组合复制到 `$DSH_HOME/.agent-presets/<id>/`,
|
|
11
|
+
* 老宿主的 agent-presets 发现逻辑扫该目录完成装载。0.2.0 宿主不再扫描任何目录,此路不影响新宿主。
|
|
12
|
+
*
|
|
13
|
+
* 路径二 · 运行时注册(0.2.0 起的唯一生效路径):0.2.0 的 dsh-agent-preset-registry 是纯运行时
|
|
14
|
+
* 服务,preset 定义只经 `agentPresets.register(definition)` 进入(官方 dsh-agent-preset 插件
|
|
15
|
+
* 即在 Service.init 中 yield register(config)),无任何目录发现。本插件经 ctx 取 agentPresets
|
|
16
|
+
* 服务(未就绪则短轮询等待),把与目录写同一份 yml 解析出的定义注册进去,两个路径语义同源。
|
|
17
|
+
*
|
|
18
|
+
* 幂等:目录写覆盖同名文件;运行时注册遇宿主 "Duplicate agent preset" 视为已注册成功。
|
|
19
|
+
* 尽力而为:单 preset 目录写/注册失败仅告警不阻断插件启动,后续挂载失败由 runner 降级日志兜底。
|
|
17
20
|
*/
|
|
18
21
|
const PRESET_IDS = ['kanban-v', 'kanban-p', 'kanban-w', 'kanban-d', 'kanban-pt', 'kanban-dt', 'swarm'];
|
|
22
|
+
/** 等待 agentPresets 服务就绪的超时(宿主组合启动期服务装配可能晚于插件 apply)。 */
|
|
23
|
+
const REGISTER_WAIT_MS = 60000;
|
|
19
24
|
/** 包内组合目录(随包分发)。src/roles/ 与 lib/roles/ 深度一致,均经 ../../ 回到包根。 */
|
|
20
25
|
export function packagePresetsDir() {
|
|
21
26
|
return fileURLToPath(new URL('../../personas/', import.meta.url));
|
|
22
27
|
}
|
|
23
|
-
/** $DSH_HOME/.agent-presets(
|
|
28
|
+
/** $DSH_HOME/.agent-presets(0.1.7-rc.x 宿主用户预设根;DSH_HOME 缺省 ~/.dsh)。 */
|
|
24
29
|
export function userPresetsRoot() {
|
|
25
30
|
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), '.agent-presets');
|
|
26
31
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
// 宿主 entry-list YAML 方言的镜像(@deepseek-ai/cordis-plugin-include 的 entryListSchema):
|
|
33
|
+
// `!!js` 标量解析为 { __jsExpr } 表达式节点,由宿主 Loader 在行激活时求值(组合内
|
|
34
|
+
// `disabled: !!js process.platform === 'win32'` 等行依赖此节点形态,禁在解析期求值)。
|
|
35
|
+
const JS_EXPR_TAG = new Type('tag:yaml.org,2002:js', {
|
|
36
|
+
kind: 'scalar',
|
|
37
|
+
resolve: (data) => typeof data === 'string',
|
|
38
|
+
construct: (data) => ({ __jsExpr: data }),
|
|
39
|
+
});
|
|
40
|
+
const ENTRY_LIST_SCHEMA = JSON_SCHEMA.extend(JS_EXPR_TAG);
|
|
41
|
+
/** 镜像宿主 registry 的入口列表形状校验:顶层为带 name 的行数组(组行递归其 config)。 */
|
|
42
|
+
function entryListProblem(rows, at = '') {
|
|
43
|
+
if (!Array.isArray(rows))
|
|
44
|
+
return at === '' ? 'composition must be a top-level list of plugin rows' : `group ${at} must hold a list of plugin rows`;
|
|
45
|
+
for (const [index, row] of rows.entries()) {
|
|
46
|
+
const label = at === '' ? `row ${String(index + 1)}` : `${at} row ${String(index + 1)}`;
|
|
47
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row))
|
|
48
|
+
return `${label} is not a plugin row`;
|
|
49
|
+
const name = row.name;
|
|
50
|
+
if (typeof name !== 'string' || name === '')
|
|
51
|
+
return `${label} names no plugin (a "name" string is required)`;
|
|
52
|
+
const config = row.config;
|
|
53
|
+
if (row.group === true && config !== undefined) {
|
|
54
|
+
const nested = entryListProblem(config, label);
|
|
55
|
+
if (nested !== undefined)
|
|
56
|
+
return nested;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
/** 解析包内单个 preset 的组合与元数据为宿主 registry 定义;组合缺失返回 undefined。 */
|
|
62
|
+
export function readPresetDefinition(pkgDir, id) {
|
|
63
|
+
const srcFile = join(pkgDir, id, 'agent.cordis.yml');
|
|
64
|
+
if (!existsSync(srcFile))
|
|
65
|
+
return undefined;
|
|
66
|
+
let rows;
|
|
67
|
+
let meta;
|
|
68
|
+
try {
|
|
69
|
+
rows = loadYaml(readFileSync(srcFile, 'utf8'), { schema: ENTRY_LIST_SCHEMA });
|
|
70
|
+
const metaFile = join(pkgDir, id, 'preset.yml');
|
|
71
|
+
meta = existsSync(metaFile)
|
|
72
|
+
? loadYaml(readFileSync(metaFile, 'utf8'), { schema: JSON_SCHEMA })
|
|
73
|
+
: {};
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.warn('[dsh-swarm] role preset parse failed ' + id + ': ' + String(err));
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const problem = entryListProblem(rows);
|
|
80
|
+
if (problem !== undefined) {
|
|
81
|
+
console.warn('[dsh-swarm] role preset composition invalid ' + id + ': ' + problem);
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
const definition = {
|
|
85
|
+
id,
|
|
86
|
+
plugins: rows,
|
|
87
|
+
};
|
|
88
|
+
if (typeof meta.name === 'string')
|
|
89
|
+
definition.name = meta.name;
|
|
90
|
+
if (typeof meta.description === 'string')
|
|
91
|
+
definition.description = meta.description;
|
|
92
|
+
if (typeof meta.order === 'number')
|
|
93
|
+
definition.order = meta.order;
|
|
94
|
+
return definition;
|
|
95
|
+
}
|
|
96
|
+
/** 路径一:目录写(0.1.7-rc.x 宿主发现根)。返回成功写入的 preset id 列表。 */
|
|
97
|
+
function writeUserDirPresets() {
|
|
31
98
|
const src = packagePresetsDir();
|
|
32
99
|
const dstRoot = userPresetsRoot();
|
|
33
|
-
const
|
|
100
|
+
const written = [];
|
|
34
101
|
for (const id of PRESET_IDS) {
|
|
35
102
|
const srcFile = join(src, id, 'agent.cordis.yml');
|
|
36
103
|
if (!existsSync(srcFile))
|
|
@@ -42,14 +109,81 @@ export function installRolePresets() {
|
|
|
42
109
|
const meta = join(src, id, 'preset.yml');
|
|
43
110
|
if (existsSync(meta))
|
|
44
111
|
copyFileSync(meta, join(dstDir, 'preset.yml'));
|
|
45
|
-
|
|
112
|
+
written.push(id);
|
|
46
113
|
}
|
|
47
114
|
catch (err) {
|
|
48
115
|
// 尽力而为:写入失败(如沙箱/权限拒绝)仅告警,不阻断插件启动
|
|
49
|
-
console.warn('[dsh-swarm] role preset
|
|
116
|
+
console.warn('[dsh-swarm] role preset dir write skipped ' + id + ' -> ' + dstRoot + ': ' + String(err));
|
|
50
117
|
}
|
|
51
118
|
}
|
|
52
|
-
return
|
|
119
|
+
return written;
|
|
120
|
+
}
|
|
121
|
+
/** 路径二:运行时注册全部包内 preset;返回注册成功/失败 id 列表与成功项的撤销句柄(组合缺失的 id 不计)。 */
|
|
122
|
+
export async function registerRolePresets(registry, ids, pkgDir = packagePresetsDir()) {
|
|
123
|
+
const registered = [];
|
|
124
|
+
const failed = [];
|
|
125
|
+
const disposers = [];
|
|
126
|
+
for (const id of ids) {
|
|
127
|
+
const definition = readPresetDefinition(pkgDir, id);
|
|
128
|
+
if (!definition)
|
|
129
|
+
continue;
|
|
130
|
+
try {
|
|
131
|
+
disposers.push(await registry.register(definition));
|
|
132
|
+
registered.push(id);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
// 插件重 apply 时定义已在 registry:与首次注册语义等价,按成功计
|
|
136
|
+
if (String(err).includes('Duplicate agent preset')) {
|
|
137
|
+
registered.push(id);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
failed.push(id);
|
|
141
|
+
console.warn('[dsh-swarm] role preset runtime register failed ' + id + ': ' + String(err));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { registered, failed, disposers };
|
|
145
|
+
}
|
|
146
|
+
/** 短轮询等待宿主 agentPresets 服务就绪;超时返回 undefined(降级仅目录写)。 */
|
|
147
|
+
async function waitForPresets(ctx, timeoutMs) {
|
|
148
|
+
const started = Date.now();
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
const poll = setInterval(() => {
|
|
151
|
+
const found = ctx.get?.('agentPresets');
|
|
152
|
+
if (found) {
|
|
153
|
+
clearInterval(poll);
|
|
154
|
+
resolve(found);
|
|
155
|
+
}
|
|
156
|
+
else if (Date.now() - started >= timeoutMs) {
|
|
157
|
+
clearInterval(poll);
|
|
158
|
+
resolve(undefined);
|
|
159
|
+
}
|
|
160
|
+
}, 500);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 双路安装入口。目录写同步完成并即时留痕;运行时注册等服务就绪后完成并留痕。
|
|
165
|
+
* 返回前目录写已落盘;注册部分异步收敛(fire-and-forget 调用方须 catch)。
|
|
166
|
+
*/
|
|
167
|
+
export async function installRolePresets(ctx, opts = {}) {
|
|
168
|
+
const dirWritten = writeUserDirPresets();
|
|
169
|
+
console.info('[dsh-swarm] role preset dir write: ' + (dirWritten.length ? dirWritten.join(',') : 'none'));
|
|
170
|
+
const registry = await waitForPresets(ctx, opts.registerWaitMs ?? REGISTER_WAIT_MS);
|
|
171
|
+
if (!registry) {
|
|
172
|
+
console.warn('[dsh-swarm] role presets installed: dir-write=' + (dirWritten.join(',') || 'none')
|
|
173
|
+
+ ' runtime-register=none (agentPresets service unavailable; directory write only)');
|
|
174
|
+
return { dirWritten, runtimeRegistered: [], runtimeFailed: [], runtimeMode: 'unavailable' };
|
|
175
|
+
}
|
|
176
|
+
const { registered, failed, disposers } = await registerRolePresets(registry, PRESET_IDS);
|
|
177
|
+
console.info('[dsh-swarm] role presets installed: dir-write=' + (dirWritten.join(',') || 'none')
|
|
178
|
+
+ ' runtime-register=' + (registered.join(',') || 'none')
|
|
179
|
+
+ (failed.length ? ' failed=' + failed.join(',') : ''));
|
|
180
|
+
// 插件卸载/重载时撤销注册,避免宿主 registry 留下失效定义(官方 register 契约:声明方持有 disposer)。
|
|
181
|
+
ctx.on?.('dispose', () => {
|
|
182
|
+
for (const dispose of disposers.splice(0))
|
|
183
|
+
void dispose().catch(() => { });
|
|
184
|
+
});
|
|
185
|
+
const report = { dirWritten, runtimeRegistered: registered, runtimeFailed: failed, runtimeMode: 'runtime' };
|
|
186
|
+
return report;
|
|
53
187
|
}
|
|
54
188
|
/**
|
|
55
189
|
* preset 挂载收敛入口(2026-09-21 事故修复):宿主 dsh-scope 的 scope key 绑定在进程
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joekytc/dsh-swarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"client": {
|
|
34
34
|
"platform": "web",
|
|
35
35
|
"inject": [
|
|
36
|
-
"@deepseek-ai/dsh-client-ui-conversation"
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-workspace"
|
|
37
38
|
]
|
|
38
39
|
}
|
|
39
40
|
},
|
|
@@ -58,27 +59,32 @@
|
|
|
58
59
|
"test": "vitest run",
|
|
59
60
|
"build:client": "node scripts/build-client.mjs"
|
|
60
61
|
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"js-yaml": "^4.3.2"
|
|
64
|
+
},
|
|
61
65
|
"peerDependencies": {
|
|
62
66
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
63
|
-
"@deepseek-ai/dsh-agent": "
|
|
64
|
-
"@deepseek-ai/dsh-api-session-controller": "
|
|
65
|
-
"@deepseek-ai/dsh-client-ui-conversation": "
|
|
66
|
-
"@deepseek-ai/dsh-credentials": "
|
|
67
|
-
"@deepseek-ai/dsh-persona": "
|
|
68
|
-
"@deepseek-ai/dsh-session": "
|
|
69
|
-
"@deepseek-ai/dsh-tool-bash": "
|
|
70
|
-
"@deepseek-ai/dsh-tool-fs": "
|
|
71
|
-
"@deepseek-ai/dsh-tool-fs-search": "
|
|
72
|
-
"@deepseek-ai/dsh-tools": "
|
|
73
|
-
"@deepseek-ai/dsh-util-values": "
|
|
67
|
+
"@deepseek-ai/dsh-agent": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-api-session-controller": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-conversation": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-credentials": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
71
|
+
"@deepseek-ai/dsh-persona": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-session": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-tool-bash": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-tool-fs": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-tool-fs-search": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-tools": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-util-values": "~0.1.7-rc.2 || ^0.2.0-rc.2",
|
|
74
78
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
75
79
|
},
|
|
76
80
|
"devDependencies": {
|
|
77
|
-
"@deepseek-ai/
|
|
78
|
-
"@deepseek-ai/dsh-client-
|
|
79
|
-
"@deepseek-ai/dsh-client-ui-
|
|
81
|
+
"@deepseek-ai/cordis-plugin-include": "^1.0.9",
|
|
82
|
+
"@deepseek-ai/dsh-client-store": "^0.2.0-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.2.0-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.2",
|
|
80
85
|
"@testing-library/dom": "^10.4.0",
|
|
81
86
|
"@testing-library/react": "^16.1.0",
|
|
87
|
+
"@types/js-yaml": "^4.0.9",
|
|
82
88
|
"@types/node": "^22.0.0",
|
|
83
89
|
"@types/react": "^18.3.0",
|
|
84
90
|
"@types/react-dom": "^18.3.0",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- id: persona
|
|
13
13
|
name: '@deepseek-ai/dsh-persona'
|
|
14
14
|
config:
|
|
15
|
-
|
|
15
|
+
prefix: >-
|
|
16
16
|
You are the fullstack development agent (the ONLY executor) powered by the {{model}} model.
|
|
17
17
|
Your working directory is {{cwd}}. You implement and test the aligned spec; you are
|
|
18
18
|
the sole accountable executor and may delegate implementation subtasks. Rules:
|
|
@@ -134,12 +134,14 @@
|
|
|
134
134
|
config:
|
|
135
135
|
provider: spawn
|
|
136
136
|
toolName: subagent
|
|
137
|
+
maxDepth: 3
|
|
137
138
|
|
|
138
139
|
- id: tool-subagent-fork
|
|
139
140
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
140
141
|
config:
|
|
141
142
|
provider: fork
|
|
142
143
|
toolName: subagent_fork
|
|
144
|
+
maxDepth: 3
|
|
143
145
|
|
|
144
146
|
- id: tool-subagent-control
|
|
145
147
|
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- id: persona
|
|
12
12
|
name: '@deepseek-ai/dsh-persona'
|
|
13
13
|
config:
|
|
14
|
-
|
|
14
|
+
prefix: >-
|
|
15
15
|
You are the implementation verifier + reviewer agent (read-only) powered by the {{model}}
|
|
16
16
|
model. Your working directory is {{cwd}}. You empirically verify D's implementation and
|
|
17
17
|
review it — you never modify source and you never execute writes. Rules:
|
|
@@ -95,3 +95,4 @@
|
|
|
95
95
|
config:
|
|
96
96
|
provider: spawn
|
|
97
97
|
toolName: subagent
|
|
98
|
+
maxDepth: 3
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- id: persona
|
|
16
16
|
name: '@deepseek-ai/dsh-persona'
|
|
17
17
|
config:
|
|
18
|
-
|
|
18
|
+
prefix: >-
|
|
19
19
|
You are the planning agent (planner) powered by the {{model}} model. Your working
|
|
20
20
|
directory is {{cwd}}. You produce the implementation plan only — you are NOT the
|
|
21
21
|
executor. When injected repo facts are insufficient, you may do READ-ONLY self-inspection
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- id: persona
|
|
10
10
|
name: '@deepseek-ai/dsh-persona'
|
|
11
11
|
config:
|
|
12
|
-
|
|
12
|
+
prefix: >-
|
|
13
13
|
You are the plan-review agent (read-only) powered by the {{model}} model. Your working
|
|
14
14
|
directory is {{cwd}}. You review P's plan deliverable only — you never modify any artifact
|
|
15
15
|
and you never execute code. Rules:
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- id: persona
|
|
13
13
|
name: '@deepseek-ai/dsh-persona'
|
|
14
14
|
config:
|
|
15
|
-
|
|
15
|
+
prefix: >-
|
|
16
16
|
You are the orchestration agent (butler) powered by the {{model}} model. Your working
|
|
17
17
|
directory is {{cwd}}. You only decompose, dispatch and summarize — you are NEVER the executor.
|
|
18
18
|
Rules:
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- id: persona
|
|
13
13
|
name: '@deepseek-ai/dsh-persona'
|
|
14
14
|
config:
|
|
15
|
-
|
|
15
|
+
prefix: >-
|
|
16
16
|
You are the knowledge officer (Knowledge Officer) powered by the {{model}} model. Your
|
|
17
17
|
working directory is {{cwd}}. You curate the requirement knowledge base (KB) in one of two
|
|
18
18
|
modes (the active mode and the local KB root, when local, are stated in your task context);
|