@fieldwangai/agentflow 0.1.95 → 0.1.97

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.
@@ -3923,6 +3923,31 @@ function workspaceSafeNodeOutputRelPath(value) {
3923
3923
  return clean;
3924
3924
  }
3925
3925
 
3926
+ function workspaceDescribeNodeOutputsDir(nodeRunDir, maxEntries = 30) {
3927
+ const outputsDir = path.resolve(nodeRunDir || "", "outputs");
3928
+ if (!nodeRunDir || !fs.existsSync(outputsDir)) return "Current node outputs directory is missing.";
3929
+ const entries = [];
3930
+ const walk = (dir, rel = "") => {
3931
+ if (entries.length >= maxEntries) return;
3932
+ let children = [];
3933
+ try {
3934
+ children = fs.readdirSync(dir, { withFileTypes: true });
3935
+ } catch {
3936
+ return;
3937
+ }
3938
+ for (const child of children) {
3939
+ if (entries.length >= maxEntries) break;
3940
+ const childRel = rel ? path.posix.join(rel, child.name) : child.name;
3941
+ entries.push(child.isDirectory() ? `${childRel}/` : childRel);
3942
+ if (child.isDirectory()) walk(path.join(dir, child.name), childRel);
3943
+ }
3944
+ };
3945
+ walk(outputsDir);
3946
+ if (!entries.length) return "Current node outputs directory is empty.";
3947
+ const suffix = entries.length >= maxEntries ? `\n...showing first ${maxEntries} entries` : "";
3948
+ return `Current node outputs entries:\n${entries.map((entry) => `- outputs/${entry}`).join("\n")}${suffix}`;
3949
+ }
3950
+
3926
3951
  function workspacePublishNodeOutputFile(runPackage, relPath) {
3927
3952
  if (!String(relPath || "").trim()) return "";
3928
3953
  const clean = workspaceSafeNodeOutputRelPath(relPath);
@@ -3939,6 +3964,7 @@ function workspacePublishNodeOutputFile(runPackage, relPath) {
3939
3964
  throw new Error(
3940
3965
  `Agent returned resultFile but did not create it under this node's outputs: ${clean}\n` +
3941
3966
  `Expected file: ${src}\n` +
3967
+ `${workspaceDescribeNodeOutputsDir(nodeRunDir)}\n` +
3942
3968
  `Write outputs using a relative path from the node cwd, or use AGENTFLOW_OUTPUTS_DIR.`
3943
3969
  );
3944
3970
  }
@@ -4160,6 +4186,8 @@ function workspaceOutputProtocolRequirements(graph, nodeId) {
4160
4186
  "",
4161
4187
  `请把${resultKindText}结果写入 \`${resultFile}\`。${resultGuidance}`,
4162
4188
  `必须先在当前执行目录下创建 \`${resultFile}\`,不要写到绝对路径或其它 run 目录;然后再返回下面的 agentflow envelope。`,
4189
+ `只在最终回复里写 \`resultFile: ${resultFile}\` 不会创建文件;必须通过工具或脚本实际写入该文件。`,
4190
+ `返回前请确认 \`${resultFile}\` 已存在且非空(例如执行 \`test -s ${resultFile}\` 或等价检查);如果无法创建文件,不要返回成功 envelope。`,
4163
4191
  downstreamInputRequirements ? `\n${downstreamInputRequirements}` : "",
4164
4192
  ...(slots.length ? [`额外输出:${slots.map((name) => `\`${name}\``).join("、")}。短值可写在 \`outParams\`,文件值写成 \`outParams.<name>File\`。`] : []),
4165
4193
  "最终只输出下面的 agentflow envelope,不要输出解释、进度或其它文字:",
@@ -4347,13 +4375,17 @@ function workspaceCachedOutputValueExists(value, scopedRoot = "") {
4347
4375
  }
4348
4376
 
4349
4377
  function workspaceUpstreamText(graph, nodeId, outputs, scopedRoot = "") {
4378
+ const contentEdge = workspaceContentInputEdge(graph, nodeId);
4379
+ if (!contentEdge) return "";
4380
+ return workspaceOutputSlotValueForEdge(graph, outputs, contentEdge, scopedRoot);
4381
+ }
4382
+
4383
+ function workspaceContentInputEdge(graph, nodeId) {
4350
4384
  const edges = Array.isArray(graph?.edges) ? graph.edges : [];
4351
4385
  const incoming = edges
4352
4386
  .filter((edge) => String(edge?.target || "") === String(nodeId))
4353
4387
  .filter((edge) => !isWorkspaceSemanticInputSlot(workspaceTargetSlotForEdge(graph, edge)));
4354
- const contentEdge = incoming.find((edge) => String(edge?.targetHandle || "") === "input-1") || incoming[0];
4355
- if (!contentEdge) return "";
4356
- return workspaceOutputSlotValueForEdge(graph, outputs, contentEdge, scopedRoot);
4388
+ return incoming.find((edge) => String(edge?.targetHandle || "") === "input-1") || incoming[0] || null;
4357
4389
  }
4358
4390
 
4359
4391
  function workspaceHandleIndex(handle, prefix) {
@@ -6089,6 +6121,11 @@ async function runWorkspaceGraph(root, scopedRoot, payload, userCtx = {}, opts =
6089
6121
  }
6090
6122
 
6091
6123
  if (workspaceDisplayKind(defId)) {
6124
+ if (!workspaceContentInputEdge(graph, nodeId)) {
6125
+ emit({ type: "status", nodeId, line: "Display unchanged: no content input edge" });
6126
+ emit({ type: "node-done", nodeId, definitionId: defId, unchanged: true });
6127
+ continue;
6128
+ }
6092
6129
  const content = workspaceUpstreamText(graph, nodeId, outputs, scopedRoot);
6093
6130
  graph.instances[nodeId] = workspaceWriteDisplayContent(instance, content);
6094
6131
  const updatedDisplays = publishNodeOutput(nodeId, content);
@@ -7048,6 +7085,24 @@ function prdWorkflowSafeStateId(value) {
7048
7085
  .slice(0, 128) || "unknown";
7049
7086
  }
7050
7087
 
7088
+ function prdWorkflowReviewIdFromRequest(tapdId, payload = {}, durability = "temporary") {
7089
+ if (durability === "temporary") return `r-${crypto.randomBytes(5).toString("hex")}`;
7090
+ const requested = String(payload.reviewId || payload.review_id || "").trim();
7091
+ if (!requested) return `review_${Date.now().toString(36)}_${crypto.randomBytes(4).toString("hex")}`;
7092
+ const safeRequested = prdWorkflowSafeStateId(requested);
7093
+ if (safeRequested.length <= 48) return safeRequested;
7094
+ const stage = String(payload.stage || payload.stageKey || payload.stage_key || "review").trim();
7095
+ const action = String(payload.action || payload.actionId || payload.action_id || "").trim();
7096
+ const issueKey = String(payload.issueKey || payload.issue_key || payload.issue || "").trim();
7097
+ const stageHead = prdWorkflowSafeStateId(stage.split(":")[0] || stage || "review").slice(0, 20);
7098
+ const digest = crypto
7099
+ .createHash("sha256")
7100
+ .update(JSON.stringify({ tapdId: String(tapdId || ""), requested, stage, action, issueKey }))
7101
+ .digest("hex")
7102
+ .slice(0, 10);
7103
+ return prdWorkflowSafeStateId(["review", tapdId, stageHead, digest].filter(Boolean).join("-")).slice(0, 64);
7104
+ }
7105
+
7051
7106
  function prdWorkflowStatePath(scopedRoot, tapdId) {
7052
7107
  const rootDir = scopedRoot || process.cwd();
7053
7108
  return path.join(rootDir, ".workspace", "prd-flow", "workflow-state", `${prdWorkflowSafeStateId(tapdId)}.json`);
@@ -7436,13 +7491,10 @@ function prdWorkflowReviewHtml(title, markdown, meta = {}) {
7436
7491
  function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "") {
7437
7492
  const content = String(payload.markdown || payload.content || payload.rawOutput || "").slice(0, 500000);
7438
7493
  if (!content.trim()) throw new Error("Missing review markdown");
7439
- const requestedReviewId = String(payload.reviewId || payload.review_id || "").trim();
7440
- const reviewId = requestedReviewId
7441
- ? prdWorkflowSafeStateId(requestedReviewId)
7442
- : `review_${Date.now().toString(36)}_${crypto.randomBytes(4).toString("hex")}`;
7443
- const paths = prdWorkflowReviewPaths(scopedRoot, tapdId, reviewId);
7444
7494
  const title = String(payload.title || payload.label || "PRD Workflow Review").trim().slice(0, 160) || "PRD Workflow Review";
7445
7495
  const durability = String(payload.durability || (payload.durable === true || payload.permanent === true ? "durable" : "temporary")).trim().toLowerCase() || "temporary";
7496
+ const reviewId = prdWorkflowReviewIdFromRequest(tapdId, payload, durability);
7497
+ const paths = prdWorkflowReviewPaths(scopedRoot, tapdId, reviewId);
7446
7498
  const ttlDaysRaw = Number(payload.ttlDays || payload.ttl_days || (durability === "temporary" ? 7 : 0));
7447
7499
  const ttlDays = Number.isFinite(ttlDaysRaw) && ttlDaysRaw > 0 ? ttlDaysRaw : 0;
7448
7500
  const createdAt = new Date();
@@ -10342,7 +10394,7 @@ export function startUiServer({
10342
10394
  const review = prdWorkflowCreateReview(scopedRoot, tapdId, payload, serverPublicBaseUrl(req, host, uiPort, payload));
10343
10395
  const query = new URLSearchParams();
10344
10396
  if (flowId) query.set("flowId", flowId);
10345
- if (flowSource) query.set("flowSource", flowSource);
10397
+ if (flowId && flowSource && flowSource !== "user") query.set("flowSource", flowSource);
10346
10398
  if (archived) query.set("archived", "1");
10347
10399
  const reviewUrl = query.toString() ? `${review.url}?${query.toString()}` : review.url;
10348
10400
  const durability = review.durability || "temporary";