@fieldwangai/agentflow 0.1.162 → 0.1.163

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 (41) hide show
  1. package/bin/lib/flow-dsl/codegen.mjs +23 -2
  2. package/bin/lib/flow-dsl/lint.mjs +44 -0
  3. package/bin/lib/flow-dsl/parser.mjs +68 -1
  4. package/bin/lib/marketplace-usage.mjs +218 -0
  5. package/bin/lib/marketplace.mjs +183 -3
  6. package/bin/lib/node-package-manifest.mjs +1 -1
  7. package/bin/lib/spaces.mjs +200 -0
  8. package/bin/lib/ui-server.mjs +251 -67
  9. package/bin/lib/workspace-routes.mjs +189 -2
  10. package/bin/lib/workspace-server.mjs +367 -30
  11. package/bin/lib/workspace-state.mjs +2 -1
  12. package/builtin/nodes/agent_subAgent.md +5 -1
  13. package/builtin/nodes/context_bundle.md +44 -0
  14. package/builtin/nodes/context_knowledge.md +29 -0
  15. package/builtin/nodes/context_skills.md +29 -0
  16. package/builtin/nodes/context_workspace.md +36 -0
  17. package/builtin/nodes/control_while.md +7 -0
  18. package/builtin/nodes/tool_git_worktree_load.md +4 -3
  19. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-CTrXOZ00.js → WorkflowAssistantThread-DmhIgoD7.js} +1 -1
  20. package/builtin/web-ui/dist/assets/index-CI9J6Unt.js +873 -0
  21. package/builtin/web-ui/dist/assets/index-DY5vE7v1.css +1 -0
  22. package/builtin/web-ui/dist/index.html +2 -2
  23. package/package.json +1 -1
  24. package/shared/slot-types.js +1 -0
  25. package/skills/agentflow-cli/SKILL.md +50 -3
  26. package/skills/agentflow-cli/runtime/bin/lib/skill-runtime.mjs +120 -6
  27. package/skills/agentflow-cli/runtime/builtin/nodes/agent_subAgent.md +5 -1
  28. package/skills/agentflow-cli/runtime/builtin/nodes/context_bundle.md +44 -0
  29. package/skills/agentflow-cli/runtime/builtin/nodes/context_knowledge.md +29 -0
  30. package/skills/agentflow-cli/runtime/builtin/nodes/context_skills.md +29 -0
  31. package/skills/agentflow-cli/runtime/builtin/nodes/context_workspace.md +36 -0
  32. package/skills/agentflow-cli/runtime/builtin/nodes/control_while.md +7 -0
  33. package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_load.md +4 -3
  34. package/skills/agentflow-cli/runtime/package.json +1 -1
  35. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +64 -0
  36. package/skills/agentflow-flow-dsl/SKILL.md +48 -2
  37. package/skills/agentflow-flow-dsl/references/node-calls.md +6 -2
  38. package/skills/agentflow-flow-dsl/references/subflow-authoring.md +34 -7
  39. package/skills/agentflow-node-reference/references/builtin-nodes.md +37 -5
  40. package/builtin/web-ui/dist/assets/index-5uJFccdX.css +0 -1
  41. package/builtin/web-ui/dist/assets/index-BeUfNQRL.js +0 -873
@@ -30,8 +30,8 @@
30
30
  @keyframes af-app-loading-slide { from { transform: translateX(-115%); } to { transform: translateX(250%); } }
31
31
  @media (prefers-reduced-motion: reduce) { .af-app-loading__track span { width: 100%; animation: none; } }
32
32
  </style>
33
- <script type="module" crossorigin src="/assets/index-BeUfNQRL.js"></script>
34
- <link rel="stylesheet" crossorigin href="/assets/index-5uJFccdX.css">
33
+ <script type="module" crossorigin src="/assets/index-CI9J6Unt.js"></script>
34
+ <link rel="stylesheet" crossorigin href="/assets/index-DY5vE7v1.css">
35
35
  </head>
36
36
  <body>
37
37
  <div id="root">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldwangai/agentflow",
3
- "version": "0.1.162",
3
+ "version": "0.1.163",
4
4
  "description": "Orchestration system for long-running complex agent tasks using Cursor, OpenCode, Claude Code, or Codex as execution backends",
5
5
  "type": "module",
6
6
  "main": "bin/agentflow.mjs",
@@ -28,6 +28,7 @@ export const SLOT_TYPE_COLORS = Object.freeze({
28
28
  node: "#ff9800",
29
29
  bool: "#9c27b0",
30
30
  json: "#00bcd4",
31
+ context: "#7c5cff",
31
32
  image: "#ec407a",
32
33
  any: "#9e9e9e",
33
34
  });
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agentflow-cli
3
- description: Direct AgentFlow operation through a bundled browser-authorized CLI, without MCP or npm. Use when Codex needs to authorize AgentFlow access, create, update, run, or publish temporary Workspace drafts; search, publish, install, or synchronize versioned node packages; pull or publish portable Workspace DSL flows; manage scheduled runs; upload read-only previews; run or inspect Workspace graphs; or read logs and display outputs through AgentFlow HTTP APIs.
3
+ description: Direct AgentFlow operation through a bundled browser-authorized CLI, without MCP or npm. Use when Codex needs to authorize AgentFlow access, create, update, run, or publish temporary Workspace drafts; search usage-ranked Marketplace Flow templates and nodes; publish, install, privatize, or synchronize versioned resources; pull or publish portable Workspace DSL flows; manage scheduled runs; upload read-only previews; run or inspect Workspace graphs; or read logs and display outputs through AgentFlow HTTP APIs.
4
4
  ---
5
5
 
6
6
  # AgentFlow CLI
@@ -152,7 +152,8 @@ node <skill-dir>/scripts/agentflow-cli.mjs node-package-install \
152
152
  ```
153
153
 
154
154
  Before authoring a new code node, search the remote catalog. The result is structured JSON with the
155
- exact import specifier, input/output slots, content hash, and install hint:
155
+ exact import specifier, input/output slots, content hash, visibility, install hint, and server-owned
156
+ `useCount` / `installCount` / `uniqueUserCount` statistics:
156
157
 
157
158
  ```bash
158
159
  node <skill-dir>/scripts/agentflow-cli.mjs node-package-search --query "read csv"
@@ -161,6 +162,52 @@ node <skill-dir>/scripts/agentflow-cli.mjs node-package-search --query "read csv
161
162
  Prefer an existing suitable exact version. Install it and import its returned `specifier`; create a
162
163
  new local package only when no result meets the requested behavior.
163
164
 
165
+ ## Marketplace resources
166
+
167
+ List complete runnable Flow templates or node packages. Results are public resources by default,
168
+ ordered by `useCount DESC`, and always include the usage statistics in their JSON records:
169
+
170
+ ```bash
171
+ node <skill-dir>/scripts/agentflow-cli.mjs marketplace-list --kind flow
172
+ node <skill-dir>/scripts/agentflow-cli.mjs marketplace-list --kind node --query "用户校验"
173
+ node <skill-dir>/scripts/agentflow-cli.mjs marketplace-list --kind flow --owned
174
+ ```
175
+
176
+ `--owned` includes the current user's private resources. Creating or testing a Draft never exposes
177
+ it. Publishing to the Marketplace is a separate explicit operation; it defaults to public, strips
178
+ runtime state, and disables Scheduled Run entries in the immutable template snapshot:
179
+
180
+ ```bash
181
+ node <skill-dir>/scripts/agentflow-cli.mjs marketplace-flow-publish \
182
+ --flow-id user-import-validation \
183
+ --flow-source user \
184
+ --version 1.0.0 \
185
+ --visibility public
186
+ ```
187
+
188
+ Install a public or owned private template into the caller's personal space. Installation creates a
189
+ copy, records its Marketplace origin for later successful-run accounting, and keeps scheduling
190
+ disabled until the installer explicitly enables it:
191
+
192
+ ```bash
193
+ node <skill-dir>/scripts/agentflow-cli.mjs marketplace-flow-install \
194
+ --flow user-import-validation@1.0.0 \
195
+ --flow-id my-user-import-validation
196
+ ```
197
+
198
+ Owners can stop discovery and new installation without deleting already installed copies:
199
+
200
+ ```bash
201
+ node <skill-dir>/scripts/agentflow-cli.mjs marketplace-visibility \
202
+ --kind flow \
203
+ --resource user-import-validation@1.0.0 \
204
+ --visibility private
205
+ ```
206
+
207
+ The same command supports `--kind node`. Never infer Marketplace publication from ordinary
208
+ `publish-flow`: that command writes an editable personal/workspace/team Flow, while
209
+ `marketplace-flow-publish` creates a reusable immutable template.
210
+
164
211
  When a Flow already declares its dependencies, do not install packages one by one. Synchronize the
165
212
  whole Flow from its versioned imports:
166
213
 
@@ -409,7 +456,7 @@ The only admin write exception is audited version-membership repair. Read its st
409
456
 
410
457
  1. Check authorization with `config`. If missing, use `auth start`, give the URL to the user, then
411
458
  run `auth complete` only after the user approves it.
412
- 2. Use `list-workspace` or `list-flows` to discover Flow/Pipeline targets only. Use `node-package-search` before creating a new code node. Use `publish-flow` only after a local Flow has passed validation and the user has confirmed the preview.
459
+ 2. Use `list-workspace` or `list-flows` to discover Flow/Pipeline targets only. Use `marketplace-list` or `node-package-search` before creating a new Flow/node; both expose usage counts for comparison. Use `publish-flow` only after a local Flow has passed validation and the user has confirmed the preview, and use `marketplace-flow-publish` only after the user explicitly wants a reusable Marketplace snapshot.
413
460
  3. For a new user-authored Flow, prefer `draft-create` → `draft-run`/`draft-update` → explicit user confirmation → `draft-publish`. Keep `workspace-preview` for read-only sharing.
414
461
  4. Use `run` to start a published flow. If the task needs the generated page/text, inspect returned `displayOutputs` or call `display-outputs`.
415
462
  5. Use `status`, `list-run-by-workspace`, and `logs` when a run is active, failed, or needs debugging. Use `schedule-list` after every schedule mutation or scheduled publish.
@@ -1,4 +1,4 @@
1
- // Generated by scripts/build-agentflow-cli-skill-runtime.mjs for AgentFlow 0.1.161.
1
+ // Generated by scripts/build-agentflow-cli-skill-runtime.mjs for AgentFlow 0.1.162.
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1859,9 +1859,10 @@ function isDisplayDefinition(definitionId) {
1859
1859
  function isSemanticInputSlot(slot) {
1860
1860
  const name = String(slot?.name || "");
1861
1861
  const type2 = String(slot?.type || "");
1862
- return type2 === "node" || name === "prev" || name === "next" || CONTEXT_SLOT_NAMES.has(name);
1862
+ return type2 === "node" || type2 === "context" || name === "prev" || name === "next" || CONTEXT_SLOT_NAMES.has(name);
1863
1863
  }
1864
1864
  var CONTEXT_SLOT_NAMES = /* @__PURE__ */ new Set([
1865
+ "context",
1865
1866
  "skillsContext",
1866
1867
  "mcpContext",
1867
1868
  "knowledgeContext",
@@ -10650,7 +10651,7 @@ function normalizeNodeUiForSlots(value, inputs = [], outputs = []) {
10650
10651
 
10651
10652
  // bin/lib/node-package-manifest.mjs
10652
10653
  var NODE_PACKAGE_ENTRY = "index.mjs";
10653
- var SLOT_TYPES = /* @__PURE__ */ new Set(["text", "file", "bool", "node", "image", "json"]);
10654
+ var SLOT_TYPES = /* @__PURE__ */ new Set(["text", "file", "bool", "node", "image", "json", "context"]);
10654
10655
  function staticEval(node, where) {
10655
10656
  if (!node) throw new Error(`${where}: \u7A7A\u8868\u8FBE\u5F0F`);
10656
10657
  switch (node.type) {
@@ -12561,7 +12562,7 @@ function generateFlowSource(ir, opts = {}) {
12561
12562
  taken.add(name);
12562
12563
  return name;
12563
12564
  };
12564
- const flowApiRoots = ["agent", "control", "display", "file", "flow", "provide", "tool", "workspace"];
12565
+ const flowApiRoots = ["agent", "context", "control", "display", "file", "flow", "provide", "tool", "workspace"];
12565
12566
  const flowApiBinding = new Map(flowApiRoots.map((root) => [root, uniqueName(root, `${root}Api`)]));
12566
12567
  const apiCall = (name) => {
12567
12568
  const [root, ...tail] = String(name || "").split(".");
@@ -12654,6 +12655,12 @@ function generateFlowSource(ir, opts = {}) {
12654
12655
  const pinValue = (id, name, value) => {
12655
12656
  const text = String(value);
12656
12657
  if (slotTypeOf(id, name) === "bool" && (text === "true" || text === "false")) return text;
12658
+ if (slotTypeOf(id, name) === "json") {
12659
+ try {
12660
+ return JSON.stringify(JSON.parse(text), null, 2);
12661
+ } catch {
12662
+ }
12663
+ }
12657
12664
  return textArg(id, name, text);
12658
12665
  };
12659
12666
  function pinsObject(id, folds = /* @__PURE__ */ new Map()) {
@@ -12670,7 +12677,17 @@ function generateFlowSource(ir, opts = {}) {
12670
12677
  const key = isIdentifier(name) ? name : JSON.stringify(name);
12671
12678
  if (wired.has(name)) {
12672
12679
  const x2 = wired.get(name);
12673
- lines.push(`${key}: ${outVar.get(`${x2.from}|${x2.fromSlot}`) || `${x2.from}.${x2.fromSlot}`}`);
12680
+ const sourceDefinitionId = N[x2.from]?.definitionId;
12681
+ const bundleAlias = node.definitionId === "context_bundle" ? { knowledgeContext: "knowledge", skillsContext: "skills", workspaceContext: "workspace", mcpContext: "mcp" }[name] || "" : "";
12682
+ const contextResourceOutput = {
12683
+ context_knowledge: "knowledgeContext",
12684
+ context_skills: "skillsContext",
12685
+ context_workspace: "workspaceContext"
12686
+ }[sourceDefinitionId] || "";
12687
+ const directContextBundle = name === "context" && sourceDefinitionId === "context_bundle" && x2.fromSlot === "context";
12688
+ const renderedKey = bundleAlias || key;
12689
+ const renderedValue = directContextBundle || bundleAlias && contextResourceOutput === name && x2.fromSlot === name ? x2.from : outVar.get(`${x2.from}|${x2.fromSlot}`) || `${x2.from}.${x2.fromSlot}`;
12690
+ lines.push(`${renderedKey}: ${renderedValue}`);
12674
12691
  continue;
12675
12692
  }
12676
12693
  if (node.inputs[name] !== void 0) {
@@ -13196,6 +13213,36 @@ function parseFlowSource(source, opts = {}) {
13196
13213
  }
13197
13214
  return null;
13198
13215
  }
13216
+ function staticJsonOf(node) {
13217
+ if (!node) return { ok: false, value: null };
13218
+ if (node.type === "Literal") return { ok: true, value: node.value };
13219
+ if (node.type === "UnaryExpression" && (node.operator === "-" || node.operator === "+") && node.argument.type === "Literal" && typeof node.argument.value === "number") {
13220
+ return { ok: true, value: node.operator === "-" ? -node.argument.value : node.argument.value };
13221
+ }
13222
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
13223
+ return { ok: true, value: node.quasis[0].value.cooked };
13224
+ }
13225
+ if (node.type === "ArrayExpression") {
13226
+ const value = [];
13227
+ for (const item of node.elements) {
13228
+ const parsed = staticJsonOf(item);
13229
+ if (!parsed.ok) return { ok: false, value: null };
13230
+ value.push(parsed.value);
13231
+ }
13232
+ return { ok: true, value };
13233
+ }
13234
+ if (node.type === "ObjectExpression") {
13235
+ const value = {};
13236
+ for (const prop of node.properties) {
13237
+ if (prop.type !== "Property" || prop.computed || prop.kind !== "init") return { ok: false, value: null };
13238
+ const parsed = staticJsonOf(prop.value);
13239
+ if (!parsed.ok) return { ok: false, value: null };
13240
+ value[String(prop.key.name ?? prop.key.value ?? "")] = parsed.value;
13241
+ }
13242
+ return { ok: true, value };
13243
+ }
13244
+ return { ok: false, value: null };
13245
+ }
13199
13246
  const packageOf = /* @__PURE__ */ new Map();
13200
13247
  for (const stmt of ast.body) {
13201
13248
  if (stmt.type !== "ImportDeclaration") continue;
@@ -13264,6 +13311,8 @@ function parseFlowSource(source, opts = {}) {
13264
13311
  const def = definitionOf(definitionId);
13265
13312
  const defInputs = new Set(def.input.map((s) => s.name));
13266
13313
  const defOutputs = new Set(def.output.map((s) => s.name));
13314
+ const inputType = new Map(def.input.map((s) => [s.name, String(s.type || "text")]));
13315
+ const outputType = new Map(def.output.map((s) => [s.name, String(s.type || "text")]));
13267
13316
  const node = nodes[id];
13268
13317
  if (!obj || obj.type !== "ObjectExpression") return;
13269
13318
  for (const prop of obj.properties) {
@@ -13275,8 +13324,23 @@ function parseFlowSource(source, opts = {}) {
13275
13324
  unresolvedAt(prop, `${id}: \u5F15\u811A\u540D\u4E0D\u80FD\u662F\u52A8\u6001\u8868\u8FBE\u5F0F`);
13276
13325
  continue;
13277
13326
  }
13278
- const key = prop.key.name ?? prop.key.value;
13327
+ const rawKey = prop.key.name ?? prop.key.value;
13328
+ const key = definitionId === "context_bundle" ? { knowledge: "knowledgeContext", skills: "skillsContext", workspace: "workspaceContext", mcp: "mcpContext" }[rawKey] || rawKey : rawKey;
13279
13329
  const isCustom = !STD_SLOTS.has(key) && !defInputs.has(key);
13330
+ if (prop.value.type === "Identifier" && nodes[prop.value.name]) {
13331
+ const sourceDefinitionId = nodes[prop.value.name].definitionId;
13332
+ const contextOutput = {
13333
+ context_knowledge: "knowledgeContext",
13334
+ context_skills: "skillsContext",
13335
+ context_workspace: "workspaceContext",
13336
+ context_bundle: "context"
13337
+ }[sourceDefinitionId];
13338
+ if (contextOutput && (definitionId === "context_bundle" && key === contextOutput || key === "context" && sourceDefinitionId === "context_bundle")) {
13339
+ edges.push(`${prop.value.name}|${contextOutput}|${id}|${key}`);
13340
+ if (isCustom) node.extraIn.push(key);
13341
+ continue;
13342
+ }
13343
+ }
13280
13344
  const member = memberPath(prop.value);
13281
13345
  if (member) {
13282
13346
  edges.push(`${member[0]}|${member[1]}|${id}|${key}`);
@@ -13294,6 +13358,15 @@ function parseFlowSource(source, opts = {}) {
13294
13358
  continue;
13295
13359
  }
13296
13360
  const text = stringOf(prop.value);
13361
+ if (text === null && (inputType.get(key) === "json" || outputType.get(key) === "json")) {
13362
+ const parsed = staticJsonOf(prop.value);
13363
+ if (parsed.ok) {
13364
+ if (defOutputs.has(key) && !defInputs.has(key)) node.outputs[key] = JSON.stringify(parsed.value);
13365
+ else node.inputs[key] = JSON.stringify(parsed.value);
13366
+ if (isCustom) node.extraIn.push(key);
13367
+ continue;
13368
+ }
13369
+ }
13297
13370
  if (text === null) {
13298
13371
  unresolvedAt(prop, `${id}.${key}: \u5F15\u811A\u503C\u65E2\u4E0D\u662F\u4E0A\u6E38\u5F15\u7528\u4E5F\u4E0D\u662F\u5B57\u9762\u91CF`);
13299
13372
  continue;
@@ -14177,6 +14250,7 @@ var SLOT_TYPE_COLORS = Object.freeze({
14177
14250
  node: "#ff9800",
14178
14251
  bool: "#9c27b0",
14179
14252
  json: "#00bcd4",
14253
+ context: "#7c5cff",
14180
14254
  image: "#ec407a",
14181
14255
  any: "#9e9e9e"
14182
14256
  });
@@ -14225,6 +14299,15 @@ function declaredSlotType(node, kind, name) {
14225
14299
  if (kind === "input" && node?.inputTypes?.[name]) return String(node.inputTypes[name]);
14226
14300
  return "";
14227
14301
  }
14302
+ function parseJsonInput(node, name) {
14303
+ const raw = String(node?.inputs?.[name] || "").trim();
14304
+ if (!raw) return { value: null, error: "\u4E3A\u7A7A" };
14305
+ try {
14306
+ return { value: JSON.parse(raw), error: "" };
14307
+ } catch (error) {
14308
+ return { value: null, error: error.message };
14309
+ }
14310
+ }
14228
14311
  function walk(node, visit) {
14229
14312
  if (!node || typeof node !== "object") return;
14230
14313
  if (node.type) visit(node);
@@ -14325,6 +14408,29 @@ function lintFlowDir(flowDir, opts = {}) {
14325
14408
  if (definitionId === "control_subflow_call" && !subflows[node.attrs?.subflowId]) {
14326
14409
  errors.push(`${id}: \u5F15\u7528\u7684\u5B50\u6D41\u7A0B ${node.attrs?.subflowId || "(empty)"} \u4E0D\u5B58\u5728`);
14327
14410
  }
14411
+ if (definitionId === "context_knowledge") {
14412
+ const parsed = parseJsonInput(node, "workspaceIds");
14413
+ if (parsed.error) errors.push(`${id}: context.knowledge workspaceIds \u5FC5\u987B\u662F JSON \u6570\u7EC4\uFF08${parsed.error}\uFF09`);
14414
+ else if (!Array.isArray(parsed.value) || parsed.value.length === 0) errors.push(`${id}: context.knowledge \u81F3\u5C11\u9009\u62E9\u4E00\u4E2A Workspace ID`);
14415
+ else if (parsed.value.some((value) => typeof value !== "string" || !String(value).trim())) errors.push(`${id}: context.knowledge workspaceIds \u53EA\u80FD\u5305\u542B\u975E\u7A7A\u5B57\u7B26\u4E32 ID`);
14416
+ }
14417
+ if (definitionId === "context_skills") {
14418
+ const parsed = parseJsonInput(node, "skills");
14419
+ if (parsed.error) errors.push(`${id}: context.skills skills \u5FC5\u987B\u662F JSON \u6570\u7EC4\uFF08${parsed.error}\uFF09`);
14420
+ else if (!Array.isArray(parsed.value) || parsed.value.length === 0) errors.push(`${id}: context.skills \u81F3\u5C11\u58F0\u660E\u4E00\u4E2A skill`);
14421
+ }
14422
+ if (definitionId === "context_workspace") {
14423
+ const workspaceId = String(node.inputs?.workspaceId || "current").trim();
14424
+ const access = String(node.inputs?.access || "read-write").trim().toLowerCase();
14425
+ if (!workspaceId) errors.push(`${id}: context.workspace workspaceId \u4E0D\u80FD\u4E3A\u7A7A`);
14426
+ if (!["read-only", "read-write"].includes(access)) errors.push(`${id}: context.workspace access \u53EA\u80FD\u662F read-only \u6216 read-write`);
14427
+ }
14428
+ if (definitionId === "context_bundle") {
14429
+ const incoming = ir.edges.filter((edge) => edge.split("|")[2] === id && edge.split("|")[3] !== "prev");
14430
+ if (!incoming.length) errors.push(`${id}: context.bundle \u81F3\u5C11\u8FDE\u63A5\u4E00\u4E2A Context \u8D44\u6E90`);
14431
+ const literal3 = Object.entries(node.inputs || {}).filter(([, value]) => String(value || "").trim());
14432
+ if (literal3.length) errors.push(`${id}: context.bundle \u53EA\u80FD\u8FDE\u63A5\u8D44\u6E90\u8282\u70B9\uFF0C\u4E0D\u80FD\u5185\u5D4C Context \u6B63\u6587`);
14433
+ }
14328
14434
  if (definitionId === "control_while") {
14329
14435
  const conditionId = String(node.attrs?.conditionSubflowId || "");
14330
14436
  const bodyId = String(node.attrs?.bodySubflowId || "");
@@ -14352,9 +14458,11 @@ function lintFlowDir(flowDir, opts = {}) {
14352
14458
  }
14353
14459
  if (body && !body.outputs?.state) errors.push(`${id}: Body \u5B50\u6D41\u7A0B ${bodyId} \u7F3A\u5C11\u8F93\u51FA state`);
14354
14460
  for (const [contract, name, expected] of [
14461
+ [condition?.inputs, "context", "context"],
14355
14462
  [condition?.inputs, "state", "json"],
14356
14463
  [condition?.inputs, "iteration", "text"],
14357
14464
  [condition?.outputs, "decision", "text"],
14465
+ [body?.inputs, "context", "context"],
14358
14466
  [body?.inputs, "state", "json"],
14359
14467
  [body?.inputs, "iteration", "text"],
14360
14468
  [body?.inputs, "idempotencyKey", "text"],
@@ -14386,6 +14494,12 @@ function lintFlowDir(flowDir, opts = {}) {
14386
14494
  for (const slot of node.extraIn) if (!defIn.has(slot)) errors.push(`${id}[${definitionId}]: \u4E0D\u5B58\u5728\u7684\u8F93\u5165\u69FD "${slot}"`);
14387
14495
  for (const slot of node.extraOut) if (!defOut.has(slot)) errors.push(`${id}[${definitionId}]: \u4E0D\u5B58\u5728\u7684\u8F93\u51FA\u69FD "${slot}"`);
14388
14496
  }
14497
+ if (definitionId === "agent_subAgent") {
14498
+ const incomingNames = new Set(ir.edges.map((edge) => edge.split("|")).filter((parts) => parts[2] === id).map((parts) => parts[3]));
14499
+ if (incomingNames.has("context") && ["knowledgeContext", "skillsContext", "workspaceContext", "mcpContext"].some((name) => incomingNames.has(name))) {
14500
+ warnings.push(`${id}: \u5DF2\u8FDE\u63A5 context Bundle\uFF0C\u4E0D\u8981\u518D\u91CD\u590D\u8FDE\u63A5\u65E7 Context \u6587\u672C\u5F15\u811A`);
14501
+ }
14502
+ }
14389
14503
  }
14390
14504
  const inputSeen = /* @__PURE__ */ new Map();
14391
14505
  const adjacency = /* @__PURE__ */ new Map();
@@ -1,12 +1,16 @@
1
1
  ---
2
2
  # 内置节点:子 Agent
3
3
  runtime: native
4
- description: 利用子 Agent 执行任务;可接收 knowledgeContext 读取知识库,可接收 workspaceContext 切换执行工作区,并接收 skillsContext / mcpContext 注入已加载 skills 与 MCP 工具清单。
4
+ description: 利用子 Agent 执行任务;新流程优先接收一个强类型 context Bundle。knowledgeContextworkspaceContextskillsContextmcpContext 保留为旧流程兼容引脚。
5
5
  displayName: 子 Agent
6
6
  input:
7
7
  - type: node
8
8
  name: prev
9
9
  default: ""
10
+ - type: context
11
+ name: context
12
+ default: ""
13
+ showOnNode: true
10
14
  - type: text
11
15
  name: workspaceContext
12
16
  default: ""
@@ -0,0 +1,44 @@
1
+ ---
2
+ # Built-in node: Context Bundle
3
+ runtime: native
4
+ type: provide
5
+ description: Compose knowledge, skills, workspace, and MCP resources into one strongly typed Context value that can cross Agent, Subflow, and While boundaries.
6
+ displayName: Context Bundle
7
+ ui:
8
+ card:
9
+ template: context-resource
10
+ icon: hub
11
+ tone: purple
12
+ sections:
13
+ - type: context
14
+ label: Context resources
15
+ inputs:
16
+ - name: knowledgeContext
17
+ label: Knowledge
18
+ - name: skillsContext
19
+ label: Skills
20
+ - name: workspaceContext
21
+ label: Workspace
22
+ - name: mcpContext
23
+ label: MCP
24
+ input:
25
+ - type: text
26
+ name: knowledgeContext
27
+ default: ""
28
+ - type: text
29
+ name: skillsContext
30
+ default: ""
31
+ - type: text
32
+ name: workspaceContext
33
+ default: ""
34
+ - type: text
35
+ name: mcpContext
36
+ default: ""
37
+ output:
38
+ - type: context
39
+ name: context
40
+ default: ""
41
+ required: true
42
+ showOnNode: true
43
+ ---
44
+ Compose Context resources without putting their content or credentials into the business data flow.
@@ -0,0 +1,29 @@
1
+ ---
2
+ # Built-in node: Knowledge Context Resource
3
+ runtime: native
4
+ type: provide
5
+ description: Select one or more read-only knowledge sources by their authenticated Workspace catalog IDs. The runtime resolves IDs from the same catalog as GET /api/workspaces; paths and credentials are not stored in Flow DSL.
6
+ displayName: Knowledge Context
7
+ ui:
8
+ card:
9
+ template: context-resource
10
+ icon: menu_book
11
+ tone: cyan
12
+ sections:
13
+ - type: binding
14
+ label: Workspace IDs
15
+ input: workspaceIds
16
+ input:
17
+ - type: json
18
+ name: workspaceIds
19
+ default: "[]"
20
+ required: true
21
+ showOnNode: false
22
+ output:
23
+ - type: text
24
+ name: knowledgeContext
25
+ default: ""
26
+ required: true
27
+ showOnNode: true
28
+ ---
29
+ Resolve authenticated Workspace catalog IDs into read-only knowledge sources for downstream Context consumers.
@@ -0,0 +1,29 @@
1
+ ---
2
+ # Built-in node: Skills Context Resource
3
+ runtime: native
4
+ type: provide
5
+ description: Declare versioned skills as a reusable Context resource. This is a data resource and does not participate in the prev/next control chain.
6
+ displayName: Skills Context
7
+ ui:
8
+ card:
9
+ template: context-resource
10
+ icon: extension
11
+ tone: purple
12
+ sections:
13
+ - type: binding
14
+ label: Skills
15
+ input: skills
16
+ input:
17
+ - type: json
18
+ name: skills
19
+ default: "[]"
20
+ required: true
21
+ showOnNode: false
22
+ output:
23
+ - type: text
24
+ name: skillsContext
25
+ default: ""
26
+ required: true
27
+ showOnNode: true
28
+ ---
29
+ Load the declared skills for downstream Context consumers.
@@ -0,0 +1,36 @@
1
+ ---
2
+ # Built-in node: Workspace Context Resource
3
+ runtime: native
4
+ type: provide
5
+ description: Bind one authenticated Workspace catalog entry as execution context. The Flow stores only workspaceId; paths and credentials remain runtime-owned.
6
+ displayName: Workspace Context
7
+ ui:
8
+ card:
9
+ template: context-resource
10
+ icon: folder_open
11
+ tone: green
12
+ sections:
13
+ - type: binding
14
+ label: Workspace ID
15
+ input: workspaceId
16
+ - type: binding
17
+ label: Access
18
+ input: access
19
+ input:
20
+ - type: text
21
+ name: workspaceId
22
+ default: current
23
+ required: true
24
+ showOnNode: false
25
+ - type: text
26
+ name: access
27
+ default: read-write
28
+ showOnNode: false
29
+ output:
30
+ - type: text
31
+ name: workspaceContext
32
+ default: ""
33
+ required: true
34
+ showOnNode: true
35
+ ---
36
+ Resolve the runtime-owned Workspace catalog entry for downstream Context consumers.
@@ -8,6 +8,9 @@ description: |
8
8
 
9
9
  Preferred DSL form:
10
10
  `control.while("Advance", { state, maxIterations, timeout }, conditionFlow, bodyFlow)`.
11
+ An optional typed `context` input is captured once before iteration and forwarded only to
12
+ Condition/Body subflows that explicitly declare `flow.input("context", "context")`. Context is
13
+ never copied into business state, history, or checkpoints.
11
14
  Condition must accept `state` and `iteration`, and return `decision` plus optional `summary`.
12
15
  Only `continue` invokes Body. Body must accept `state`, `iteration`, and `idempotencyKey`, and
13
16
  return the next `state` plus optional `summary`. `wait`, `done`, and `fail` skip Body.
@@ -83,6 +86,10 @@ input:
83
86
  - type: node
84
87
  name: prev
85
88
  default: ""
89
+ - type: context
90
+ name: context
91
+ default: ""
92
+ showOnNode: true
86
93
  - type: json
87
94
  name: state
88
95
  default: "null"
@@ -7,9 +7,10 @@ description: |
7
7
  - `repoPath` is required unless `gitContext.repoPath` is connected.
8
8
  - `workspaceContext` is required so the node can preserve the previous execution context.
9
9
  - `branch` is optional. When empty, AgentFlow creates a detached worktree at the current HEAD.
10
- - `worktreePath` is optional. When empty, AgentFlow creates a temporary worktree under the current run temp directory.
11
- - A worktree created by this node during the current Workspace run is removed when the run finishes or is stopped, even when `worktreePath` is explicitly set.
12
- - Existing registered worktrees under the current flow workspace are also removed after the run, covering leftovers from previous interrupted runs.
10
+ - `worktreePath` is optional. When empty, AgentFlow creates a managed execution worktree under `.workspace/agentflow/run-workspaces/`, separate from node temp files and durable `outputs/` artifacts.
11
+ - A `wait` checkpoint retains the execution worktree. The next run reuses the retained output path so loop state and code changes remain available while resuming.
12
+ - On terminal completion or stop, AgentFlow removes clean managed worktrees. Dirty worktrees are preserved with a warning instead of being force-deleted.
13
+ - Existing registered worktrees under the current flow workspace are managed by the same lifecycle policy.
13
14
  - Existing registered worktrees outside the current flow workspace are reused and not removed automatically unless this run created them.
14
15
  - Existing worktree paths are reused only when they are registered by `git worktree list` for the given repo.
15
16
  - `pruneMissing` defaults to true. When Git has a registered worktree whose directory is missing, AgentFlow runs `git worktree prune` before adding it again.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldwangai/agentflow-skill-runtime",
3
- "version": "0.1.161",
3
+ "version": "0.1.162",
4
4
  "type": "module",
5
5
  "agentflowSkillRuntime": 1
6
6
  }
@@ -78,6 +78,10 @@ Commands:
78
78
  node-package-publish --file <nodePackageDir>
79
79
  node-package-install --node <id>@<version> [--workspace-root <dir>]
80
80
  node-package-sync --flow <flowDir|workspace.flow.js> [--workspace-root <dir>]
81
+ marketplace-list [--kind flow|node] [--owned] [--query <text>]
82
+ marketplace-flow-publish --flow-id <id> [--flow-source user] [--version 1.0.0] [--visibility public|private]
83
+ marketplace-flow-install --flow <id>@<version> [--flow-id <targetId>]
84
+ marketplace-visibility --kind flow|node --resource <id>@<version> --visibility public|private
81
85
  pull-flow --flow-id <id> [--flow-source user] [--output <flowDir>] [--workspace-root <dir>] [--replace]
82
86
  publish-flow --flow-id <id> --file <flowDir|workspace.flow.js|flow.yaml> [--target-space personal|workspace|team] [--schedule enabled|disabled|preserve] [--with-dependencies] [--replace]
83
87
  get-graph --flow-id <id> [--flow-source user]
@@ -371,6 +375,11 @@ function searchNodePackageCatalog(nodes, queryText, limitValue) {
371
375
  inputs: node.inputs || node.input || [],
372
376
  outputs: node.outputs || node.output || [],
373
377
  contentSha256: node.contentSha256 || "",
378
+ visibility: node.visibility || "public",
379
+ useCount: Number(node.useCount || 0),
380
+ installCount: Number(node.installCount || 0),
381
+ uniqueUserCount: Number(node.uniqueUserCount || 0),
382
+ lastUsedAt: node.lastUsedAt || "",
374
383
  installCommand: `node-package-install --node ${node.id}@${node.version}`,
375
384
  }));
376
385
  }
@@ -1152,6 +1161,61 @@ async function main() {
1152
1161
  return;
1153
1162
  }
1154
1163
 
1164
+ if (command === "marketplace-list") {
1165
+ const kind = String(option(args, "kind") || "flow").trim().toLowerCase();
1166
+ if (kind !== "flow" && kind !== "node") throw new Error("--kind must be flow or node.");
1167
+ const result = await httpJson(args, `/api/marketplace/resources${query({
1168
+ kind,
1169
+ scope: args.owned === true ? "owned" : "",
1170
+ q: option(args, "query") || option(args, "q"),
1171
+ sort: "useCount",
1172
+ order: "desc",
1173
+ })}`);
1174
+ printJson(result);
1175
+ return;
1176
+ }
1177
+
1178
+ if (command === "marketplace-flow-publish") {
1179
+ const flowId = requireFlowId(args);
1180
+ const visibility = String(option(args, "visibility") || "public").trim().toLowerCase();
1181
+ if (visibility !== "public" && visibility !== "private") throw new Error("--visibility must be public or private.");
1182
+ printJson(await httpJson(args, "/api/marketplace/flows/publish", {
1183
+ method: "POST",
1184
+ body: {
1185
+ flowId,
1186
+ flowSource: option(args, "flow-source") || "user",
1187
+ id: option(args, "marketplace-id") || flowId,
1188
+ displayName: option(args, "display-name") || flowId,
1189
+ description: option(args, "description") || "",
1190
+ version: option(args, "version") || "1.0.0",
1191
+ visibility,
1192
+ },
1193
+ }));
1194
+ return;
1195
+ }
1196
+
1197
+ if (command === "marketplace-flow-install") {
1198
+ const spec = parseNodePackageSpec(option(args, "flow"));
1199
+ printJson(await httpJson(args, "/api/marketplace/flows/install", {
1200
+ method: "POST",
1201
+ body: { id: spec.id, version: spec.version, flowId: option(args, "flow-id") || spec.id },
1202
+ }));
1203
+ return;
1204
+ }
1205
+
1206
+ if (command === "marketplace-visibility") {
1207
+ const kind = String(option(args, "kind") || "").trim().toLowerCase();
1208
+ if (kind !== "flow" && kind !== "node") throw new Error("--kind must be flow or node.");
1209
+ const spec = parseNodePackageSpec(option(args, "resource"));
1210
+ const visibility = String(option(args, "visibility") || "").trim().toLowerCase();
1211
+ if (visibility !== "public" && visibility !== "private") throw new Error("--visibility must be public or private.");
1212
+ printJson(await httpJson(args, "/api/marketplace/visibility", {
1213
+ method: "PATCH",
1214
+ body: { kind, id: spec.id, version: spec.version, visibility },
1215
+ }));
1216
+ return;
1217
+ }
1218
+
1155
1219
  if (command === "pull-flow") {
1156
1220
  const result = await pullFlow(args);
1157
1221
  printJson(result);