@fieldwangai/agentflow 0.1.81 → 0.1.83

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.
@@ -225,6 +225,7 @@ export function loadGitWorktree({ repoPath, branch = "", worktreePath = "", pipe
225
225
  const repoRoot = resolveGitRepoRoot(repoPath);
226
226
  const wantedBranch = String(branch || "").trim();
227
227
  const target = path.resolve(worktreePath || defaultWorktreePath(pipelineWorkspace || repoRoot, repoRoot, wantedBranch));
228
+ let created = false;
228
229
 
229
230
  if (wantedBranch && !branchExists(repoRoot, wantedBranch)) {
230
231
  throw new Error(`branch does not exist in repoPath: ${wantedBranch}`);
@@ -262,6 +263,7 @@ export function loadGitWorktree({ repoPath, branch = "", worktreePath = "", pipe
262
263
  if (result.status !== 0) {
263
264
  throw new Error(`git worktree add failed: ${result.stderr || result.stdout}`);
264
265
  }
266
+ created = true;
265
267
  }
266
268
 
267
269
  const actualBranch = actualWorktreeBranch(target);
@@ -271,6 +273,7 @@ export function loadGitWorktree({ repoPath, branch = "", worktreePath = "", pipe
271
273
  worktreePath: target,
272
274
  branch: actualBranch,
273
275
  commit,
276
+ created,
274
277
  };
275
278
  }
276
279
 
@@ -1481,7 +1481,7 @@ function readWorkspaceGraph(workspaceRoot) {
1481
1481
  }
1482
1482
 
1483
1483
  const DISPLAY_SHARE_FILENAME = "display-shares.json";
1484
- const DISPLAY_SHARE_TTL_MS = 24 * 60 * 60 * 1000;
1484
+ const DISPLAY_SHARE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
1485
1485
 
1486
1486
  function displaySharesPath() {
1487
1487
  return path.join(getAgentflowDataRoot(), DISPLAY_SHARE_FILENAME);
@@ -4091,6 +4091,76 @@ function workspaceUpstreamMcpBlocks(graph, nodeId, outputs) {
4091
4091
  return Array.from(new Set(blocks)).join("\n\n---\n\n");
4092
4092
  }
4093
4093
 
4094
+ function workspaceSemanticInputText(graph, nodeId, outputs, name, scopedRoot = "") {
4095
+ const targetName = String(name || "").trim();
4096
+ if (!targetName) return "";
4097
+ const edges = Array.isArray(graph?.edges) ? graph.edges : [];
4098
+ const edge = edges
4099
+ .filter((item) => String(item?.target || "") === String(nodeId))
4100
+ .find((item) => String(workspaceTargetSlotForEdge(graph, item)?.name || "") === targetName);
4101
+ if (edge) return workspaceOutputSlotValueForEdge(graph, outputs, edge, scopedRoot);
4102
+ const instance = graph?.instances && typeof graph.instances === "object" ? graph.instances[String(nodeId || "")] : null;
4103
+ return workspaceSlotValue(workspaceSlotByName(instance, targetName));
4104
+ }
4105
+
4106
+ function workspaceContextObjectFromText(text, baseCwd, scopedRoot) {
4107
+ const raw = String(text || "").trim();
4108
+ if (!raw) return null;
4109
+ const parsed = parseJsonText(raw, null);
4110
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
4111
+ const resolved = workspaceResolvePath(baseCwd || scopedRoot, raw) || raw;
4112
+ return {
4113
+ version: 1,
4114
+ label: "workspace",
4115
+ cwd: resolved,
4116
+ workspaceRoot: resolved,
4117
+ pipelineWorkspace: scopedRoot ? path.resolve(scopedRoot) : "",
4118
+ previous: null,
4119
+ };
4120
+ }
4121
+
4122
+ function workspaceNodeWorkspaceContextBlock(graph, nodeId, outputs, scopedRoot = "", logicalCwd = "") {
4123
+ const root = scopedRoot ? path.resolve(scopedRoot) : "";
4124
+ const cwd = logicalCwd ? path.resolve(logicalCwd) : root;
4125
+ const workspaceText = workspaceSemanticInputText(graph, nodeId, outputs, "workspaceContext", scopedRoot);
4126
+ let workspaceContext = workspaceContextObjectFromText(workspaceText, cwd || root, scopedRoot);
4127
+ if (!workspaceContext && cwd && root && cwd !== root) {
4128
+ workspaceContext = {
4129
+ version: 1,
4130
+ label: "workspace",
4131
+ cwd,
4132
+ workspaceRoot: cwd,
4133
+ pipelineWorkspace: root,
4134
+ previous: null,
4135
+ };
4136
+ }
4137
+ const gitContext = normalizeGitContext(workspaceSemanticInputText(graph, nodeId, outputs, "gitContext", scopedRoot));
4138
+ if (!workspaceContext && !gitContext) return "";
4139
+
4140
+ const contextCwd = workspaceContext?.cwd ? path.resolve(String(workspaceContext.cwd)) : "";
4141
+ const workspaceRoot = workspaceContext?.workspaceRoot ? path.resolve(String(workspaceContext.workspaceRoot)) : contextCwd;
4142
+ const pipelineWorkspace = workspaceContext?.pipelineWorkspace ? path.resolve(String(workspaceContext.pipelineWorkspace)) : root;
4143
+ const label = String(workspaceContext?.label || "").trim();
4144
+ const lines = [
4145
+ "## Workspace 上下文",
4146
+ "",
4147
+ "当前 Agent 仍在独立节点目录中运行,文件边界以“文件边界”章节为准。",
4148
+ label ? `- 名称:${label}` : "",
4149
+ contextCwd ? `- 当前工作目录上下文:\`${contextCwd}\`` : "",
4150
+ workspaceRoot && workspaceRoot !== contextCwd ? `- workspaceRoot:\`${workspaceRoot}\`` : "",
4151
+ pipelineWorkspace ? `- 流程目录:\`${pipelineWorkspace}\`` : "",
4152
+ gitContext?.repoPath ? `- Git repoPath:\`${gitContext.repoPath}\`` : "",
4153
+ gitContext?.worktreePath ? `- Git worktreePath:\`${gitContext.worktreePath}\`` : "",
4154
+ gitContext?.branch ? `- Git branch:\`${gitContext.branch}\`` : "",
4155
+ gitContext?.commit ? `- Git commit:\`${gitContext.commit}\`` : "",
4156
+ "",
4157
+ "使用要求:",
4158
+ "- 读取、搜索、分析当前项目或资料时,优先从“当前工作目录上下文”开始;不要把节点的“当前执行目录”误认为项目根目录。",
4159
+ "- 临时文件和正式产物仍必须按“文件边界”写入本节点的 `tmp/` 与 `outputs/`。",
4160
+ ].filter((line) => line !== "");
4161
+ return lines.join("\n");
4162
+ }
4163
+
4094
4164
  function mergeWorkspaceSkillBlocks(...values) {
4095
4165
  const blocks = values
4096
4166
  .map((value) => String(value || ""))
@@ -4206,7 +4276,7 @@ function workspaceUpdateDirectDisplays(graph, sourceId, content, outputs = null,
4206
4276
  return updated;
4207
4277
  }
4208
4278
 
4209
- function workspaceNodePrompt(graph, nodeId, upstreamText, skillsBlock, mcpBlock = "", inputValues = {}, nodeTmpDir = "", implementationBlock = "") {
4279
+ function workspaceNodePrompt(graph, nodeId, upstreamText, skillsBlock, mcpBlock = "", inputValues = {}, nodeTmpDir = "", implementationBlock = "", workspaceContextBlock = "") {
4210
4280
  const instance = graph.instances[nodeId] || {};
4211
4281
  const body = workspaceResolveBodyPlaceholders(instance.body || "", inputValues).trim();
4212
4282
  const { values: relevantInputValues, placeholders } = workspaceRelevantInputValues(instance.body || "", inputValues);
@@ -4217,6 +4287,7 @@ function workspaceNodePrompt(graph, nodeId, upstreamText, skillsBlock, mcpBlock
4217
4287
  return [
4218
4288
  "你正在执行一个独立任务。只使用本提示中的任务、输入、可用能力和文件边界。",
4219
4289
  fileBoundary ? `\n${fileBoundary}` : "",
4290
+ workspaceContextBlock ? `\n${workspaceContextBlock}` : "",
4220
4291
  inputBlock ? `\n${inputBlock}` : "",
4221
4292
  placeholders.size ? "\n任务只显式引用了上面的输入槽;其它未被 `${...}` 引用的已连接业务输入不要作为分析依据。" : "",
4222
4293
  implementationBlock ? `\n${implementationBlock}` : "",
@@ -4258,8 +4329,8 @@ function workspaceDefaultWorktreePath(runTmpRoot, nodeId, repoPath, branch = "")
4258
4329
  );
4259
4330
  }
4260
4331
 
4261
- function workspaceShouldAutoCleanupWorktree(worktreePath, hasExplicitWorktreePath) {
4262
- return Boolean(worktreePath) && !hasExplicitWorktreePath;
4332
+ function workspaceShouldAutoCleanupWorktree(result) {
4333
+ return Boolean(result?.worktreePath) && result.created === true;
4263
4334
  }
4264
4335
 
4265
4336
  function workspaceTrackAutoCleanupWorktree(list, item) {
@@ -4341,7 +4412,7 @@ function workspaceCreateNodeTmpDir(runTmpRoot, nodeId) {
4341
4412
  return dir;
4342
4413
  }
4343
4414
 
4344
- function workspaceCreateNodeRunPackage(runTmpRoot, nodeId, { scopedRoot, task = "", inputValues = {}, skillsBlock = "", mcpBlock = "" } = {}) {
4415
+ function workspaceCreateNodeRunPackage(runTmpRoot, nodeId, { scopedRoot, cwd = "", task = "", inputValues = {}, skillsBlock = "", mcpBlock = "" } = {}) {
4345
4416
  const nodeRunDir = workspaceCreateNodeTmpDir(runTmpRoot, nodeId);
4346
4417
  const nodeTmpDir = path.join(nodeRunDir, "tmp");
4347
4418
  const outputsDir = path.join(nodeRunDir, "outputs");
@@ -4357,6 +4428,7 @@ function workspaceCreateNodeRunPackage(runTmpRoot, nodeId, { scopedRoot, task =
4357
4428
  nodeTmpDir,
4358
4429
  outputsDir,
4359
4430
  workspaceRoot,
4431
+ executionCwd: cwd ? path.resolve(cwd) : workspaceRoot,
4360
4432
  createdAt: new Date().toISOString(),
4361
4433
  };
4362
4434
  const materializedInputs = workspaceMaterializeNodeInputFiles(nodeRunDir, workspaceRoot, inputValues);
@@ -4933,13 +5005,12 @@ async function runWorkspaceGraph(root, scopedRoot, payload, userCtx = {}, opts =
4933
5005
  const worktreePath = rawWorktreePath
4934
5006
  ? workspaceResolvePath(cwd, rawWorktreePath)
4935
5007
  : (gitContext?.worktreePath ? path.resolve(gitContext.worktreePath) : workspaceDefaultWorktreePath(runTmpRoot, nodeId, repoPath, branch));
4936
- const hasExplicitWorktreePath = Boolean(rawWorktreePath) || Boolean(gitContext?.worktreePath);
4937
5008
  const previousCwd = cwd;
4938
5009
  const force = ["true", "1", "yes", "on"].includes(workspaceSlotValue(workspaceSlotByName(instance, "force")).trim().toLowerCase());
4939
5010
  const pruneMissingRaw = workspaceSlotValue(workspaceSlotByName(instance, "pruneMissing")).trim().toLowerCase();
4940
5011
  const pruneMissing = pruneMissingRaw !== "false";
4941
5012
  const result = loadGitWorktree({ repoPath, branch, worktreePath, pipelineWorkspace: scopedRoot, force, pruneMissing });
4942
- if (workspaceShouldAutoCleanupWorktree(result.worktreePath, hasExplicitWorktreePath)) {
5013
+ if (workspaceShouldAutoCleanupWorktree(result)) {
4943
5014
  workspaceTrackAutoCleanupWorktree(autoCleanupWorktrees, {
4944
5015
  nodeId,
4945
5016
  repoPath: result.repoRoot,
@@ -5191,7 +5262,8 @@ async function runWorkspaceGraph(root, scopedRoot, payload, userCtx = {}, opts =
5191
5262
  // Best-effort debug artifact only.
5192
5263
  }
5193
5264
  const historyBlock = workspaceNodeHistoryBlock(nodeId, scopedRoot, runPackage);
5194
- const prompt = workspaceNodePrompt(graph, nodeId, promptUpstreamText, promptSkillsBlock, promptMcpBlock, runtimeInputValues, runPackage, historyBlock);
5265
+ const workspaceContextBlock = workspaceNodeWorkspaceContextBlock(graph, nodeId, outputs, scopedRoot, cwd);
5266
+ const prompt = workspaceNodePrompt(graph, nodeId, promptUpstreamText, promptSkillsBlock, promptMcpBlock, runtimeInputValues, runPackage, historyBlock, workspaceContextBlock);
5195
5267
  try {
5196
5268
  fs.writeFileSync(path.join(runPackage.nodeRunDir, "prompt.md"), prompt.trimEnd() + "\n", "utf-8");
5197
5269
  } catch {
@@ -6,8 +6,9 @@ description: |
6
6
  - `repoPath` is required unless `gitContext.repoPath` is connected.
7
7
  - `workspaceContext` is required so the node can preserve the previous execution context.
8
8
  - `branch` is optional. When empty, AgentFlow creates a detached worktree at the current HEAD.
9
- - `worktreePath` is optional. When empty, AgentFlow creates a temporary worktree under the current run temp directory and removes it after the run finishes.
10
- - Set `worktreePath` only when you explicitly want a persistent worktree outside the run lifecycle.
9
+ - `worktreePath` is optional. When empty, AgentFlow creates a temporary worktree under the current run temp directory.
10
+ - 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.
11
+ - Existing registered worktrees that were present before the run are reused and not removed automatically.
11
12
  - Existing worktree paths are reused only when they are registered by `git worktree list` for the given repo.
12
13
  - `pruneMissing` defaults to true. When Git has a registered worktree whose directory is missing, AgentFlow runs `git worktree prune` before adding it again.
13
14
  - `force` defaults to false. When true, AgentFlow passes `--force` to `git worktree add`.