@fieldwangai/agentflow 0.1.159 → 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.
Files changed (42) hide show
  1. package/bin/lib/catalog-flows.mjs +10 -1
  2. package/bin/lib/control-while.mjs +336 -0
  3. package/bin/lib/flow-dsl/codegen.mjs +74 -11
  4. package/bin/lib/flow-dsl/ir.mjs +33 -2
  5. package/bin/lib/flow-dsl/lint.mjs +128 -3
  6. package/bin/lib/flow-dsl/parser.mjs +168 -4
  7. package/bin/lib/marketplace.mjs +10 -2
  8. package/bin/lib/node-package-manifest.mjs +7 -2
  9. package/bin/lib/node-ui-kit.mjs +156 -0
  10. package/bin/lib/paths.mjs +2 -0
  11. package/bin/lib/workspace-flow-store.mjs +11 -2
  12. package/bin/lib/workspace-graph-merge.mjs +3 -0
  13. package/bin/lib/workspace-routes.mjs +10 -1
  14. package/bin/lib/workspace-server.mjs +592 -11
  15. package/bin/pipeline/validate-flow.mjs +3 -3
  16. package/builtin/nodes/control_parse_json.md +25 -0
  17. package/builtin/nodes/control_subflow_call.md +27 -0
  18. package/builtin/nodes/control_while.md +124 -0
  19. package/builtin/nodes/provide_json.md +14 -0
  20. package/builtin/nodes/workspace_subflow_input.md +14 -0
  21. package/builtin/pipelines/subflow-preview/workspace.flow.js +29 -0
  22. package/builtin/pipelines/subflow-preview/workspace.layout.json +38 -0
  23. package/builtin/pipelines/subflow-preview/workspace.nodes.json +19 -0
  24. package/builtin/pipelines/subflow-preview/workspace.state.json +93 -0
  25. package/builtin/pipelines/while-subflow-preview/workspace.flow.js +55 -0
  26. package/builtin/pipelines/while-subflow-preview/workspace.layout.json +66 -0
  27. package/builtin/pipelines/while-subflow-preview/workspace.nodes.json +49 -0
  28. package/builtin/pipelines/while-subflow-preview/workspace.state.json +194 -0
  29. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-CY19DsYq.js → WorkflowAssistantThread-Bfo9Ythw.js} +1 -1
  30. package/builtin/web-ui/dist/assets/index-XbeI5foV.js +872 -0
  31. package/builtin/web-ui/dist/assets/index-e1omCEau.css +1 -0
  32. package/builtin/web-ui/dist/index.html +2 -2
  33. package/package.json +2 -1
  34. package/reference/flow-control-capabilities.md +41 -16
  35. package/shared/slot-types.js +58 -0
  36. package/skills/agentflow-flow-dsl/SKILL.md +60 -6
  37. package/skills/agentflow-flow-dsl/references/node-calls.md +3 -0
  38. package/skills/agentflow-flow-dsl/references/subflow-authoring.md +230 -0
  39. package/skills/agentflow-node-dsl/SKILL.md +39 -0
  40. package/skills/agentflow-node-reference/references/builtin-nodes.md +25 -0
  41. package/builtin/web-ui/dist/assets/index-B9ppXv7e.css +0 -1
  42. package/builtin/web-ui/dist/assets/index-CdEQWRrp.js +0 -872
@@ -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,14 @@
1
+ ---
2
+ # 内置节点:直接提供 JSON
3
+ runtime: native
4
+ type: provide
5
+ description: 提供经过校验的 JSON 值,输出可以直接连接 json 类型输入。
6
+ displayName: JSON
7
+ output:
8
+ - type: json
9
+ name: value
10
+ default: "null"
11
+ required: true
12
+ showOnNode: true
13
+ ---
14
+ ${USER_PROMPT}
@@ -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
+ }