@fieldwangai/agentflow 0.1.153 → 0.1.156
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/README.md +16 -34
- package/README.zh-CN.md +14 -34
- package/agents/agentflow-node-executor-code.md +13 -3
- package/agents/agentflow-node-executor-planning.md +13 -3
- package/agents/agentflow-node-executor-requirement.md +13 -3
- package/agents/agentflow-node-executor-test.md +13 -3
- package/agents/agentflow-node-executor-ui.md +13 -3
- package/agents/agentflow-node-executor.md +13 -3
- package/agents/en/agentflow-node-executor.md +13 -3
- package/agents/zh/agentflow-node-executor.md +13 -3
- package/bin/agentflow.mjs +3 -3
- package/bin/lib/admin-builtin-pipelines.mjs +3 -2
- package/bin/lib/agent-runners.mjs +0 -929
- package/bin/lib/auth.mjs +2 -1
- package/bin/lib/catalog-agents.mjs +2 -1
- package/bin/lib/catalog-flows.mjs +165 -90
- package/bin/lib/composer-agent.mjs +3 -793
- package/bin/lib/composer-skill-router.mjs +3 -323
- package/bin/lib/exec-buffered.mjs +32 -0
- package/bin/lib/flow-dsl/cli.mjs +277 -0
- package/bin/lib/flow-dsl/codegen.mjs +358 -0
- package/bin/lib/flow-dsl/defs.mjs +103 -0
- package/bin/lib/flow-dsl/index.mjs +77 -0
- package/bin/lib/flow-dsl/ir.mjs +305 -0
- package/bin/lib/flow-dsl/layout.mjs +115 -0
- package/bin/lib/flow-dsl/legacy-yaml.mjs +303 -0
- package/bin/lib/flow-dsl/lint.mjs +241 -0
- package/bin/lib/flow-dsl/packages.mjs +251 -0
- package/bin/lib/flow-dsl/parser.mjs +444 -0
- package/bin/lib/flow-import.mjs +96 -26
- package/bin/lib/flow-write.mjs +48 -12
- package/bin/lib/help.mjs +8 -28
- package/bin/lib/html-escape.mjs +19 -0
- package/bin/lib/http-util.mjs +49 -0
- package/bin/lib/legacy-flow-execution.mjs +37 -0
- package/bin/lib/locales/en.json +1 -1
- package/bin/lib/locales/zh.json +1 -1
- package/bin/lib/main.mjs +152 -286
- package/bin/lib/marketplace.mjs +206 -61
- package/bin/lib/model-config.mjs +10 -0
- package/bin/lib/node-package-archive.mjs +240 -0
- package/bin/lib/node-package-bootstrap.mjs +72 -0
- package/bin/lib/node-package-manifest.mjs +174 -0
- package/bin/lib/paths.mjs +42 -19
- package/bin/lib/prd-workflow-routes.mjs +2949 -0
- package/bin/lib/prd-workflow-server.mjs +4884 -0
- package/bin/lib/run-events.mjs +0 -66
- package/bin/lib/schedule-config.mjs +4 -4
- package/bin/lib/skill-runtime.mjs +35 -0
- package/bin/lib/startup-storage-migrations.mjs +274 -0
- package/bin/lib/ui-server.mjs +1092 -18123
- package/bin/lib/user-env.mjs +16 -0
- package/bin/lib/workspace-auto-layout.mjs +234 -0
- package/bin/lib/workspace-flow-store.mjs +478 -0
- package/bin/lib/workspace-graph-merge.mjs +41 -34
- package/bin/lib/workspace-preview.mjs +12 -2
- package/bin/lib/workspace-routes.mjs +3088 -0
- package/bin/lib/workspace-server.mjs +6199 -0
- package/bin/lib/workspace-state.mjs +331 -0
- package/bin/lib/workspace.mjs +2 -1
- package/builtin/nodes/agent_subAgent.md +1 -0
- package/builtin/nodes/control_agent_toBool.md +2 -0
- package/builtin/nodes/control_anyOne.md +2 -0
- package/builtin/nodes/control_cancelled.md +2 -0
- package/builtin/nodes/control_cd_workspace.md +5 -11
- package/builtin/nodes/control_delay.md +2 -0
- package/builtin/nodes/control_end.md +2 -0
- package/builtin/nodes/control_if.md +1 -0
- package/builtin/nodes/control_interval_loop.md +2 -0
- package/builtin/nodes/control_load_mcp.md +24 -0
- package/builtin/nodes/control_load_skills.md +8 -23
- package/builtin/nodes/control_start.md +2 -0
- package/builtin/nodes/control_toBool.md +2 -0
- package/builtin/nodes/control_user_workspace.md +2 -0
- package/builtin/nodes/control_wait_until.md +2 -0
- package/builtin/nodes/display_ascii.md +1 -0
- package/builtin/nodes/display_chart.md +1 -0
- package/builtin/nodes/display_html.md +1 -0
- package/builtin/nodes/display_image.md +1 -0
- package/builtin/nodes/display_markdown.md +1 -0
- package/builtin/nodes/display_mermaid.md +1 -0
- package/builtin/nodes/display_react_app.md +1 -0
- package/builtin/nodes/display_table.md +1 -0
- package/builtin/nodes/provide_bool.md +1 -0
- package/builtin/nodes/provide_file.md +1 -0
- package/builtin/nodes/provide_password.md +1 -0
- package/builtin/nodes/provide_str.md +1 -0
- package/builtin/nodes/tool_display_share_link.md +1 -0
- package/builtin/nodes/tool_get_env.md +2 -0
- package/builtin/nodes/tool_git_checkout.md +1 -0
- package/builtin/nodes/tool_git_worktree_load.md +1 -0
- package/builtin/nodes/tool_git_worktree_unload.md +1 -0
- package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
- package/builtin/nodes/tool_jenkins_build.md +2 -0
- package/builtin/nodes/tool_load_key.md +2 -0
- package/builtin/nodes/tool_nodejs.md +17 -19
- package/builtin/nodes/tool_print.md +2 -0
- package/builtin/nodes/tool_save_key.md +2 -0
- package/builtin/nodes/tool_set_run_env.md +1 -0
- package/builtin/nodes/tool_user_ask.md +2 -0
- package/builtin/nodes/tool_user_check.md +2 -0
- package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
- package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
- package/builtin/nodes/workspace_one_click_task.md +44 -0
- package/builtin/nodes/workspace_run.md +16 -0
- package/builtin/nodes/workspace_scheduled_run.md +16 -0
- package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
- package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
- package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
- package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
- package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
- package/builtin/pipelines/new/workspace.flow.js +91 -0
- package/builtin/pipelines/new/workspace.layout.json +70 -0
- package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-B9xslwI0.js → WorkflowAssistantThread-ClNxY2Wu.js} +1 -1
- package/builtin/web-ui/dist/assets/index-BJzMYRK3.js +870 -0
- package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +3 -1
- package/reference/flow-control-capabilities.md +77 -158
- package/reference/flow-layout.md +1 -1
- package/reference/flow-prompt-handler-check.md +2 -2
- package/skills/agentflow-author-flow/SKILL.md +1 -1
- package/skills/agentflow-cli/SKILL.md +139 -24
- package/skills/agentflow-cli/agents/openai.yaml +2 -2
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +654 -20
- package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
- package/skills/agentflow-flow-add-instances/SKILL.md +1 -1
- package/skills/agentflow-flow-dsl/SKILL.md +206 -0
- package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
- package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
- package/skills/agentflow-flow-edit-node-fields/SKILL.md +1 -1
- package/skills/agentflow-flow-recipes/SKILL.md +7 -4
- package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
- package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
- package/skills/agentflow-node-dsl/SKILL.md +212 -0
- package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
- package/skills/agentflow-node-reference/SKILL.md +2 -2
- package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
- package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
- package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
- package/skills/agentflow-workspace-graph/SKILL.md +62 -48
- package/skills/agentflow-workspace-html/SKILL.md +7 -2
- package/skills/agentflow-workspace-image/SKILL.md +6 -2
- package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
- package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
- package/bin/lib/api-runner.mjs +0 -387
- package/bin/lib/apply.mjs +0 -903
- package/bin/lib/composer-flow-instances.mjs +0 -68
- package/bin/lib/composer-flow-skeleton.mjs +0 -334
- package/bin/lib/composer-flow-validate.mjs +0 -47
- package/bin/lib/composer-model-router.mjs +0 -185
- package/bin/lib/composer-node-schema.mjs +0 -303
- package/bin/lib/composer-planner.mjs +0 -751
- package/bin/lib/composer-script-ops.mjs +0 -233
- package/bin/lib/flow-static-preview.mjs +0 -104
- package/bin/lib/hub-login.mjs +0 -54
- package/bin/lib/hub-publish.mjs +0 -159
- package/bin/lib/hub-remote.mjs +0 -189
- package/bin/lib/hub.mjs +0 -299
- package/bin/lib/jenkins.mjs +0 -380
- package/bin/lib/node-execute.mjs +0 -539
- package/bin/lib/normalize-node-tool-command.mjs +0 -97
- package/bin/lib/runtime-context.mjs +0 -243
- package/bin/lib/scheduler.mjs +0 -601
- package/bin/lib/ui-print.mjs +0 -94
- package/bin/pipeline/build-node-prompt.mjs +0 -271
- package/bin/pipeline/check-cache.mjs +0 -191
- package/bin/pipeline/check-flow.mjs +0 -543
- package/bin/pipeline/collect-nodes.mjs +0 -212
- package/bin/pipeline/compute-cache-md5.mjs +0 -177
- package/bin/pipeline/ensure-run-dir.mjs +0 -71
- package/bin/pipeline/gc.mjs +0 -129
- package/bin/pipeline/get-env.mjs +0 -59
- package/bin/pipeline/get-resolved-values.mjs +0 -344
- package/bin/pipeline/load-key.mjs +0 -62
- package/bin/pipeline/parse-flow.mjs +0 -708
- package/bin/pipeline/post-process-control-if.mjs +0 -23
- package/bin/pipeline/post-process-node.mjs +0 -490
- package/bin/pipeline/pre-process-node.mjs +0 -1430
- package/bin/pipeline/resolve-inputs.mjs +0 -201
- package/bin/pipeline/run-tool-nodejs.mjs +0 -167
- package/bin/pipeline/save-key.mjs +0 -93
- package/bin/pipeline/snapshot-prior-round.mjs +0 -70
- package/bin/pipeline/validate-for-ui.mjs +0 -234
- package/bin/pipeline/validate-script-output.mjs +0 -130
- package/bin/pipeline/write-result.mjs +0 -182
- package/builtin/pipelines/module-migrate/flow.yaml +0 -819
- package/builtin/pipelines/new/flow.yaml +0 -545
- package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
- package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
- package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
- package/builtin/web-ui/dist/assets/index-DmUV7ZCL.js +0 -888
- package/skills/agentflow-node-authoring/SKILL.md +0 -57
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace 图的设计态 / 运行态分离。
|
|
3
|
+
*
|
|
4
|
+
* `workspace.graph.json` 原本一个文件装三样东西:图结构、运行产出、画布布局。真实语料
|
|
5
|
+
* 里运行产出占 33.6%(21 个流程 1.38 MB 中的 465 KB),每跑一次就变一次——既污染 diff,
|
|
6
|
+
* 也让 AI 生成的图和跑过的图无法比对。
|
|
7
|
+
*
|
|
8
|
+
* 这里把运行态抽到 `workspace.state.json`:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* workspace.graph.json 设计态:节点、连线、位置、作者写的内容
|
|
12
|
+
* workspace.state.json 运行态:输出槽产出、展示节点的运行内容、视口
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* **这是纯存储层的改动。** `readWorkspaceGraph` 合并两个文件后返回的图与拆分前逐字节
|
|
16
|
+
* 相同,上层(含 designRevision 的计算)看不到任何区别。
|
|
17
|
+
*
|
|
18
|
+
* 哪些算运行态,与 `workspace-graph-merge.mjs` 的 `isRuntimePath` 保持同一套判断:
|
|
19
|
+
*
|
|
20
|
+
* - 非 provide 节点的 `output[*].value` / `.default`——provide 节点的输出值是用户填的
|
|
21
|
+
* - **接了非语义入边**的 `input[*].value` / `.default`——每次运行都会被上游覆写
|
|
22
|
+
* - `displayReloadKey`
|
|
23
|
+
* - `runFingerprint`——节点上次成功执行时的输入指纹,缓存命中判据,见 `workspace-server.mjs`
|
|
24
|
+
* - `ui.viewport`
|
|
25
|
+
* - **有内容入边**的展示节点的 `body`
|
|
26
|
+
*
|
|
27
|
+
* 后两条需要看图结构才能判断。展示节点没有内容入边时,`body` 是作者手写的文档(语料里
|
|
28
|
+
* 有 29 个这样的节点、64 KB),当成运行态外移就等于删掉它们;判断规则与 ui-server 运行
|
|
29
|
+
* 循环里的 `workspaceContentInputEdge` 完全一致。入槽同理:没有入边的槽,值是作者填的
|
|
30
|
+
* 默认值;有入边的槽,值只是上一次跑进来的东西。
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export const WORKSPACE_STATE_FILENAME = "workspace.state.json";
|
|
34
|
+
|
|
35
|
+
const STATE_VERSION = 1;
|
|
36
|
+
|
|
37
|
+
function isPlainObject(value) {
|
|
38
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isProvideDefinition(definitionId) {
|
|
42
|
+
return String(definitionId || "").startsWith("provide_");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isDisplayDefinition(definitionId) {
|
|
46
|
+
return String(definitionId || "").startsWith("display_");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 语义入槽——连到这些槽上的边不代表「内容由上游驱动」。
|
|
51
|
+
* 与 ui-server 的 `isWorkspaceSemanticInputSlot` 逐字一致;改一处必须改两处。
|
|
52
|
+
*/
|
|
53
|
+
function isSemanticInputSlot(slot) {
|
|
54
|
+
const name = String(slot?.name || "");
|
|
55
|
+
const type = String(slot?.type || "");
|
|
56
|
+
return type === "node"
|
|
57
|
+
|| name === "prev" || name === "next"
|
|
58
|
+
|| CONTEXT_SLOT_NAMES.has(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 上下文注入槽。这些槽的值一律由运行时灌入——skills 正文、MCP 清单、工作区摘要——
|
|
63
|
+
* 有没有入边都一样。语料里见过 15 KB 的 HTML 正文被复制进 `workspaceContext`,
|
|
64
|
+
* 那显然不是作者手填的默认值。
|
|
65
|
+
*/
|
|
66
|
+
const CONTEXT_SLOT_NAMES = new Set([
|
|
67
|
+
"skillsContext",
|
|
68
|
+
"mcpContext",
|
|
69
|
+
"knowledgeContext",
|
|
70
|
+
"workspaceContext",
|
|
71
|
+
"gitContext",
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
function handleIndex(handle, prefix) {
|
|
75
|
+
const match = String(handle || "").match(new RegExp(`^${prefix}-(\\d+)$`));
|
|
76
|
+
return match ? Number(match[1]) : 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 展示节点是否由上游内容驱动。复刻 ui-server 的 `workspaceContentInputEdge`:
|
|
81
|
+
* 收集入边,去掉指向语义槽的,剩下任意一条就说明 body 是运行产出。
|
|
82
|
+
*
|
|
83
|
+
* 不能简单地按 `targetHandle !== "input-0"` 近似——语料里有 32 个实例的槽位顺序不规范,
|
|
84
|
+
* input-0 未必是 prev;语义槽也可能出现在更靠后的位置。
|
|
85
|
+
*/
|
|
86
|
+
function displayNodeIdsDrivenByEdges(graph) {
|
|
87
|
+
const out = new Set();
|
|
88
|
+
for (const [nodeId, slots] of edgeDrivenInputSlots(graph)) {
|
|
89
|
+
const definitionId = graph?.instances?.[nodeId]?.definitionId;
|
|
90
|
+
if (isDisplayDefinition(definitionId) && slots.size) out.add(nodeId);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 每个节点上「值由上游驱动」的入槽名。
|
|
97
|
+
*
|
|
98
|
+
* 语义槽(prev / skillsContext / …)不算:连到它们的边传的是控制流或上下文,不会覆写
|
|
99
|
+
* 槽里的值。其余槽只要有入边,运行时就会用上游的值覆盖——留在设计态里等于每跑一次
|
|
100
|
+
* 就把设计文件改一次。
|
|
101
|
+
*
|
|
102
|
+
* @returns {Map<string, Set<string>>}
|
|
103
|
+
*/
|
|
104
|
+
function edgeDrivenInputSlots(graph) {
|
|
105
|
+
const out = new Map();
|
|
106
|
+
const instances = isPlainObject(graph?.instances) ? graph.instances : {};
|
|
107
|
+
for (const edge of Array.isArray(graph?.edges) ? graph.edges : []) {
|
|
108
|
+
const targetId = String(edge?.target || "");
|
|
109
|
+
const target = instances[targetId];
|
|
110
|
+
if (!targetId || !isPlainObject(target)) continue;
|
|
111
|
+
const input = Array.isArray(target.input) ? target.input : [];
|
|
112
|
+
const slot = input[handleIndex(edge?.targetHandle, "input")] || null;
|
|
113
|
+
if (!slot || isSemanticInputSlot(slot)) continue;
|
|
114
|
+
const name = String(slot.name ?? "");
|
|
115
|
+
if (!name) continue;
|
|
116
|
+
if (!out.has(targetId)) out.set(targetId, new Set());
|
|
117
|
+
out.get(targetId).add(name);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** 槽名在实例内唯一时才按名字外移;重名就整个数组放弃外移,宁可留在设计里也不丢值。 */
|
|
123
|
+
function slotNamesAreUnique(slots) {
|
|
124
|
+
const names = (Array.isArray(slots) ? slots : []).map((s) => String(s?.name ?? ""));
|
|
125
|
+
return new Set(names).size === names.length;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 把一组槽里的 value/default 摘出来。
|
|
130
|
+
* @returns {{ slots: any[], state: Record<string, {value?: any, default?: any}> }}
|
|
131
|
+
*/
|
|
132
|
+
function extractSlotValues(slots, shouldExtract) {
|
|
133
|
+
const state = {};
|
|
134
|
+
const next = slots.map((slot) => {
|
|
135
|
+
if (!isPlainObject(slot)) return slot;
|
|
136
|
+
const name = String(slot.name ?? "");
|
|
137
|
+
if (!shouldExtract(name, slot)) return slot;
|
|
138
|
+
const entry = {};
|
|
139
|
+
if (slot.value !== undefined) entry.value = slot.value;
|
|
140
|
+
if (slot.default !== undefined) entry.default = slot.default;
|
|
141
|
+
if (!Object.keys(entry).length) return slot;
|
|
142
|
+
state[name] = entry;
|
|
143
|
+
const clean = { ...slot };
|
|
144
|
+
delete clean.value;
|
|
145
|
+
delete clean.default;
|
|
146
|
+
return clean;
|
|
147
|
+
});
|
|
148
|
+
return { slots: next, state };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function restoreSlotValues(slots, state) {
|
|
152
|
+
if (!isPlainObject(state) || !Array.isArray(slots)) return slots;
|
|
153
|
+
return slots.map((slot) => {
|
|
154
|
+
if (!isPlainObject(slot)) return slot;
|
|
155
|
+
const entry = state[String(slot.name ?? "")];
|
|
156
|
+
if (!isPlainObject(entry)) return slot;
|
|
157
|
+
const next = { ...slot };
|
|
158
|
+
if (entry.value !== undefined) next.value = entry.value;
|
|
159
|
+
if (entry.default !== undefined) next.default = entry.default;
|
|
160
|
+
return next;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 一张图里「哪些位置是运行态」的完整答案,供存储拆分、三路合并、revision 计算共用。
|
|
166
|
+
*
|
|
167
|
+
* 这三处必须用同一份判断,否则会得到很难查的错觉:存储层把某个值当运行产出移走了,
|
|
168
|
+
* revision 却仍然把它算进设计——于是跑一次流程,所有协作者手里的 designRevision 全部
|
|
169
|
+
* 作废,明明没人动过图。
|
|
170
|
+
*
|
|
171
|
+
* @param {object} graph
|
|
172
|
+
* @returns {{ inputs: Map<string, Set<string>>, displayBodies: Set<string>, isProvide: (nodeId: string) => boolean }}
|
|
173
|
+
*/
|
|
174
|
+
export function workspaceRuntimeSurface(graph) {
|
|
175
|
+
const instances = isPlainObject(graph?.instances) ? graph.instances : {};
|
|
176
|
+
const driven = edgeDrivenInputSlots(graph);
|
|
177
|
+
const inputs = new Map();
|
|
178
|
+
for (const [nodeId, instance] of Object.entries(instances)) {
|
|
179
|
+
const names = new Set(driven.get(nodeId) || []);
|
|
180
|
+
for (const slot of Array.isArray(instance?.input) ? instance.input : []) {
|
|
181
|
+
const name = String(slot?.name ?? "");
|
|
182
|
+
if (name && CONTEXT_SLOT_NAMES.has(name)) names.add(name);
|
|
183
|
+
}
|
|
184
|
+
if (names.size) inputs.set(nodeId, names);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
inputs,
|
|
188
|
+
displayBodies: displayNodeIdsDrivenByEdges(graph),
|
|
189
|
+
isProvide: (nodeId) => isProvideDefinition(instances[nodeId]?.definitionId),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 把一张完整的图拆成设计态和运行态。
|
|
195
|
+
* @param {object} graph
|
|
196
|
+
* @returns {{ design: object, state: object }}
|
|
197
|
+
*/
|
|
198
|
+
export function splitWorkspaceGraph(graph) {
|
|
199
|
+
const source = isPlainObject(graph) ? graph : {};
|
|
200
|
+
const instances = isPlainObject(source.instances) ? source.instances : {};
|
|
201
|
+
const displayDriven = displayNodeIdsDrivenByEdges(source);
|
|
202
|
+
const drivenInputs = edgeDrivenInputSlots(source);
|
|
203
|
+
|
|
204
|
+
const inputs = {};
|
|
205
|
+
const outputs = {};
|
|
206
|
+
const displayBodies = {};
|
|
207
|
+
const displayReloadKeys = {};
|
|
208
|
+
const fingerprints = {};
|
|
209
|
+
const designInstances = {};
|
|
210
|
+
|
|
211
|
+
for (const [nodeId, raw] of Object.entries(instances)) {
|
|
212
|
+
if (!isPlainObject(raw)) {
|
|
213
|
+
designInstances[nodeId] = raw;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const instance = { ...raw };
|
|
217
|
+
|
|
218
|
+
if (instance.displayReloadKey !== undefined) {
|
|
219
|
+
displayReloadKeys[nodeId] = instance.displayReloadKey;
|
|
220
|
+
delete instance.displayReloadKey;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (instance.runFingerprint !== undefined) {
|
|
224
|
+
fingerprints[nodeId] = instance.runFingerprint;
|
|
225
|
+
delete instance.runFingerprint;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (isDisplayDefinition(instance.definitionId) && displayDriven.has(nodeId) && instance.body !== undefined) {
|
|
229
|
+
displayBodies[nodeId] = instance.body;
|
|
230
|
+
delete instance.body;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const driven = drivenInputs.get(nodeId) || new Set();
|
|
234
|
+
if (Array.isArray(instance.input) && slotNamesAreUnique(instance.input)) {
|
|
235
|
+
const extracted = extractSlotValues(
|
|
236
|
+
instance.input,
|
|
237
|
+
(name) => driven.has(name) || CONTEXT_SLOT_NAMES.has(name),
|
|
238
|
+
);
|
|
239
|
+
instance.input = extracted.slots;
|
|
240
|
+
if (Object.keys(extracted.state).length) inputs[nodeId] = extracted.state;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!isProvideDefinition(instance.definitionId)
|
|
244
|
+
&& Array.isArray(instance.output)
|
|
245
|
+
&& slotNamesAreUnique(instance.output)) {
|
|
246
|
+
const extracted = extractSlotValues(instance.output, () => true);
|
|
247
|
+
instance.output = extracted.slots;
|
|
248
|
+
if (Object.keys(extracted.state).length) outputs[nodeId] = extracted.state;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
designInstances[nodeId] = instance;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const design = { ...source, instances: designInstances };
|
|
255
|
+
if (isPlainObject(source.ui)) {
|
|
256
|
+
const ui = { ...source.ui };
|
|
257
|
+
delete ui.viewport;
|
|
258
|
+
design.ui = ui;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const state = { version: STATE_VERSION };
|
|
262
|
+
if (Object.keys(inputs).length) state.inputs = inputs;
|
|
263
|
+
if (Object.keys(outputs).length) state.outputs = outputs;
|
|
264
|
+
if (Object.keys(displayBodies).length) state.displayBodies = displayBodies;
|
|
265
|
+
if (Object.keys(displayReloadKeys).length) state.displayReloadKeys = displayReloadKeys;
|
|
266
|
+
if (Object.keys(fingerprints).length) state.fingerprints = fingerprints;
|
|
267
|
+
if (isPlainObject(source.ui) && source.ui.viewport !== undefined) state.viewport = source.ui.viewport;
|
|
268
|
+
|
|
269
|
+
return { design, state };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 把运行态合回设计态。`state` 为空时原样返回设计态,因此对尚未拆分的旧图是恒等操作。
|
|
274
|
+
* @param {object} design
|
|
275
|
+
* @param {object|null} state
|
|
276
|
+
* @returns {object}
|
|
277
|
+
*/
|
|
278
|
+
export function mergeWorkspaceState(design, state) {
|
|
279
|
+
const base = isPlainObject(design) ? design : {};
|
|
280
|
+
if (!isPlainObject(state)) return base;
|
|
281
|
+
|
|
282
|
+
const inputs = isPlainObject(state.inputs) ? state.inputs : {};
|
|
283
|
+
const outputs = isPlainObject(state.outputs) ? state.outputs : {};
|
|
284
|
+
const displayBodies = isPlainObject(state.displayBodies) ? state.displayBodies : {};
|
|
285
|
+
const displayReloadKeys = isPlainObject(state.displayReloadKeys) ? state.displayReloadKeys : {};
|
|
286
|
+
const fingerprints = isPlainObject(state.fingerprints) ? state.fingerprints : {};
|
|
287
|
+
const instances = isPlainObject(base.instances) ? base.instances : {};
|
|
288
|
+
|
|
289
|
+
const merged = {};
|
|
290
|
+
for (const [nodeId, raw] of Object.entries(instances)) {
|
|
291
|
+
if (!isPlainObject(raw)) {
|
|
292
|
+
merged[nodeId] = raw;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const instance = { ...raw };
|
|
296
|
+
|
|
297
|
+
if (Object.prototype.hasOwnProperty.call(displayBodies, nodeId)) {
|
|
298
|
+
instance.body = displayBodies[nodeId];
|
|
299
|
+
}
|
|
300
|
+
if (Object.prototype.hasOwnProperty.call(displayReloadKeys, nodeId)) {
|
|
301
|
+
instance.displayReloadKey = displayReloadKeys[nodeId];
|
|
302
|
+
}
|
|
303
|
+
if (Object.prototype.hasOwnProperty.call(fingerprints, nodeId)) {
|
|
304
|
+
instance.runFingerprint = fingerprints[nodeId];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 只在原来就有这个数组时回填——直接赋值会给没有 input/output 的实例凭空加上
|
|
308
|
+
// `input: undefined`,合并前后就不再逐字节相同了
|
|
309
|
+
if (Array.isArray(instance.input)) instance.input = restoreSlotValues(instance.input, inputs[nodeId]);
|
|
310
|
+
if (Array.isArray(instance.output)) instance.output = restoreSlotValues(instance.output, outputs[nodeId]);
|
|
311
|
+
|
|
312
|
+
merged[nodeId] = instance;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const out = { ...base, instances: merged };
|
|
316
|
+
if (state.viewport !== undefined) {
|
|
317
|
+
out.ui = { ...(isPlainObject(base.ui) ? base.ui : {}), viewport: state.viewport };
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** 运行态里没有任何内容时为 true——此时不必落盘 state 文件。 */
|
|
323
|
+
export function isEmptyWorkspaceState(state) {
|
|
324
|
+
if (!isPlainObject(state)) return true;
|
|
325
|
+
return !state.inputs
|
|
326
|
+
&& !state.outputs
|
|
327
|
+
&& !state.displayBodies
|
|
328
|
+
&& !state.displayReloadKeys
|
|
329
|
+
&& !state.fingerprints
|
|
330
|
+
&& state.viewport === undefined;
|
|
331
|
+
}
|
package/bin/lib/workspace.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getUserPipelinesRoot,
|
|
12
12
|
resolveUniqueUserPipelineDir,
|
|
13
13
|
ARCHIVED_PIPELINES_DIR_NAME,
|
|
14
|
+
isFlowDir,
|
|
14
15
|
} from "./paths.mjs";
|
|
15
16
|
|
|
16
17
|
export { getRunDir } from "./paths.mjs";
|
|
@@ -144,7 +145,7 @@ export function listRunsWithLogs(workspaceRoot) {
|
|
|
144
145
|
/** 解析活跃 flow 目录:user → workspace → legacy workspace → builtin。归档 flow 必须走显式 archived resolver。 */
|
|
145
146
|
export function getFlowDir(workspaceRoot, flowName, opts = {}) {
|
|
146
147
|
const root = path.resolve(workspaceRoot);
|
|
147
|
-
const hasFlow = (dir) =>
|
|
148
|
+
const hasFlow = (dir) => isFlowDir(dir);
|
|
148
149
|
|
|
149
150
|
// user pipelines
|
|
150
151
|
const userRoot = getUserPipelinesRoot(opts.userId);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# 内置节点:AI 转布尔(agent 执行)
|
|
3
|
+
runtime: degraded
|
|
4
|
+
palette: hidden
|
|
3
5
|
description: "AI-powered boolean judgment: an agent evaluates the input value and writes true/false to prediction. Use for non-deterministic scenarios requiring semantic understanding."
|
|
4
6
|
displayName: Agent ToBool
|
|
5
7
|
input:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# 内置节点:加载知识库
|
|
3
|
+
runtime: native
|
|
4
|
+
type: control
|
|
3
5
|
description: |
|
|
4
6
|
Load one or more read-only knowledge sources for downstream Agent nodes.
|
|
5
7
|
|
|
@@ -11,20 +13,16 @@ input:
|
|
|
11
13
|
- type: node
|
|
12
14
|
name: prev
|
|
13
15
|
default: ""
|
|
14
|
-
- type: text
|
|
15
|
-
name: knowledgeContext
|
|
16
|
-
default: ""
|
|
17
|
-
showOnNode: false
|
|
18
16
|
- type: text
|
|
19
17
|
name: path
|
|
20
18
|
default: ""
|
|
21
19
|
showOnNode: false
|
|
22
20
|
- type: text
|
|
23
|
-
name:
|
|
24
|
-
default: "
|
|
21
|
+
name: label
|
|
22
|
+
default: ""
|
|
25
23
|
showOnNode: false
|
|
26
24
|
- type: text
|
|
27
|
-
name:
|
|
25
|
+
name: knowledgeContext
|
|
28
26
|
default: ""
|
|
29
27
|
showOnNode: false
|
|
30
28
|
- type: text
|
|
@@ -48,9 +46,5 @@ output:
|
|
|
48
46
|
name: cwd
|
|
49
47
|
default: ""
|
|
50
48
|
showOnNode: false
|
|
51
|
-
- type: text
|
|
52
|
-
name: previous
|
|
53
|
-
default: ""
|
|
54
|
-
showOnNode: false
|
|
55
49
|
---
|
|
56
50
|
Load selected knowledge sources for downstream agents.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:Load MCP
|
|
3
|
+
runtime: native
|
|
4
|
+
type: control
|
|
5
|
+
description: 加载所选 Cursor MCP Server 的工具清单,通过 mcpContext 传给下游 agent 节点。
|
|
6
|
+
displayName: Load MCP
|
|
7
|
+
input:
|
|
8
|
+
- type: node
|
|
9
|
+
name: prev
|
|
10
|
+
default: ""
|
|
11
|
+
- type: text
|
|
12
|
+
name: serverNames
|
|
13
|
+
default: ""
|
|
14
|
+
showOnNode: false
|
|
15
|
+
output:
|
|
16
|
+
- type: node
|
|
17
|
+
name: next
|
|
18
|
+
default: ""
|
|
19
|
+
- type: text
|
|
20
|
+
name: mcpContext
|
|
21
|
+
default: ""
|
|
22
|
+
showOnNode: true
|
|
23
|
+
---
|
|
24
|
+
Load selected Cursor MCP server tool manifests for downstream agent nodes.
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
# 内置节点:Load Skills
|
|
3
|
+
runtime: native
|
|
4
|
+
type: control
|
|
3
5
|
description: |
|
|
4
|
-
Load
|
|
6
|
+
Load the currently selected Workspace skill collection for downstream agent nodes.
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
Set `skillKeys` to skill names or registry keys, then connect `skillsContext` to
|
|
9
|
+
downstream agent/tool nodes. Loaded skills are injected into the node prompt under
|
|
10
|
+
"已加载 Skills"。
|
|
7
11
|
|
|
8
12
|
Skill key examples:
|
|
9
13
|
- `agentflow-flow-add-instances`
|
|
10
14
|
- `workspace-agents:agentflow-flow-edit-node-fields`
|
|
11
15
|
- `global-codex:some-skill`
|
|
12
|
-
|
|
13
|
-
Merge modes:
|
|
14
|
-
- `replace`
|
|
15
|
-
- `append`
|
|
16
|
-
- `prepend`
|
|
17
16
|
displayName: Load Skills
|
|
18
17
|
input:
|
|
19
18
|
- type: node
|
|
@@ -24,15 +23,6 @@ input:
|
|
|
24
23
|
default: ""
|
|
25
24
|
required: true
|
|
26
25
|
showOnNode: true
|
|
27
|
-
- type: text
|
|
28
|
-
name: mergeMode
|
|
29
|
-
default: "replace"
|
|
30
|
-
- type: text
|
|
31
|
-
name: workspaceContext
|
|
32
|
-
default: ""
|
|
33
|
-
- type: text
|
|
34
|
-
name: skillsContext
|
|
35
|
-
default: ""
|
|
36
26
|
output:
|
|
37
27
|
- type: node
|
|
38
28
|
name: next
|
|
@@ -40,11 +30,6 @@ output:
|
|
|
40
30
|
- type: text
|
|
41
31
|
name: skillsContext
|
|
42
32
|
default: ""
|
|
43
|
-
|
|
44
|
-
name: loadedCount
|
|
45
|
-
default: ""
|
|
46
|
-
- type: text
|
|
47
|
-
name: summary
|
|
48
|
-
default: ""
|
|
33
|
+
showOnNode: true
|
|
49
34
|
---
|
|
50
|
-
Load public skills `${skillKeys}`
|
|
35
|
+
Load public skills `${skillKeys}` and pass them to downstream nodes.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# 内置节点:代码转布尔(本地脚本执行,★ 可扩展输入)
|
|
3
|
+
runtime: none
|
|
4
|
+
palette: hidden
|
|
3
5
|
description: "Script-based boolean conversion: executes script to produce true/false prediction. Like tool_nodejs but enforces bool output. Must have script field."
|
|
4
6
|
displayName: Code ToBool
|
|
5
7
|
input:
|