dsh-knj-workflow 2026.9.70 → 2026.9.72
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 +3 -3
- package/lib/orchestrator.js +4 -0
- package/lib/orchestrator.test.js +21 -0
- package/package.json +8 -7
package/lib/client.js
CHANGED
|
@@ -1141,7 +1141,7 @@ table.knj-tbl{width:100%;border-collapse:collapse;table-layout:fixed}
|
|
|
1141
1141
|
]),
|
|
1142
1142
|
sec('行为 prompt(task 节点)', [
|
|
1143
1143
|
'本节点让 AI 做什么,写清楚目标和约束。',
|
|
1144
|
-
'可引用:${inputDescription}(任务需求描述)、${inputTitle}(标题)、${上游编码.字段}(上游输出,如 ${analyze.description})、${inputs.参数名}(任务输入)。',
|
|
1144
|
+
'可引用:${inputDescription}(任务需求描述)、${inputTitle}(标题)、${taskId}(任务 id,流程实例唯一标识;同任务续跑/重跑 id 不变,可拼产出路径隔离产物)、${上游编码.字段}(上游输出,如 ${analyze.description})、${inputs.参数名}(任务输入)。',
|
|
1145
1145
|
'执行方式:single 一个 AI 完成;parallel 派多个 AI 并行(结果合并为数组)。',
|
|
1146
1146
|
'最大执行次数:本节点在一轮任务里最多执行几次(默认 3)。驳回重做 / 循环连线会重复执行节点,超过上限本轮失败——自动防死循环,无需其他配置。',
|
|
1147
1147
|
]),
|
|
@@ -1195,7 +1195,7 @@ table.knj-tbl{width:100%;border-collapse:collapse;table-layout:fixed}
|
|
|
1195
1195
|
]),
|
|
1196
1196
|
sec('行为 prompt(task 节点)', [
|
|
1197
1197
|
'本节点让 AI 做什么,写清目标与约束。',
|
|
1198
|
-
'可引用:${inputDescription}(任务需求描述)、${inputTitle}(标题)、${storyCode}(用户故事编码)、${cwd}(任务工作目录)、${inputs.参数名}(任务输入)。',
|
|
1198
|
+
'可引用:${inputDescription}(任务需求描述)、${inputTitle}(标题)、${storyCode}(用户故事编码)、${cwd}(任务工作目录)、${taskId}(任务 id,同任务续跑/重跑 id 不变,产出路径用它拼接可隔离各实例产物)、${inputs.参数名}(任务输入)。',
|
|
1199
1199
|
'运行时每个 task 节点会自动声明任务工作目录(所有文件/命令操作都在其中)。',
|
|
1200
1200
|
'需要 skill 时在 prompt 里直接写「先调用 skill 工具加载 skill「xxx」」。',
|
|
1201
1201
|
'上游输出引用(${上游编码.字段}):',
|
|
@@ -1702,7 +1702,7 @@ table.knj-tbl{width:100%;border-collapse:collapse;table-layout:fixed}
|
|
|
1702
1702
|
fieldLabel('节点编码(id)', '节点唯一标识:下游用 ${编码.字段} 引用(如 ${analyze.description}),网关用字段值做分支判断。修改后 prompt 里旧的 ${旧编码.字段} 引用需手动更新。', '下游用 ${编码.字段} 引用,如 ${analyze.description}'),
|
|
1703
1703
|
react.createElement('input', { className: 'knj-input', style: { marginBottom: 8 }, placeholder: '如:analyze', defaultValue: node.id || '', onBlur: (e) => this.patchNodeId(node.id, e.target.value.trim()) }),
|
|
1704
1704
|
isTask ? react.createElement('div', null,
|
|
1705
|
-
fieldLabel('行为 prompt(主体)', '本节点让 AI 做什么,写清目标与约束。\n可引用:\n${inputDescription}(任务需求描述)\n${inputTitle}(标题)\n${storyCode}(用户故事编码,可选)\n${cwd}(任务工作目录;运行时还会自动向 subagent 声明该目录)\n${inputs.参数名}(任务输入)\n\n上游输出引用(${上游编码.字段}):\n' + this.buildRefsText(node) + '\n\n注意:XOR 分支未执行的节点引用为空;整节点输出用 ${节点id};${ctx.前缀写法仍兼容。', '让 AI 做什么;可引用 ${inputDescription}、${storyCode}、${cwd}、${上游编码.字段}(ⓘ 看完整清单)'),
|
|
1705
|
+
fieldLabel('行为 prompt(主体)', '本节点让 AI 做什么,写清目标与约束。\n可引用:\n${inputDescription}(任务需求描述)\n${inputTitle}(标题)\n${storyCode}(用户故事编码,可选)\n${cwd}(任务工作目录;运行时还会自动向 subagent 声明该目录)\n${taskId}(任务 id,流程实例唯一标识;同任务续跑/重跑 id 不变——产出路径可写 ${cwd}/out/${taskId}/ 按实例隔离产物)\n${inputs.参数名}(任务输入)\n\n上游输出引用(${上游编码.字段}):\n' + this.buildRefsText(node) + '\n\n注意:XOR 分支未执行的节点引用为空;整节点输出用 ${节点id};${ctx.前缀写法仍兼容。', '让 AI 做什么;可引用 ${inputDescription}、${storyCode}、${cwd}、${上游编码.字段}(ⓘ 看完整清单)'),
|
|
1706
1706
|
react.createElement('textarea', { className: 'knj-textarea', style: { minHeight: 140 }, placeholder: '如:根据需求 ${inputDescription} 输出功能描述与验收标准', value: node.body?.prompt || '', onChange: (e) => this.patchNode(node.id, { body: { ...node.body, prompt: e.target.value } }) }),
|
|
1707
1707
|
fieldLabel('执行方式', 'single:一个 AI 完成本阶段,输出单个对象。\nparallel:派多个 AI 并行做同一件事,结果合并为数组(配合「最大执行次数」外的 parallelItems 使用,见输出说明)。', 'single:单个 AI 完成;parallel:多个 AI 并行,结果合并为数组'),
|
|
1708
1708
|
react.createElement('select', { className: 'knj-input', value: node.body?.mode || 'single', onChange: (e) => this.patchNode(node.id, { body: { ...node.body, mode: e.target.value } }) },
|
package/lib/orchestrator.js
CHANGED
|
@@ -182,7 +182,11 @@ const BUILTIN_ACTIONS = {
|
|
|
182
182
|
function getCtx() {
|
|
183
183
|
// 任务级变量:inputDescription(新建任务需求描述)、inputTitle(标题)、storyCode(用户故事编码)、
|
|
184
184
|
// cwd(工作目录)、inputs(参数映射)并列,工作流任意 prompt/hook 参数可引用。
|
|
185
|
+
// taskId = task.id(流程实例唯一标识;同任务 resume/rerun 不变)——节点产出文件路径可用它拼接,
|
|
186
|
+
// 让不同任务实例的产物互相隔离(等价写法 ${task.id})。
|
|
185
187
|
return {
|
|
188
|
+
task: { id: task.id },
|
|
189
|
+
taskId: task.id,
|
|
186
190
|
inputs: args.inputs || {},
|
|
187
191
|
inputDescription: task.description || '',
|
|
188
192
|
inputTitle: task.title || '',
|
package/lib/orchestrator.test.js
CHANGED
|
@@ -694,6 +694,27 @@ test('orchestrator: 用户故事编码 storyCode 可引用(${storyCode})', a
|
|
|
694
694
|
assert.ok(!capturedPrompt.includes('${storyCode}'), capturedPrompt);
|
|
695
695
|
});
|
|
696
696
|
|
|
697
|
+
test('orchestrator: task.id 可引用(${taskId} 与 ${task.id}),可拼产出路径隔离实例产物', async () => {
|
|
698
|
+
const wf = {
|
|
699
|
+
id: 'wf-taskid', nodes: [
|
|
700
|
+
{ id: 'start', type: 'start' },
|
|
701
|
+
{ id: 'a', type: 'task', title: 'A', inputs: [], body: { prompt: '产物目录:${cwd}/out/${taskId}/,别名:${task.id}', mode: 'single', output: {} } },
|
|
702
|
+
{ id: 'end', type: 'end' },
|
|
703
|
+
],
|
|
704
|
+
edges: [{ from: 'start', to: 'a' }, { from: 'a', to: 'end' }],
|
|
705
|
+
};
|
|
706
|
+
let capturedPrompt = null;
|
|
707
|
+
const { result } = await runOrchestrator(wf, (label, prompt) => {
|
|
708
|
+
if (label === 'A') capturedPrompt = prompt;
|
|
709
|
+
return { ok: true };
|
|
710
|
+
}, { task: { id: 'task-mt1uc64i', title: 'T', taskDir: '.tasks/task-mt1uc64i', cwd: 'D:/ws/proj' } });
|
|
711
|
+
// 两种写法都解析为 task.id,拼接出的产出路径按实例隔离
|
|
712
|
+
assert.ok(capturedPrompt.includes('产物目录:D:/ws/proj/out/task-mt1uc64i/'), capturedPrompt);
|
|
713
|
+
assert.ok(capturedPrompt.includes('别名:task-mt1uc64i'), capturedPrompt);
|
|
714
|
+
assert.ok(!capturedPrompt.includes('${task'), capturedPrompt);
|
|
715
|
+
assert.equal(result.current, 'end');
|
|
716
|
+
});
|
|
717
|
+
|
|
697
718
|
test('orchestrator: human reject 带 feedback → rejectTo 节点 prompt 含人工意见', async () => {
|
|
698
719
|
let capturedPrompt = null;
|
|
699
720
|
const { result, agentCalls } = await runOrchestrator(humanWorkflow(), (label, prompt) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-knj-workflow",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.72",
|
|
4
4
|
"description": "KNJ 开发任务编排插件:配置驱动的工作流 + 开发任务管理 + 可视化阶段进度(DeepSeek Harness)",
|
|
5
5
|
"author": "knj",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,12 +37,13 @@
|
|
|
37
37
|
],
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
40
|
-
"@deepseek-ai/dsh-agent": "0.1.2-rc.1",
|
|
41
|
-
"@deepseek-ai/dsh-commands": "0.1.2-rc.1",
|
|
42
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
|
|
43
|
-
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
44
|
-
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
|
45
|
-
"@deepseek-ai/dsh-workflow": "0.1.2-rc.1"
|
|
40
|
+
"@deepseek-ai/dsh-agent": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-rc.1 <0.1.3-0",
|
|
41
|
+
"@deepseek-ai/dsh-commands": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-rc.1 <0.1.3-0",
|
|
42
|
+
"@deepseek-ai/dsh-host-webserver": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-rc.1 <0.1.3-0",
|
|
43
|
+
"@deepseek-ai/dsh-session": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-rc.1 <0.1.3-0",
|
|
44
|
+
"@deepseek-ai/dsh-tools": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-rc.1 <0.1.3-0",
|
|
45
|
+
"@deepseek-ai/dsh-workflow": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-rc.1 <0.1.3-0",
|
|
46
|
+
"@deepseek-ai/schemastery": ">=3.18.0 <4.0.0"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@deepseek-ai/cordis": "4.0.1",
|