agentweaver 0.1.15 → 0.1.17

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 (110) hide show
  1. package/README.md +76 -19
  2. package/dist/artifact-manifest.js +219 -0
  3. package/dist/artifacts.js +88 -3
  4. package/dist/doctor/checks/env-diagnostics.js +25 -0
  5. package/dist/doctor/checks/executors.js +2 -2
  6. package/dist/doctor/checks/flow-readiness.js +15 -18
  7. package/dist/flow-state.js +212 -15
  8. package/dist/index.js +539 -209
  9. package/dist/interactive/blessed-session.js +361 -0
  10. package/dist/interactive/controller.js +1326 -0
  11. package/dist/interactive/create-interactive-session.js +5 -0
  12. package/dist/interactive/ink/index.js +597 -0
  13. package/dist/interactive/progress.js +245 -0
  14. package/dist/interactive/selectors.js +14 -0
  15. package/dist/interactive/session.js +1 -0
  16. package/dist/interactive/state.js +34 -0
  17. package/dist/interactive/tree.js +155 -0
  18. package/dist/interactive/types.js +1 -0
  19. package/dist/interactive/view-model.js +1 -0
  20. package/dist/interactive-ui.js +159 -194
  21. package/dist/pipeline/auto-flow.js +9 -6
  22. package/dist/pipeline/context.js +7 -5
  23. package/dist/pipeline/declarative-flow-runner.js +212 -6
  24. package/dist/pipeline/declarative-flows.js +63 -17
  25. package/dist/pipeline/execution-routing-config.js +15 -0
  26. package/dist/pipeline/flow-catalog.js +50 -12
  27. package/dist/pipeline/flow-run-resume.js +29 -0
  28. package/dist/pipeline/flow-specs/auto-common.json +90 -360
  29. package/dist/pipeline/flow-specs/auto-golang.json +81 -360
  30. package/dist/pipeline/flow-specs/auto-simple.json +141 -0
  31. package/dist/pipeline/flow-specs/bugz/bug-analyze.json +2 -0
  32. package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
  33. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +316 -0
  34. package/dist/pipeline/flow-specs/design-review.json +10 -0
  35. package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +11 -0
  36. package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +2 -0
  37. package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
  38. package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +2 -0
  39. package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +2 -0
  40. package/dist/pipeline/flow-specs/implement.json +13 -6
  41. package/dist/pipeline/flow-specs/instant-task.json +177 -0
  42. package/dist/pipeline/flow-specs/normalize-task-source.json +311 -0
  43. package/dist/pipeline/flow-specs/plan-revise.json +7 -1
  44. package/dist/pipeline/flow-specs/plan.json +51 -71
  45. package/dist/pipeline/flow-specs/review/review-fix.json +24 -4
  46. package/dist/pipeline/flow-specs/review/review-loop.json +351 -45
  47. package/dist/pipeline/flow-specs/review/review-project-loop.json +590 -0
  48. package/dist/pipeline/flow-specs/review/review-project.json +12 -0
  49. package/dist/pipeline/flow-specs/review/review.json +37 -31
  50. package/dist/pipeline/flow-specs/task-describe.json +2 -0
  51. package/dist/pipeline/flow-specs/task-source/jira-fetch.json +70 -0
  52. package/dist/pipeline/flow-specs/task-source/manual-input.json +216 -0
  53. package/dist/pipeline/launch-profile-config.js +30 -18
  54. package/dist/pipeline/node-contract.js +1 -0
  55. package/dist/pipeline/node-registry.js +115 -6
  56. package/dist/pipeline/node-runner.js +3 -2
  57. package/dist/pipeline/nodes/build-review-fix-prompt-node.js +5 -1
  58. package/dist/pipeline/nodes/clear-ready-to-merge-node.js +11 -0
  59. package/dist/pipeline/nodes/commit-message-form-node.js +8 -0
  60. package/dist/pipeline/nodes/design-review-verdict-node.js +36 -0
  61. package/dist/pipeline/nodes/ensure-summary-json-node.js +13 -2
  62. package/dist/pipeline/nodes/fetch-gitlab-diff-node.js +19 -2
  63. package/dist/pipeline/nodes/fetch-gitlab-review-node.js +19 -2
  64. package/dist/pipeline/nodes/flow-run-node.js +242 -8
  65. package/dist/pipeline/nodes/git-commit-form-node.js +8 -0
  66. package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +19 -2
  67. package/dist/pipeline/nodes/jira-fetch-node.js +50 -4
  68. package/dist/pipeline/nodes/llm-prompt-node.js +38 -36
  69. package/dist/pipeline/nodes/planning-bundle-node.js +10 -0
  70. package/dist/pipeline/nodes/review-verdict-node.js +86 -0
  71. package/dist/pipeline/nodes/select-files-form-node.js +8 -0
  72. package/dist/pipeline/nodes/structured-summary-node.js +24 -0
  73. package/dist/pipeline/nodes/user-input-node.js +38 -3
  74. package/dist/pipeline/nodes/write-selection-file-node.js +20 -4
  75. package/dist/pipeline/plugin-loader.js +389 -0
  76. package/dist/pipeline/plugin-types.js +1 -0
  77. package/dist/pipeline/prompt-registry.js +3 -1
  78. package/dist/pipeline/prompt-runtime.js +4 -1
  79. package/dist/pipeline/registry.js +71 -4
  80. package/dist/pipeline/review-iteration.js +26 -0
  81. package/dist/pipeline/spec-compiler.js +3 -0
  82. package/dist/pipeline/spec-loader.js +14 -0
  83. package/dist/pipeline/spec-types.js +3 -0
  84. package/dist/pipeline/spec-validator.js +20 -0
  85. package/dist/pipeline/value-resolver.js +76 -2
  86. package/dist/plugin-sdk.js +1 -0
  87. package/dist/prompts.js +36 -14
  88. package/dist/review-severity.js +45 -0
  89. package/dist/runtime/artifact-registry.js +405 -0
  90. package/dist/runtime/design-review-input-contract.js +17 -16
  91. package/dist/runtime/env-loader.js +3 -0
  92. package/dist/runtime/execution-routing-store.js +134 -0
  93. package/dist/runtime/execution-routing.js +233 -0
  94. package/dist/runtime/interactive-execution-routing.js +471 -0
  95. package/dist/runtime/plan-revise-input-contract.js +35 -32
  96. package/dist/runtime/planning-bundle.js +123 -0
  97. package/dist/runtime/ready-to-merge.js +22 -1
  98. package/dist/runtime/review-input-contract.js +100 -0
  99. package/dist/structured-artifact-schema-registry.js +9 -0
  100. package/dist/structured-artifact-schemas.json +140 -1
  101. package/dist/structured-artifacts.js +77 -6
  102. package/dist/user-input.js +70 -3
  103. package/docs/example/.flows/examples/claude-example.json +50 -0
  104. package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
  105. package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
  106. package/docs/examples/.flows/claude-example.json +50 -0
  107. package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
  108. package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
  109. package/docs/plugin-sdk.md +731 -0
  110. package/package.json +11 -4
@@ -1,11 +1,12 @@
1
1
  import { setCurrentNode } from "../tui.js";
2
2
  import { runNodeChecks } from "./checks.js";
3
3
  export async function runNode(node, context, params, options = {}) {
4
+ const effectiveContext = options.contextOverrides ? { ...context, ...options.contextOverrides } : context;
4
5
  setCurrentNode(node.kind);
5
6
  try {
6
- const result = await node.run(context, params);
7
+ const result = await node.run(effectiveContext, params);
7
8
  if (!options.skipChecks) {
8
- const checks = node.checks?.(context, params, result) ?? [];
9
+ const checks = node.checks?.(effectiveContext, params, result) ?? [];
9
10
  runNodeChecks(checks);
10
11
  }
11
12
  return result;
@@ -22,7 +22,11 @@ export const buildReviewFixPromptNode = {
22
22
  "Use the user selection below as source of truth for the current review-fix scope.",
23
23
  `Selection file: ${params.selectionFile}`,
24
24
  `apply_all: ${applyAll ? "true" : "false"}`,
25
- applyAll ? "Fix all findings in the current iteration." : `Fix only selected findings:\n- ${selectedFindings.join("\n- ")}`,
25
+ applyAll
26
+ ? "Fix all findings in the current iteration."
27
+ : selectedFindings.length > 0
28
+ ? `Fix only selected findings:\n- ${selectedFindings.join("\n- ")}`
29
+ : "No findings were selected for this review-fix iteration. Do not modify code; report the no-op outcome after validation.",
26
30
  ].join("\n\n");
27
31
  return {
28
32
  value: {
@@ -0,0 +1,11 @@
1
+ import { clearReadyToMergeFile } from "../../runtime/ready-to-merge.js";
2
+ export const clearReadyToMergeNode = {
3
+ kind: "clear-ready-to-merge",
4
+ version: 1,
5
+ async run(_context, params) {
6
+ const cleared = clearReadyToMergeFile(params.taskKey);
7
+ return {
8
+ value: { cleared },
9
+ };
10
+ },
11
+ };
@@ -1,5 +1,6 @@
1
1
  import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
3
4
  import { TaskRunnerError } from "../../errors.js";
4
5
  import { validateUserInputValues } from "../../user-input.js";
5
6
  function parseCommitMessage(content) {
@@ -72,6 +73,13 @@ export const commitMessageFormNode = {
72
73
  kind: "artifact",
73
74
  path: params.outputFile,
74
75
  required: true,
76
+ manifest: {
77
+ publish: true,
78
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
79
+ payloadFamily: "structured-json",
80
+ schemaId: "user-input/v1",
81
+ schemaVersion: 1,
82
+ },
75
83
  },
76
84
  ],
77
85
  };
@@ -0,0 +1,36 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { TaskRunnerError } from "../../errors.js";
3
+ import { validateStructuredArtifacts } from "../../structured-artifacts.js";
4
+ import { designReviewJsonFile, latestArtifactIteration } from "../../artifacts.js";
5
+ function readVerdictFile(path) {
6
+ if (!existsSync(path)) {
7
+ throw new TaskRunnerError(`Design review verdict file not found: ${path}`);
8
+ }
9
+ try {
10
+ return JSON.parse(readFileSync(path, "utf8"));
11
+ }
12
+ catch {
13
+ throw new TaskRunnerError(`Failed to parse design review verdict JSON: ${path}`);
14
+ }
15
+ }
16
+ export const designReviewVerdictNode = {
17
+ kind: "design-review-verdict",
18
+ version: 1,
19
+ async run(_context, params) {
20
+ const iteration = params.iteration ?? latestArtifactIteration(params.taskKey, "design-review", "json") ?? 1;
21
+ const jsonPath = designReviewJsonFile(params.taskKey, iteration);
22
+ validateStructuredArtifacts([{ path: jsonPath, schemaId: "design-review/v1" }], "Design review verdict is invalid or missing.");
23
+ const verdict = readVerdictFile(jsonPath);
24
+ const status = (verdict?.status ?? "needs_revision");
25
+ const canProceed = status === "approved" || status === "approved_with_warnings";
26
+ const needsRevision = status === "needs_revision";
27
+ return {
28
+ value: {
29
+ status,
30
+ canProceed,
31
+ needsRevision,
32
+ verdict: verdict?.summary ?? `Design review status: ${status}`,
33
+ },
34
+ };
35
+ },
36
+ };
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { TaskRunnerError } from "../../errors.js";
4
+ import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
4
5
  function toSummaryText(markdown) {
5
6
  return markdown
6
7
  .replace(/\r\n/g, "\n")
@@ -27,7 +28,7 @@ function hasValidSummaryArtifact(outputFile) {
27
28
  export const ensureSummaryJsonNode = {
28
29
  kind: "ensure-summary-json",
29
30
  version: 1,
30
- async run(_context, params) {
31
+ async run(context, params) {
31
32
  if (hasValidSummaryArtifact(params.outputFile)) {
32
33
  return {
33
34
  value: {
@@ -53,7 +54,17 @@ export const ensureSummaryJsonNode = {
53
54
  created: !repaired,
54
55
  repaired,
55
56
  },
56
- outputs: [{ kind: "artifact", path: params.outputFile, required: true }],
57
+ outputs: [
58
+ {
59
+ kind: "artifact",
60
+ path: params.outputFile,
61
+ required: true,
62
+ manifest: {
63
+ publish: true,
64
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
65
+ },
66
+ },
67
+ ],
57
68
  };
58
69
  },
59
70
  };
@@ -1,3 +1,4 @@
1
+ import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
1
2
  import { toExecutorContext } from "../types.js";
2
3
  export const fetchGitLabDiffNode = {
3
4
  kind: "fetch-gitlab-diff",
@@ -12,8 +13,24 @@ export const fetchGitLabDiffNode = {
12
13
  return {
13
14
  value,
14
15
  outputs: [
15
- { kind: "artifact", path: params.outputFile, required: true },
16
- { kind: "artifact", path: params.outputJsonFile, required: true },
16
+ {
17
+ kind: "artifact",
18
+ path: params.outputFile,
19
+ required: true,
20
+ manifest: {
21
+ publish: true,
22
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
23
+ },
24
+ },
25
+ {
26
+ kind: "artifact",
27
+ path: params.outputJsonFile,
28
+ required: true,
29
+ manifest: {
30
+ publish: true,
31
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputJsonFile),
32
+ },
33
+ },
17
34
  ],
18
35
  };
19
36
  },
@@ -1,3 +1,4 @@
1
+ import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
1
2
  import { toExecutorContext } from "../types.js";
2
3
  export const fetchGitLabReviewNode = {
3
4
  kind: "fetch-gitlab-review",
@@ -12,8 +13,24 @@ export const fetchGitLabReviewNode = {
12
13
  return {
13
14
  value,
14
15
  outputs: [
15
- { kind: "artifact", path: params.outputFile, required: true },
16
- { kind: "artifact", path: params.outputJsonFile, required: true },
16
+ {
17
+ kind: "artifact",
18
+ path: params.outputFile,
19
+ required: true,
20
+ manifest: {
21
+ publish: true,
22
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
23
+ },
24
+ },
25
+ {
26
+ kind: "artifact",
27
+ path: params.outputJsonFile,
28
+ required: true,
29
+ manifest: {
30
+ publish: true,
31
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputJsonFile),
32
+ },
33
+ },
17
34
  ],
18
35
  };
19
36
  },
@@ -1,6 +1,209 @@
1
1
  import { printInfo } from "../../tui.js";
2
+ import { nextArtifactIteration } from "../../artifacts.js";
3
+ import { resolveDesignReviewInputContract } from "../../runtime/design-review-input-contract.js";
4
+ import { resolvePlanReviseInputContract } from "../../runtime/plan-revise-input-contract.js";
5
+ import { inspectReviewInputContract } from "../../runtime/review-input-contract.js";
2
6
  import { runExpandedPhase } from "../declarative-flow-runner.js";
3
7
  import { loadNamedDeclarativeFlow } from "../declarative-flows.js";
8
+ import { isFlowRunResumeEnvelope } from "../flow-run-resume.js";
9
+ import { withCanonicalReviewLoopParams } from "../review-iteration.js";
10
+ import { ARTIFACT_LINEAGE_REF_PATHS_PARAM } from "../value-resolver.js";
11
+ function withArtifactLineageRefPaths(params, lineageRefs) {
12
+ if (Object.keys(lineageRefs).length === 0) {
13
+ return params;
14
+ }
15
+ const existing = params[ARTIFACT_LINEAGE_REF_PATHS_PARAM];
16
+ const merged = existing && typeof existing === "object" && !Array.isArray(existing)
17
+ ? { ...existing, ...lineageRefs }
18
+ : lineageRefs;
19
+ return {
20
+ ...params,
21
+ [ARTIFACT_LINEAGE_REF_PATHS_PARAM]: merged,
22
+ };
23
+ }
24
+ function parsePositiveInteger(value) {
25
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
26
+ }
27
+ export function resolveNestedFlowParams(flowKind, flowParams) {
28
+ let resolvedFlowParams = withCanonicalReviewLoopParams(flowKind, flowParams);
29
+ if (flowKind === "design-review-flow") {
30
+ const taskKey = String(flowParams["taskKey"] ?? "");
31
+ if (!taskKey) {
32
+ return resolvedFlowParams;
33
+ }
34
+ const contract = resolveDesignReviewInputContract(taskKey);
35
+ const iteration = parsePositiveInteger(flowParams["iteration"]) ?? nextArtifactIteration(taskKey, "design-review");
36
+ return withArtifactLineageRefPaths({
37
+ ...flowParams,
38
+ iteration,
39
+ planningIteration: contract.planningIteration,
40
+ designFile: contract.designFile,
41
+ designJsonFile: contract.designJsonFile,
42
+ planFile: contract.planFile,
43
+ planJsonFile: contract.planJsonFile,
44
+ hasQaArtifacts: contract.hasQaArtifacts,
45
+ qaFilePath: contract.qaFilePath,
46
+ qaJsonFilePath: contract.qaJsonFilePath,
47
+ qaFile: contract.qaFile,
48
+ qaJsonFile: contract.qaJsonFile,
49
+ hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
50
+ taskContextJsonFilePath: contract.taskContextJsonFilePath,
51
+ taskContextJsonFile: contract.taskContextJsonFile,
52
+ hasJiraTaskFile: contract.hasJiraTaskFile,
53
+ jiraTaskFilePath: contract.jiraTaskFilePath,
54
+ jiraTaskFile: contract.jiraTaskFile,
55
+ hasJiraAttachmentsManifestFile: contract.hasJiraAttachmentsManifestFile,
56
+ jiraAttachmentsManifestFilePath: contract.jiraAttachmentsManifestFilePath,
57
+ jiraAttachmentsManifestFile: contract.jiraAttachmentsManifestFile,
58
+ hasJiraAttachmentsContextFile: contract.hasJiraAttachmentsContextFile,
59
+ jiraAttachmentsContextFilePath: contract.jiraAttachmentsContextFilePath,
60
+ jiraAttachmentsContextFile: contract.jiraAttachmentsContextFile,
61
+ hasPlanningAnswersJsonFile: contract.hasPlanningAnswersJsonFile,
62
+ planningAnswersJsonFilePath: contract.planningAnswersJsonFilePath,
63
+ planningAnswersJsonFile: contract.planningAnswersJsonFile,
64
+ hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
65
+ taskInputJsonFilePath: contract.taskInputJsonFilePath,
66
+ taskInputJsonFile: contract.taskInputJsonFile,
67
+ }, {
68
+ "params.designFile": contract.designFile,
69
+ "params.designJsonFile": contract.designJsonFile,
70
+ "params.planFile": contract.planFile,
71
+ "params.planJsonFile": contract.planJsonFile,
72
+ ...(contract.qaFilePath ? { "params.qaFile": contract.qaFilePath } : {}),
73
+ ...(contract.qaJsonFilePath ? { "params.qaJsonFile": contract.qaJsonFilePath } : {}),
74
+ ...(contract.taskContextJsonFilePath
75
+ ? { "params.taskContextJsonFile": contract.taskContextJsonFilePath }
76
+ : {}),
77
+ ...(contract.jiraTaskFilePath ? { "params.jiraTaskFile": contract.jiraTaskFilePath } : {}),
78
+ ...(contract.jiraAttachmentsManifestFilePath
79
+ ? { "params.jiraAttachmentsManifestFile": contract.jiraAttachmentsManifestFilePath }
80
+ : {}),
81
+ ...(contract.jiraAttachmentsContextFilePath
82
+ ? { "params.jiraAttachmentsContextFile": contract.jiraAttachmentsContextFilePath }
83
+ : {}),
84
+ ...(contract.planningAnswersJsonFilePath
85
+ ? { "params.planningAnswersJsonFile": contract.planningAnswersJsonFilePath }
86
+ : {}),
87
+ ...(contract.taskInputJsonFilePath
88
+ ? { "params.taskInputJsonFile": contract.taskInputJsonFilePath }
89
+ : {}),
90
+ });
91
+ }
92
+ if (flowKind === "plan-revise-flow") {
93
+ const taskKey = String(flowParams["taskKey"] ?? "");
94
+ if (!taskKey) {
95
+ return resolvedFlowParams;
96
+ }
97
+ const contract = resolvePlanReviseInputContract(taskKey);
98
+ return withArtifactLineageRefPaths({
99
+ ...flowParams,
100
+ reviewIteration: contract.reviewIteration,
101
+ reviewFile: contract.reviewFile,
102
+ reviewJsonFile: contract.reviewJsonFile,
103
+ sourcePlanningIteration: contract.sourcePlanningIteration,
104
+ outputIteration: contract.outputIteration,
105
+ designFile: contract.designFile,
106
+ designJsonFile: contract.designJsonFile,
107
+ planFile: contract.planFile,
108
+ planJsonFile: contract.planJsonFile,
109
+ hasQaArtifacts: contract.hasQaArtifacts,
110
+ qaFilePath: contract.qaFilePath,
111
+ qaJsonFilePath: contract.qaJsonFilePath,
112
+ qaFile: contract.qaFile,
113
+ qaJsonFile: contract.qaJsonFile,
114
+ revisedDesignFile: contract.revisedDesignFile,
115
+ revisedDesignJsonFile: contract.revisedDesignJsonFile,
116
+ revisedPlanFile: contract.revisedPlanFile,
117
+ revisedPlanJsonFile: contract.revisedPlanJsonFile,
118
+ revisedQaFile: contract.revisedQaFile,
119
+ revisedQaJsonFile: contract.revisedQaJsonFile,
120
+ hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
121
+ taskContextJsonFilePath: contract.taskContextJsonFilePath,
122
+ taskContextJsonFile: contract.taskContextJsonFile,
123
+ hasJiraTaskFile: contract.hasJiraTaskFile,
124
+ jiraTaskFilePath: contract.jiraTaskFilePath,
125
+ jiraTaskFile: contract.jiraTaskFile,
126
+ hasJiraAttachmentsManifestFile: contract.hasJiraAttachmentsManifestFile,
127
+ jiraAttachmentsManifestFilePath: contract.jiraAttachmentsManifestFilePath,
128
+ jiraAttachmentsManifestFile: contract.jiraAttachmentsManifestFile,
129
+ hasJiraAttachmentsContextFile: contract.hasJiraAttachmentsContextFile,
130
+ jiraAttachmentsContextFilePath: contract.jiraAttachmentsContextFilePath,
131
+ jiraAttachmentsContextFile: contract.jiraAttachmentsContextFile,
132
+ hasPlanningAnswersJsonFile: contract.hasPlanningAnswersJsonFile,
133
+ planningAnswersJsonFilePath: contract.planningAnswersJsonFilePath,
134
+ planningAnswersJsonFile: contract.planningAnswersJsonFile,
135
+ hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
136
+ taskInputJsonFilePath: contract.taskInputJsonFilePath,
137
+ taskInputJsonFile: contract.taskInputJsonFile,
138
+ }, {
139
+ "params.reviewFile": contract.reviewFile,
140
+ "params.reviewJsonFile": contract.reviewJsonFile,
141
+ "params.designFile": contract.designFile,
142
+ "params.designJsonFile": contract.designJsonFile,
143
+ "params.planFile": contract.planFile,
144
+ "params.planJsonFile": contract.planJsonFile,
145
+ ...(contract.qaFilePath ? { "params.qaFile": contract.qaFilePath } : {}),
146
+ ...(contract.qaJsonFilePath ? { "params.qaJsonFile": contract.qaJsonFilePath } : {}),
147
+ ...(contract.taskContextJsonFilePath
148
+ ? { "params.taskContextJsonFile": contract.taskContextJsonFilePath }
149
+ : {}),
150
+ ...(contract.jiraTaskFilePath ? { "params.jiraTaskFile": contract.jiraTaskFilePath } : {}),
151
+ ...(contract.jiraAttachmentsManifestFilePath
152
+ ? { "params.jiraAttachmentsManifestFile": contract.jiraAttachmentsManifestFilePath }
153
+ : {}),
154
+ ...(contract.jiraAttachmentsContextFilePath
155
+ ? { "params.jiraAttachmentsContextFile": contract.jiraAttachmentsContextFilePath }
156
+ : {}),
157
+ ...(contract.planningAnswersJsonFilePath
158
+ ? { "params.planningAnswersJsonFile": contract.planningAnswersJsonFilePath }
159
+ : {}),
160
+ ...(contract.taskInputJsonFilePath
161
+ ? { "params.taskInputJsonFile": contract.taskInputJsonFilePath }
162
+ : {}),
163
+ });
164
+ }
165
+ if (flowKind === "review-flow") {
166
+ const taskKey = String(flowParams["taskKey"] ?? "");
167
+ if (!taskKey) {
168
+ return resolvedFlowParams;
169
+ }
170
+ const inspection = inspectReviewInputContract(taskKey);
171
+ if (inspection.status !== "ready") {
172
+ return resolvedFlowParams;
173
+ }
174
+ const { contract } = inspection;
175
+ return withArtifactLineageRefPaths({
176
+ ...flowParams,
177
+ planningIteration: contract.planningIteration,
178
+ designFile: contract.designFile,
179
+ designJsonFile: contract.designJsonFile,
180
+ planFile: contract.planFile,
181
+ planJsonFile: contract.planJsonFile,
182
+ hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
183
+ taskContextJsonFilePath: contract.taskContextJsonFilePath,
184
+ taskContextJsonFile: contract.taskContextJsonFile,
185
+ hasJiraTaskFile: contract.hasJiraTaskFile,
186
+ jiraTaskFilePath: contract.jiraTaskFilePath,
187
+ jiraTaskFile: contract.jiraTaskFile,
188
+ hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
189
+ taskInputJsonFilePath: contract.taskInputJsonFilePath,
190
+ taskInputJsonFile: contract.taskInputJsonFile,
191
+ }, {
192
+ "params.designFile": contract.designFile,
193
+ "params.designJsonFile": contract.designJsonFile,
194
+ "params.planFile": contract.planFile,
195
+ "params.planJsonFile": contract.planJsonFile,
196
+ ...(contract.taskContextJsonFilePath
197
+ ? { "params.taskContextJsonFile": contract.taskContextJsonFilePath }
198
+ : {}),
199
+ ...(contract.jiraTaskFilePath ? { "params.jiraTaskFile": contract.jiraTaskFilePath } : {}),
200
+ ...(contract.taskInputJsonFilePath
201
+ ? { "params.taskInputJsonFile": contract.taskInputJsonFilePath }
202
+ : {}),
203
+ });
204
+ }
205
+ return resolvedFlowParams;
206
+ }
4
207
  export const flowRunNode = {
5
208
  kind: "flow-run",
6
209
  version: 1,
@@ -12,29 +215,60 @@ export const flowRunNode = {
12
215
  if (labelText) {
13
216
  printInfo(String(labelText));
14
217
  }
15
- const flow = loadNamedDeclarativeFlow(fileName, context.cwd);
16
- const executionState = {
218
+ const flow = await loadNamedDeclarativeFlow(fileName, context.cwd, {
219
+ ...(context.registryContext ? { registryContext: context.registryContext } : {}),
220
+ });
221
+ const resolvedFlowParams = resolveNestedFlowParams(flow.kind, flowParams);
222
+ const resumeValue = isFlowRunResumeEnvelope(context.resumeStepValue)
223
+ && context.resumeStepValue.flowKind === flow.kind
224
+ && context.resumeStepValue.flowVersion === flow.version
225
+ ? context.resumeStepValue
226
+ : null;
227
+ const executionState = resumeValue?.executionState ?? {
17
228
  flowKind: flow.kind,
18
229
  flowVersion: flow.version,
19
230
  terminated: false,
231
+ terminationOutcome: "success",
20
232
  phases: [],
21
233
  };
234
+ const buildResumeEnvelope = (nextExecutionState) => ({
235
+ resumeKind: "flow-run",
236
+ flowKind: flow.kind,
237
+ flowVersion: flow.version,
238
+ executionState: nextExecutionState,
239
+ publishedArtifacts: collectPublishedArtifacts(nextExecutionState),
240
+ });
22
241
  for (const phase of flow.phases) {
23
- await runExpandedPhase(phase, context, flowParams, flow.constants, {
242
+ await runExpandedPhase(phase, context, resolvedFlowParams, flow.constants, {
24
243
  executionState,
25
244
  flowKind: flow.kind,
26
245
  flowVersion: flow.version,
246
+ onStateChange: async (nextExecutionState) => {
247
+ await context.persistRunningStepValue?.(buildResumeEnvelope(nextExecutionState));
248
+ },
27
249
  });
28
250
  if (executionState.terminated) {
29
251
  break;
30
252
  }
31
253
  }
32
254
  return {
33
- value: {
34
- flowKind: flow.kind,
35
- flowVersion: flow.version,
36
- executionState,
37
- },
255
+ value: buildResumeEnvelope(executionState),
38
256
  };
39
257
  },
40
258
  };
259
+ function collectPublishedArtifacts(executionState) {
260
+ const merged = [];
261
+ const seen = new Set();
262
+ for (const phase of executionState.phases) {
263
+ for (const step of phase.steps) {
264
+ for (const artifact of step.publishedArtifacts ?? []) {
265
+ if (seen.has(artifact.artifact_id)) {
266
+ continue;
267
+ }
268
+ seen.add(artifact.artifact_id);
269
+ merged.push(artifact);
270
+ }
271
+ }
272
+ }
273
+ return merged;
274
+ }
@@ -1,5 +1,6 @@
1
1
  import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
3
4
  import { TaskRunnerError } from "../../errors.js";
4
5
  import { validateUserInputValues } from "../../user-input.js";
5
6
  function parseCommitMessage(content) {
@@ -131,6 +132,13 @@ export const gitCommitFormNode = {
131
132
  kind: "artifact",
132
133
  path: params.outputFile,
133
134
  required: true,
135
+ manifest: {
136
+ publish: true,
137
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
138
+ payloadFamily: "structured-json",
139
+ schemaId: "user-input/v1",
140
+ schemaVersion: 1,
141
+ },
134
142
  },
135
143
  ],
136
144
  };
@@ -1,5 +1,6 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { readFileSync } from "node:fs";
3
+ import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
3
4
  import { TaskRunnerError } from "../../errors.js";
4
5
  function normalizeMarkdownLanguage(mdLang) {
5
6
  return mdLang === "en" ? "en" : "ru";
@@ -101,8 +102,24 @@ export const gitlabReviewArtifactsNode = {
101
102
  readyToMerge: artifact.ready_to_merge,
102
103
  },
103
104
  outputs: [
104
- { kind: "artifact", path: params.reviewFile, required: true },
105
- { kind: "artifact", path: params.reviewJsonFile, required: true },
105
+ {
106
+ kind: "artifact",
107
+ path: params.reviewFile,
108
+ required: true,
109
+ manifest: {
110
+ publish: true,
111
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.reviewFile),
112
+ },
113
+ },
114
+ {
115
+ kind: "artifact",
116
+ path: params.reviewJsonFile,
117
+ required: true,
118
+ manifest: {
119
+ publish: true,
120
+ logicalKey: buildLogicalKeyForPayload(context.issueKey, params.reviewJsonFile),
121
+ },
122
+ },
106
123
  ],
107
124
  };
108
125
  },
@@ -10,15 +10,61 @@ export const jiraFetchNode = {
10
10
  ...(params.attachmentsManifestFile ? { attachmentsManifestFile: params.attachmentsManifestFile } : {}),
11
11
  ...(params.attachmentsContextFile ? { attachmentsContextFile: params.attachmentsContextFile } : {}),
12
12
  }, executor.defaultConfig);
13
- const outputs = [{ kind: "file", path: params.outputFile, required: true }];
13
+ const outputs = [
14
+ {
15
+ kind: "file",
16
+ path: params.outputFile,
17
+ required: true,
18
+ manifest: {
19
+ publish: true,
20
+ logicalKey: "artifacts/jira-task.json",
21
+ payloadFamily: "helper-json",
22
+ schemaId: "helper-json/v1",
23
+ schemaVersion: 1,
24
+ },
25
+ },
26
+ ];
14
27
  if (params.attachmentsManifestFile) {
15
- outputs.push({ kind: "artifact", path: params.attachmentsManifestFile, required: true });
28
+ outputs.push({
29
+ kind: "artifact",
30
+ path: params.attachmentsManifestFile,
31
+ required: true,
32
+ manifest: {
33
+ publish: true,
34
+ logicalKey: "artifacts/jira-attachments.json",
35
+ payloadFamily: "helper-json",
36
+ schemaId: "helper-json/v1",
37
+ schemaVersion: 1,
38
+ },
39
+ });
16
40
  }
17
41
  if (params.attachmentsContextFile) {
18
- outputs.push({ kind: "artifact", path: params.attachmentsContextFile, required: true });
42
+ outputs.push({
43
+ kind: "artifact",
44
+ path: params.attachmentsContextFile,
45
+ required: true,
46
+ manifest: {
47
+ publish: true,
48
+ logicalKey: "jira-attachments-context.txt",
49
+ payloadFamily: "plain-text",
50
+ schemaId: "plain-text/v1",
51
+ schemaVersion: 1,
52
+ },
53
+ });
19
54
  }
20
55
  if (value.enrichedFile) {
21
- outputs.push({ kind: "artifact", path: value.enrichedFile, required: false });
56
+ outputs.push({
57
+ kind: "artifact",
58
+ path: value.enrichedFile,
59
+ required: false,
60
+ manifest: {
61
+ publish: true,
62
+ logicalKey: "artifacts/jira-task-enriched.json",
63
+ payloadFamily: "helper-json",
64
+ schemaId: "helper-json/v1",
65
+ schemaVersion: 1,
66
+ },
67
+ });
22
68
  }
23
69
  return {
24
70
  value,