@fieldwangai/agentflow 0.1.157 → 0.1.160
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/bin/lib/catalog-flows.mjs +10 -1
- package/bin/lib/control-while.mjs +336 -0
- package/bin/lib/flow-dsl/codegen.mjs +75 -12
- package/bin/lib/flow-dsl/ir.mjs +33 -2
- package/bin/lib/flow-dsl/lint.mjs +128 -3
- package/bin/lib/flow-dsl/parser.mjs +168 -4
- package/bin/lib/locales/en.json +4 -0
- package/bin/lib/locales/zh.json +4 -0
- package/bin/lib/marketplace.mjs +10 -2
- package/bin/lib/mcp-server.mjs +1 -0
- package/bin/lib/node-package-manifest.mjs +7 -2
- package/bin/lib/node-ui-kit.mjs +156 -0
- package/bin/lib/paths.mjs +2 -0
- package/bin/lib/workspace-flow-store.mjs +11 -2
- package/bin/lib/workspace-graph-merge.mjs +3 -0
- package/bin/lib/workspace-routes.mjs +10 -1
- package/bin/lib/workspace-server.mjs +601 -15
- package/bin/pipeline/validate-flow.mjs +3 -3
- package/builtin/nodes/control_parse_json.md +25 -0
- package/builtin/nodes/control_subflow_call.md +27 -0
- package/builtin/nodes/control_while.md +124 -0
- package/builtin/nodes/display_code.md +39 -0
- package/builtin/nodes/provide_json.md +14 -0
- package/builtin/nodes/workspace_subflow_input.md +14 -0
- package/builtin/pipelines/subflow-preview/workspace.flow.js +29 -0
- package/builtin/pipelines/subflow-preview/workspace.layout.json +38 -0
- package/builtin/pipelines/subflow-preview/workspace.nodes.json +19 -0
- package/builtin/pipelines/subflow-preview/workspace.state.json +93 -0
- package/builtin/pipelines/while-subflow-preview/workspace.flow.js +55 -0
- package/builtin/pipelines/while-subflow-preview/workspace.layout.json +66 -0
- package/builtin/pipelines/while-subflow-preview/workspace.nodes.json +49 -0
- package/builtin/pipelines/while-subflow-preview/workspace.state.json +194 -0
- package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-g_ljSViJ.js → WorkflowAssistantThread-Bfo9Ythw.js} +1 -1
- package/builtin/web-ui/dist/assets/index-XbeI5foV.js +872 -0
- package/builtin/web-ui/dist/assets/index-e1omCEau.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +2 -1
- package/reference/flow-control-capabilities.md +41 -16
- package/shared/slot-types.js +58 -0
- package/skills/agentflow-flow-dsl/SKILL.md +61 -7
- package/skills/agentflow-flow-dsl/references/node-calls.md +4 -0
- package/skills/agentflow-flow-dsl/references/subflow-authoring.md +230 -0
- package/skills/agentflow-node-dsl/SKILL.md +39 -0
- package/skills/agentflow-node-reference/references/builtin-nodes.md +33 -0
- package/builtin/web-ui/dist/assets/index-C0iq6zHl.js +0 -870
- package/builtin/web-ui/dist/assets/index-mmXs3H9P.css +0 -1
|
@@ -501,10 +501,10 @@ function checkFlowCore(nodes, edges, flowDir, nodeIdToSlots, getNodeBody, instan
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
-
// provide_str / provide_file / provide_bool output 类型校验
|
|
505
|
-
if (defId === "provide_str" || defId === "provide_file" || defId === "provide_bool") {
|
|
504
|
+
// provide_str / provide_json / provide_file / provide_bool output 类型校验
|
|
505
|
+
if (defId === "provide_str" || defId === "provide_json" || defId === "provide_file" || defId === "provide_bool") {
|
|
506
506
|
const out = Array.isArray(inst.output) ? inst.output : [];
|
|
507
|
-
const expectedType = defId === "provide_file" ? "file" : defId === "provide_bool" ? "bool" : "text";
|
|
507
|
+
const expectedType = defId === "provide_file" ? "file" : defId === "provide_bool" ? "bool" : defId === "provide_json" ? "json" : "text";
|
|
508
508
|
if (out.length !== 1) {
|
|
509
509
|
errors.push(`节点 "${n.id}"(${defId})output 必须仅有 1 个槽位(value:${expectedType}),当前 ${out.length} 个`);
|
|
510
510
|
} else {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:解析 JSON
|
|
3
|
+
runtime: native
|
|
4
|
+
type: control
|
|
5
|
+
description: 显式解析并校验文本 JSON,成功后输出 json 类型;解析失败会终止本次运行。
|
|
6
|
+
displayName: Parse JSON
|
|
7
|
+
input:
|
|
8
|
+
- type: node
|
|
9
|
+
name: prev
|
|
10
|
+
default: ""
|
|
11
|
+
- type: text
|
|
12
|
+
name: value
|
|
13
|
+
default: ""
|
|
14
|
+
required: true
|
|
15
|
+
showOnNode: true
|
|
16
|
+
output:
|
|
17
|
+
- type: node
|
|
18
|
+
name: next
|
|
19
|
+
default: ""
|
|
20
|
+
- type: json
|
|
21
|
+
name: result
|
|
22
|
+
default: "null"
|
|
23
|
+
showOnNode: true
|
|
24
|
+
---
|
|
25
|
+
Parse a text value as JSON and expose the validated result through a typed output.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:调用子流程
|
|
3
|
+
runtime: native
|
|
4
|
+
type: control
|
|
5
|
+
palette: hidden
|
|
6
|
+
description: |
|
|
7
|
+
调用 workspace.flow.js 中由 flow.subflow 声明的同 Workspace 子流程。
|
|
8
|
+
输入和输出槽来自子流程契约;每次调用使用独立调用帧,内部节点仍按标准 AgentFlow DSL 和运行时执行。
|
|
9
|
+
displayName: Call Subflow
|
|
10
|
+
ui:
|
|
11
|
+
card:
|
|
12
|
+
template: details
|
|
13
|
+
icon: account_tree
|
|
14
|
+
tone: purple
|
|
15
|
+
sections:
|
|
16
|
+
- type: subflow
|
|
17
|
+
label: Called subflow
|
|
18
|
+
input:
|
|
19
|
+
- type: node
|
|
20
|
+
name: prev
|
|
21
|
+
default: ""
|
|
22
|
+
output:
|
|
23
|
+
- type: node
|
|
24
|
+
name: next
|
|
25
|
+
default: ""
|
|
26
|
+
---
|
|
27
|
+
Execute the referenced inline subflow using its declared input and output contract.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:While 单步推进
|
|
3
|
+
runtime: native
|
|
4
|
+
description: |
|
|
5
|
+
Repeatedly execute either an explicit Condition/Body subflow pair or one legacy deterministic
|
|
6
|
+
step command (`script` or `scriptRef`) without adding a cycle to the Workspace graph. A Run
|
|
7
|
+
restarted after `wait` resumes from the saved output state.
|
|
8
|
+
|
|
9
|
+
Preferred DSL form:
|
|
10
|
+
`control.while("Advance", { state, maxIterations, timeout }, conditionFlow, bodyFlow)`.
|
|
11
|
+
Condition must accept `state` and `iteration`, and return `decision` plus optional `summary`.
|
|
12
|
+
Only `continue` invokes Body. Body must accept `state`, `iteration`, and `idempotencyKey`, and
|
|
13
|
+
return the next `state` plus optional `summary`. `wait`, `done`, and `fail` skip Body.
|
|
14
|
+
|
|
15
|
+
In legacy script mode, the command runs once per iteration and stdout must be exactly one JSON object:
|
|
16
|
+
`{"decision":"continue|wait|done|fail","state":{},"summary":"..."}`.
|
|
17
|
+
`continue` starts another iteration, `wait` pauses this Run before downstream nodes, `done`
|
|
18
|
+
continues downstream, and `fail` fails the node. A missing `state` keeps the previous state.
|
|
19
|
+
|
|
20
|
+
Each step receives `AGENTFLOW_WHILE_STATE` (JSON), the absolute
|
|
21
|
+
`AGENTFLOW_WHILE_ITERATION`, `AGENTFLOW_WHILE_MAX_ITERATIONS`,
|
|
22
|
+
`AGENTFLOW_WHILE_TIMEOUT_MS`, and a stable per-iteration
|
|
23
|
+
`AGENTFLOW_WHILE_IDEMPOTENCY_KEY`. Pass the idempotency key to external write APIs when they
|
|
24
|
+
support one. Write progress logs to stderr because stdout is reserved for the decision object.
|
|
25
|
+
The command also supports the same runtime placeholders as `tool.nodejs`, including
|
|
26
|
+
`${flowDir}` and `${workspaceRoot}`.
|
|
27
|
+
|
|
28
|
+
A waiting checkpoint retains state, history, elapsed active time, and the next absolute
|
|
29
|
+
iteration. `maxIterations` and `timeout` are cumulative across resumes. A changed input resets
|
|
30
|
+
the checkpoint; a matching but malformed checkpoint fails closed. Step output is schema-strict
|
|
31
|
+
and bounded: unknown fields are rejected, state/stdout are limited to 1 MiB, stderr to 256 KiB,
|
|
32
|
+
and summary to 4000 characters.
|
|
33
|
+
displayName: While
|
|
34
|
+
ui:
|
|
35
|
+
card:
|
|
36
|
+
template: state-machine
|
|
37
|
+
icon: repeat
|
|
38
|
+
tone: purple
|
|
39
|
+
sections:
|
|
40
|
+
- type: binding
|
|
41
|
+
label: State input
|
|
42
|
+
input: state
|
|
43
|
+
- type: loop
|
|
44
|
+
label: Loop execution
|
|
45
|
+
field: script
|
|
46
|
+
- type: decision
|
|
47
|
+
label: Decision from step
|
|
48
|
+
output: decision
|
|
49
|
+
source: step.stdout.decision
|
|
50
|
+
options:
|
|
51
|
+
- value: continue
|
|
52
|
+
label: continue ↻
|
|
53
|
+
tone: purple
|
|
54
|
+
description: 保存 state 并立即进入下一轮
|
|
55
|
+
- value: wait
|
|
56
|
+
label: wait ⏸
|
|
57
|
+
tone: amber
|
|
58
|
+
description: 保存 checkpoint,暂停当前 Run
|
|
59
|
+
- value: done
|
|
60
|
+
label: done →
|
|
61
|
+
tone: green
|
|
62
|
+
description: 结束循环并继续下游
|
|
63
|
+
- value: fail
|
|
64
|
+
label: fail ×
|
|
65
|
+
tone: red
|
|
66
|
+
description: 终止并标记节点失败
|
|
67
|
+
- type: metrics
|
|
68
|
+
label: Limits and progress
|
|
69
|
+
items:
|
|
70
|
+
- label: Iteration
|
|
71
|
+
output: iterations
|
|
72
|
+
maxInput: maxIterations
|
|
73
|
+
- label: Timeout
|
|
74
|
+
input: timeout
|
|
75
|
+
- type: summary
|
|
76
|
+
label: Latest outcome
|
|
77
|
+
output: summary
|
|
78
|
+
- type: history
|
|
79
|
+
label: Iteration history
|
|
80
|
+
output: history
|
|
81
|
+
limit: 3
|
|
82
|
+
input:
|
|
83
|
+
- type: node
|
|
84
|
+
name: prev
|
|
85
|
+
default: ""
|
|
86
|
+
- type: json
|
|
87
|
+
name: state
|
|
88
|
+
default: "null"
|
|
89
|
+
- type: text
|
|
90
|
+
name: maxIterations
|
|
91
|
+
default: "20"
|
|
92
|
+
- type: text
|
|
93
|
+
name: timeout
|
|
94
|
+
default: "30m"
|
|
95
|
+
output:
|
|
96
|
+
- type: node
|
|
97
|
+
name: next
|
|
98
|
+
default: ""
|
|
99
|
+
- type: json
|
|
100
|
+
name: result
|
|
101
|
+
default: ""
|
|
102
|
+
- type: json
|
|
103
|
+
name: state
|
|
104
|
+
default: "null"
|
|
105
|
+
- type: text
|
|
106
|
+
name: decision
|
|
107
|
+
default: ""
|
|
108
|
+
showOnNode: true
|
|
109
|
+
- type: text
|
|
110
|
+
name: iterations
|
|
111
|
+
default: "0"
|
|
112
|
+
showOnNode: true
|
|
113
|
+
- type: text
|
|
114
|
+
name: summary
|
|
115
|
+
default: ""
|
|
116
|
+
showOnNode: true
|
|
117
|
+
- type: json
|
|
118
|
+
name: history
|
|
119
|
+
default: "[]"
|
|
120
|
+
- type: text
|
|
121
|
+
name: checkpointFingerprint
|
|
122
|
+
default: ""
|
|
123
|
+
---
|
|
124
|
+
${USER_PROMPT}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:代码展示
|
|
3
|
+
runtime: native
|
|
4
|
+
description: Display source code with language highlighting, line numbers, copy, wrap, and download controls; passes content downstream as text
|
|
5
|
+
displayName: Code Display
|
|
6
|
+
input:
|
|
7
|
+
- type: node
|
|
8
|
+
name: prev
|
|
9
|
+
default: ""
|
|
10
|
+
- type: text
|
|
11
|
+
name: content
|
|
12
|
+
default: ""
|
|
13
|
+
required: true
|
|
14
|
+
showOnNode: true
|
|
15
|
+
- type: text
|
|
16
|
+
name: language
|
|
17
|
+
default: ""
|
|
18
|
+
description: Syntax language such as javascript, typescript, python, kotlin, java, shell, json, yaml, html, css, or sql
|
|
19
|
+
showOnNode: false
|
|
20
|
+
- type: text
|
|
21
|
+
name: fileName
|
|
22
|
+
default: ""
|
|
23
|
+
description: Optional file name used when downloading
|
|
24
|
+
showOnNode: false
|
|
25
|
+
- type: bool
|
|
26
|
+
name: wrap
|
|
27
|
+
default: "false"
|
|
28
|
+
description: Wrap long source lines by default
|
|
29
|
+
showOnNode: false
|
|
30
|
+
output:
|
|
31
|
+
- type: text
|
|
32
|
+
name: content
|
|
33
|
+
default: ""
|
|
34
|
+
showOnNode: true
|
|
35
|
+
- type: node
|
|
36
|
+
name: next
|
|
37
|
+
default: ""
|
|
38
|
+
---
|
|
39
|
+
${content}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:子流程输入
|
|
3
|
+
runtime: native
|
|
4
|
+
type: control
|
|
5
|
+
palette: hidden
|
|
6
|
+
description: 子流程调用帧的输入代理。只允许通过 flow.input 创建,不在节点面板中展示。
|
|
7
|
+
displayName: Subflow Input
|
|
8
|
+
input: []
|
|
9
|
+
output:
|
|
10
|
+
- type: text
|
|
11
|
+
name: value
|
|
12
|
+
default: ""
|
|
13
|
+
---
|
|
14
|
+
Expose one value injected by a flow.call invocation.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { agent, control, display, file, flow, provide, tool, workspace } from "agentflow/flow";
|
|
2
|
+
|
|
3
|
+
const issueIn = flow.input("issue", "text");
|
|
4
|
+
|
|
5
|
+
const inspect = agent.subAgent("分析单个 Issue", {
|
|
6
|
+
issue: issueIn.value,
|
|
7
|
+
}, "只分析本次输入的一个 Issue,给出目标、风险和下一步。不要处理其它 Issue。");
|
|
8
|
+
|
|
9
|
+
const normalize = tool.nodejs("规范化交付摘要", {
|
|
10
|
+
analysis: inspect.result,
|
|
11
|
+
}, `node -e 'process.stdout.write(process.argv[1])' \${analysis}`);
|
|
12
|
+
|
|
13
|
+
export const inspectIssue = flow.subflow("Issue 单项分析", { issue: issueIn }, flow(inspect, normalize), { summary: normalize.result, raw: inspect.result });
|
|
14
|
+
|
|
15
|
+
export const request = provide.str("待分析 Issue", {
|
|
16
|
+
value: "LIKEE-1842 · Android 评论气泡样式需要统一",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const callInspect = flow.call("调用 Issue 分析子流程", inspectIssue, {
|
|
20
|
+
issue: request.value,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const { summary, raw } = callInspect;
|
|
24
|
+
|
|
25
|
+
const preview = display.markdown("父流程收到的结果", {
|
|
26
|
+
content: summary,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const run = flow("运行案例", callInspect, preview);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"nodes": {
|
|
4
|
+
"callInspect": {
|
|
5
|
+
"x": 460,
|
|
6
|
+
"y": 80,
|
|
7
|
+
"w": 410,
|
|
8
|
+
"h": 330
|
|
9
|
+
},
|
|
10
|
+
"inspect": {
|
|
11
|
+
"x": 500,
|
|
12
|
+
"y": 700
|
|
13
|
+
},
|
|
14
|
+
"issueIn": {
|
|
15
|
+
"x": 100,
|
|
16
|
+
"y": 700
|
|
17
|
+
},
|
|
18
|
+
"normalize": {
|
|
19
|
+
"x": 900,
|
|
20
|
+
"y": 700
|
|
21
|
+
},
|
|
22
|
+
"preview": {
|
|
23
|
+
"x": 940,
|
|
24
|
+
"y": 80,
|
|
25
|
+
"w": 560,
|
|
26
|
+
"h": 420
|
|
27
|
+
},
|
|
28
|
+
"request": {
|
|
29
|
+
"x": 80,
|
|
30
|
+
"y": 80
|
|
31
|
+
},
|
|
32
|
+
"run": {
|
|
33
|
+
"x": 80,
|
|
34
|
+
"y": 300
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"description": "子流程案例:父流程通过 flow.call 调用带输入/输出契约的标准节点子图"
|
|
38
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"nodes": {
|
|
4
|
+
"callInspect": {
|
|
5
|
+
"subflowId": "inspectIssue"
|
|
6
|
+
},
|
|
7
|
+
"inspect": {
|
|
8
|
+
"subflowId": "inspectIssue"
|
|
9
|
+
},
|
|
10
|
+
"issueIn": {
|
|
11
|
+
"subflowId": "inspectIssue",
|
|
12
|
+
"subflowInputName": "issue",
|
|
13
|
+
"subflowInputType": "text"
|
|
14
|
+
},
|
|
15
|
+
"normalize": {
|
|
16
|
+
"subflowId": "inspectIssue"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"inputs": {
|
|
4
|
+
"inspect": {
|
|
5
|
+
"workspaceContext": {
|
|
6
|
+
"value": ""
|
|
7
|
+
},
|
|
8
|
+
"skillsContext": {
|
|
9
|
+
"value": ""
|
|
10
|
+
},
|
|
11
|
+
"mcpContext": {
|
|
12
|
+
"value": ""
|
|
13
|
+
},
|
|
14
|
+
"knowledgeContext": {
|
|
15
|
+
"value": ""
|
|
16
|
+
},
|
|
17
|
+
"issue": {
|
|
18
|
+
"value": ""
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"normalize": {
|
|
22
|
+
"workspaceContext": {
|
|
23
|
+
"value": ""
|
|
24
|
+
},
|
|
25
|
+
"skillsContext": {
|
|
26
|
+
"value": ""
|
|
27
|
+
},
|
|
28
|
+
"mcpContext": {
|
|
29
|
+
"value": ""
|
|
30
|
+
},
|
|
31
|
+
"analysis": {
|
|
32
|
+
"value": ""
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"callInspect": {
|
|
36
|
+
"issue": {
|
|
37
|
+
"value": ""
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"preview": {
|
|
41
|
+
"content": {
|
|
42
|
+
"value": ""
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"outputs": {
|
|
47
|
+
"issueIn": {
|
|
48
|
+
"value": {
|
|
49
|
+
"value": ""
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"inspect": {
|
|
53
|
+
"next": {
|
|
54
|
+
"value": ""
|
|
55
|
+
},
|
|
56
|
+
"result": {
|
|
57
|
+
"value": ""
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"normalize": {
|
|
61
|
+
"next": {
|
|
62
|
+
"value": ""
|
|
63
|
+
},
|
|
64
|
+
"result": {
|
|
65
|
+
"value": ""
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"callInspect": {
|
|
69
|
+
"next": {
|
|
70
|
+
"value": ""
|
|
71
|
+
},
|
|
72
|
+
"summary": {
|
|
73
|
+
"value": ""
|
|
74
|
+
},
|
|
75
|
+
"raw": {
|
|
76
|
+
"value": ""
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"preview": {
|
|
80
|
+
"content": {
|
|
81
|
+
"value": ""
|
|
82
|
+
},
|
|
83
|
+
"next": {
|
|
84
|
+
"value": ""
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"run": {
|
|
88
|
+
"next": {
|
|
89
|
+
"value": ""
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { agent, control, display, file, flow, provide, tool, workspace } from "agentflow/flow";
|
|
2
|
+
|
|
3
|
+
const bodyState = flow.input("state", "json");
|
|
4
|
+
|
|
5
|
+
const bodyIteration = flow.input("iteration", "text");
|
|
6
|
+
|
|
7
|
+
const bodyIdempotencyKey = flow.input("idempotencyKey", "text");
|
|
8
|
+
|
|
9
|
+
const selectIssue = tool.nodejs("选择本轮 Issue", {
|
|
10
|
+
state: bodyState.value,
|
|
11
|
+
iteration: bodyIteration.value,
|
|
12
|
+
}, `node -e 'const i=JSON.parse(process.env.AGENTFLOW_INPUTS_JSON||"{}");const s=JSON.parse(i.state||"{}");const issues=["LIKEE-1842 · Android 评论气泡样式","LIKEE-1843 · iOS 评论气泡样式"];process.stdout.write(issues[s.cursor]||"无待处理 Issue")'`);
|
|
13
|
+
|
|
14
|
+
const advanceIssue = tool.nodejs("推进一个研发动作", {
|
|
15
|
+
issue: selectIssue.result,
|
|
16
|
+
idempotencyKey: bodyIdempotencyKey.value,
|
|
17
|
+
}, `node -e 'const i=JSON.parse(process.env.AGENTFLOW_INPUTS_JSON||"{}");process.stdout.write("已模拟推进:"+i.issue+"(幂等键 "+String(i.idempotencyKey||"").slice(0,12)+"…)")'`);
|
|
18
|
+
|
|
19
|
+
const saveState = tool.nodejs("归并下一轮状态", {
|
|
20
|
+
previousState: bodyState.value,
|
|
21
|
+
action: advanceIssue.result,
|
|
22
|
+
}, `node -e 'const i=JSON.parse(process.env.AGENTFLOW_INPUTS_JSON||"{}");const s=JSON.parse(i.previousState||"{}");s.cursor=Number(s.cursor||0)+1;s.status="advanced";s.lastAction=i.action;process.stdout.write(JSON.stringify(s))'`);
|
|
23
|
+
|
|
24
|
+
const validateNextState = control.parseJson("验证下一轮状态", {
|
|
25
|
+
value: saveState.result,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const bodyFlow = flow.subflow("执行一轮推进", { state: bodyState, iteration: bodyIteration, idempotencyKey: bodyIdempotencyKey }, flow(selectIssue, advanceIssue, saveState, validateNextState), { state: validateNextState.result, summary: advanceIssue.result });
|
|
29
|
+
|
|
30
|
+
const conditionState = flow.input("state", "json");
|
|
31
|
+
|
|
32
|
+
const conditionIteration = flow.input("iteration", "text");
|
|
33
|
+
|
|
34
|
+
const checkBoundary = tool.nodejs("检查人工边界", {
|
|
35
|
+
state: conditionState.value,
|
|
36
|
+
iteration: conditionIteration.value,
|
|
37
|
+
}, `node -e 'const i=JSON.parse(process.env.AGENTFLOW_INPUTS_JSON||"{}");const s=JSON.parse(i.state||"{}");process.stdout.write(Number(s.cursor||0)>=2?"done":"continue")'`);
|
|
38
|
+
|
|
39
|
+
export const conditionFlow = flow.subflow("是否继续推进", { state: conditionState, iteration: conditionIteration }, flow(checkBoundary), { decision: checkBoundary.result, summary: checkBoundary.result });
|
|
40
|
+
|
|
41
|
+
export const initialState = provide.json("初始 PRD 状态", {
|
|
42
|
+
value: `{"prdId":"TAPD-1010475","cursor":0,"status":"ready"}`,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const advanceUntilBoundary = control.while("推进到下一个人工边界 · 设计预览", {
|
|
46
|
+
state: initialState.value,
|
|
47
|
+
maxIterations: "20",
|
|
48
|
+
timeout: "30m",
|
|
49
|
+
}, conditionFlow, bodyFlow);
|
|
50
|
+
|
|
51
|
+
const result = display.markdown("While 最终结果", {
|
|
52
|
+
content: advanceUntilBoundary.result,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const run = flow("运行设计预览", advanceUntilBoundary, result);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"nodes": {
|
|
4
|
+
"advanceIssue": {
|
|
5
|
+
"x": 1590,
|
|
6
|
+
"y": 820
|
|
7
|
+
},
|
|
8
|
+
"advanceUntilBoundary": {
|
|
9
|
+
"x": 520,
|
|
10
|
+
"y": 70,
|
|
11
|
+
"w": 560,
|
|
12
|
+
"h": 520
|
|
13
|
+
},
|
|
14
|
+
"bodyIdempotencyKey": {
|
|
15
|
+
"x": 950,
|
|
16
|
+
"y": 1060
|
|
17
|
+
},
|
|
18
|
+
"bodyIteration": {
|
|
19
|
+
"x": 950,
|
|
20
|
+
"y": 940
|
|
21
|
+
},
|
|
22
|
+
"bodyState": {
|
|
23
|
+
"x": 950,
|
|
24
|
+
"y": 820
|
|
25
|
+
},
|
|
26
|
+
"checkBoundary": {
|
|
27
|
+
"x": 430,
|
|
28
|
+
"y": 820
|
|
29
|
+
},
|
|
30
|
+
"conditionIteration": {
|
|
31
|
+
"x": 60,
|
|
32
|
+
"y": 940
|
|
33
|
+
},
|
|
34
|
+
"conditionState": {
|
|
35
|
+
"x": 60,
|
|
36
|
+
"y": 820
|
|
37
|
+
},
|
|
38
|
+
"initialState": {
|
|
39
|
+
"x": 80,
|
|
40
|
+
"y": 80
|
|
41
|
+
},
|
|
42
|
+
"result": {
|
|
43
|
+
"x": 1180,
|
|
44
|
+
"y": 80,
|
|
45
|
+
"w": 520,
|
|
46
|
+
"h": 360
|
|
47
|
+
},
|
|
48
|
+
"run": {
|
|
49
|
+
"x": 80,
|
|
50
|
+
"y": 300
|
|
51
|
+
},
|
|
52
|
+
"saveState": {
|
|
53
|
+
"x": 1920,
|
|
54
|
+
"y": 820
|
|
55
|
+
},
|
|
56
|
+
"selectIssue": {
|
|
57
|
+
"x": 1260,
|
|
58
|
+
"y": 820
|
|
59
|
+
},
|
|
60
|
+
"validateNextState": {
|
|
61
|
+
"x": 2360,
|
|
62
|
+
"y": 1372
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"description": "While 双子流程设计预览:Condition 负责决策,Body 负责单轮推进,图本身保持 DAG"
|
|
66
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"nodes": {
|
|
4
|
+
"advanceIssue": {
|
|
5
|
+
"subflowId": "bodyFlow"
|
|
6
|
+
},
|
|
7
|
+
"advanceUntilBoundary": {
|
|
8
|
+
"conditionSubflowId": "conditionFlow",
|
|
9
|
+
"bodySubflowId": "bodyFlow"
|
|
10
|
+
},
|
|
11
|
+
"bodyIdempotencyKey": {
|
|
12
|
+
"subflowId": "bodyFlow",
|
|
13
|
+
"subflowInputName": "idempotencyKey",
|
|
14
|
+
"subflowInputType": "text"
|
|
15
|
+
},
|
|
16
|
+
"bodyIteration": {
|
|
17
|
+
"subflowId": "bodyFlow",
|
|
18
|
+
"subflowInputName": "iteration",
|
|
19
|
+
"subflowInputType": "text"
|
|
20
|
+
},
|
|
21
|
+
"bodyState": {
|
|
22
|
+
"subflowId": "bodyFlow",
|
|
23
|
+
"subflowInputName": "state",
|
|
24
|
+
"subflowInputType": "json"
|
|
25
|
+
},
|
|
26
|
+
"checkBoundary": {
|
|
27
|
+
"subflowId": "conditionFlow"
|
|
28
|
+
},
|
|
29
|
+
"conditionIteration": {
|
|
30
|
+
"subflowId": "conditionFlow",
|
|
31
|
+
"subflowInputName": "iteration",
|
|
32
|
+
"subflowInputType": "text"
|
|
33
|
+
},
|
|
34
|
+
"conditionState": {
|
|
35
|
+
"subflowId": "conditionFlow",
|
|
36
|
+
"subflowInputName": "state",
|
|
37
|
+
"subflowInputType": "json"
|
|
38
|
+
},
|
|
39
|
+
"saveState": {
|
|
40
|
+
"subflowId": "bodyFlow"
|
|
41
|
+
},
|
|
42
|
+
"selectIssue": {
|
|
43
|
+
"subflowId": "bodyFlow"
|
|
44
|
+
},
|
|
45
|
+
"validateNextState": {
|
|
46
|
+
"subflowId": "bodyFlow"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|