agentweaver 0.1.14 → 0.1.16
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 +29 -7
- package/dist/artifact-manifest.js +219 -0
- package/dist/artifacts.js +21 -1
- package/dist/doctor/checks/cwd-context.js +4 -3
- package/dist/doctor/checks/env-diagnostics.js +193 -71
- package/dist/doctor/checks/flow-readiness.js +212 -203
- package/dist/doctor/index.js +1 -1
- package/dist/doctor/orchestrator.js +18 -7
- package/dist/doctor/runner.js +9 -8
- package/dist/doctor/types.js +12 -0
- package/dist/flow-state.js +75 -15
- package/dist/index.js +499 -199
- package/dist/interactive/blessed-session.js +361 -0
- package/dist/interactive/controller.js +1293 -0
- package/dist/interactive/create-interactive-session.js +5 -0
- package/dist/interactive/ink/index.js +576 -0
- package/dist/interactive/progress.js +245 -0
- package/dist/interactive/selectors.js +14 -0
- package/dist/interactive/session.js +1 -0
- package/dist/interactive/state.js +34 -0
- package/dist/interactive/tree.js +155 -0
- package/dist/interactive/types.js +1 -0
- package/dist/interactive/view-model.js +1 -0
- package/dist/interactive-ui.js +159 -194
- package/dist/pipeline/context.js +1 -0
- package/dist/pipeline/declarative-flow-runner.js +212 -6
- package/dist/pipeline/declarative-flows.js +27 -0
- package/dist/pipeline/execution-routing-config.js +15 -0
- package/dist/pipeline/flow-catalog.js +23 -3
- package/dist/pipeline/flow-run-resume.js +29 -0
- package/dist/pipeline/flow-specs/auto-common.json +89 -360
- package/dist/pipeline/flow-specs/auto-golang.json +58 -363
- package/dist/pipeline/flow-specs/auto-simple.json +141 -0
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +2 -0
- package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
- package/dist/pipeline/flow-specs/design-review/design-review-loop.json +304 -0
- package/dist/pipeline/flow-specs/design-review.json +249 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +11 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +2 -0
- package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
- package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +2 -0
- package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +2 -0
- package/dist/pipeline/flow-specs/implement.json +24 -5
- package/dist/pipeline/flow-specs/instant-task.json +177 -0
- package/dist/pipeline/flow-specs/normalize-task-source.json +311 -0
- package/dist/pipeline/flow-specs/plan-revise.json +267 -0
- package/dist/pipeline/flow-specs/plan.json +48 -70
- package/dist/pipeline/flow-specs/review/review-fix.json +24 -4
- package/dist/pipeline/flow-specs/review/review-loop.json +351 -45
- package/dist/pipeline/flow-specs/review/review-project-loop.json +590 -0
- package/dist/pipeline/flow-specs/review/review-project.json +12 -0
- package/dist/pipeline/flow-specs/review/review.json +37 -31
- package/dist/pipeline/flow-specs/task-describe.json +62 -2
- package/dist/pipeline/flow-specs/task-source/jira-fetch.json +70 -0
- package/dist/pipeline/flow-specs/task-source/manual-input.json +216 -0
- package/dist/pipeline/node-registry.js +49 -1
- package/dist/pipeline/node-runner.js +3 -2
- package/dist/pipeline/nodes/build-review-fix-prompt-node.js +5 -1
- package/dist/pipeline/nodes/clear-ready-to-merge-node.js +11 -0
- package/dist/pipeline/nodes/commit-message-form-node.js +8 -0
- package/dist/pipeline/nodes/design-review-verdict-node.js +36 -0
- package/dist/pipeline/nodes/ensure-summary-json-node.js +70 -0
- package/dist/pipeline/nodes/fetch-gitlab-diff-node.js +19 -2
- package/dist/pipeline/nodes/fetch-gitlab-review-node.js +19 -2
- package/dist/pipeline/nodes/flow-run-node.js +226 -7
- package/dist/pipeline/nodes/git-commit-form-node.js +8 -0
- package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +19 -2
- package/dist/pipeline/nodes/jira-fetch-node.js +50 -4
- package/dist/pipeline/nodes/llm-prompt-node.js +32 -12
- package/dist/pipeline/nodes/planning-bundle-node.js +10 -0
- package/dist/pipeline/nodes/review-verdict-node.js +86 -0
- package/dist/pipeline/nodes/select-files-form-node.js +8 -0
- package/dist/pipeline/nodes/structured-summary-node.js +24 -0
- package/dist/pipeline/nodes/user-input-node.js +38 -3
- package/dist/pipeline/nodes/write-selection-file-node.js +20 -4
- package/dist/pipeline/prompt-registry.js +5 -1
- package/dist/pipeline/prompt-runtime.js +4 -1
- package/dist/pipeline/review-iteration.js +26 -0
- package/dist/pipeline/spec-compiler.js +2 -0
- package/dist/pipeline/spec-types.js +5 -0
- package/dist/pipeline/spec-validator.js +14 -0
- package/dist/pipeline/value-resolver.js +84 -1
- package/dist/prompts.js +82 -13
- package/dist/review-severity.js +45 -0
- package/dist/runtime/artifact-registry.js +402 -0
- package/dist/runtime/design-review-input-contract.js +113 -0
- package/dist/runtime/env-loader.js +3 -0
- package/dist/runtime/execution-routing-store.js +134 -0
- package/dist/runtime/execution-routing.js +227 -0
- package/dist/runtime/interactive-execution-routing.js +462 -0
- package/dist/runtime/plan-revise-input-contract.js +147 -0
- package/dist/runtime/planning-bundle.js +123 -0
- package/dist/runtime/ready-to-merge.js +31 -0
- package/dist/runtime/review-input-contract.js +100 -0
- package/dist/scope.js +11 -2
- package/dist/structured-artifact-schema-registry.js +10 -0
- package/dist/structured-artifact-schemas.json +257 -1
- package/dist/structured-artifacts.js +83 -6
- package/dist/user-input.js +70 -3
- package/package.json +6 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { reviewJsonFile, latestArtifactIteration, readyToMergeFile } from "../../artifacts.js";
|
|
3
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
4
|
+
import { resolveBlockingReviewSeverities, normalizeReviewSeverity } from "../../review-severity.js";
|
|
5
|
+
import { validateStructuredArtifacts } from "../../structured-artifacts.js";
|
|
6
|
+
import { clearReadyToMergeFile, writeReadyToMergeFile } from "../../runtime/ready-to-merge.js";
|
|
7
|
+
function readReviewVerdictFile(path) {
|
|
8
|
+
if (!existsSync(path)) {
|
|
9
|
+
throw new TaskRunnerError(`Review findings file not found: ${path}`);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new TaskRunnerError(`Failed to parse review findings JSON: ${path}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export const reviewVerdictNode = {
|
|
19
|
+
kind: "review-verdict",
|
|
20
|
+
version: 1,
|
|
21
|
+
async run(context, params) {
|
|
22
|
+
const iteration = params.iteration ?? latestArtifactIteration(params.taskKey, "review", "json") ?? 1;
|
|
23
|
+
const jsonPath = reviewJsonFile(params.taskKey, iteration);
|
|
24
|
+
validateStructuredArtifacts([{ path: jsonPath, schemaId: "review-findings/v1" }], "Review findings are invalid or missing.");
|
|
25
|
+
const report = readReviewVerdictFile(jsonPath);
|
|
26
|
+
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
27
|
+
const blockingSeverities = resolveBlockingReviewSeverities(params.blockingSeverities);
|
|
28
|
+
const blockingSeveritySet = new Set(blockingSeverities);
|
|
29
|
+
const blockingFindingTitles = findings
|
|
30
|
+
.filter((finding) => {
|
|
31
|
+
const severity = normalizeReviewSeverity(finding.severity);
|
|
32
|
+
return severity !== null && blockingSeveritySet.has(severity);
|
|
33
|
+
})
|
|
34
|
+
.map((finding) => (typeof finding.title === "string" ? finding.title.trim() : ""))
|
|
35
|
+
.filter((title) => title.length > 0);
|
|
36
|
+
const readyToMerge = blockingFindingTitles.length === 0;
|
|
37
|
+
const summary = typeof report.summary === "string" && report.summary.trim().length > 0
|
|
38
|
+
? report.summary.trim()
|
|
39
|
+
: readyToMerge
|
|
40
|
+
? "No blocking findings."
|
|
41
|
+
: `Blocking findings: ${blockingFindingTitles.join(", ")}`;
|
|
42
|
+
const normalizedReport = {
|
|
43
|
+
...report,
|
|
44
|
+
ready_to_merge: readyToMerge,
|
|
45
|
+
};
|
|
46
|
+
writeFileSync(jsonPath, `${JSON.stringify(normalizedReport, null, 2)}\n`, "utf8");
|
|
47
|
+
if (readyToMerge) {
|
|
48
|
+
writeReadyToMergeFile(params.taskKey, {
|
|
49
|
+
...(context.mdLang !== undefined ? { mdLang: context.mdLang } : {}),
|
|
50
|
+
summary,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
clearReadyToMergeFile(params.taskKey);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
value: {
|
|
58
|
+
readyToMerge,
|
|
59
|
+
blockingSeverities,
|
|
60
|
+
blockingFindingTitles,
|
|
61
|
+
blockingFindingsCount: blockingFindingTitles.length,
|
|
62
|
+
summary,
|
|
63
|
+
reviewJsonFile: jsonPath,
|
|
64
|
+
},
|
|
65
|
+
outputs: [
|
|
66
|
+
{
|
|
67
|
+
kind: "artifact",
|
|
68
|
+
path: jsonPath,
|
|
69
|
+
required: true,
|
|
70
|
+
manifest: {
|
|
71
|
+
publish: true,
|
|
72
|
+
schemaId: "review-findings/v1",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
kind: "file",
|
|
77
|
+
path: readyToMergeFile(params.taskKey),
|
|
78
|
+
required: false,
|
|
79
|
+
manifest: {
|
|
80
|
+
publish: true,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -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
|
export const selectFilesFormNode = {
|
|
@@ -65,6 +66,13 @@ export const selectFilesFormNode = {
|
|
|
65
66
|
kind: "artifact",
|
|
66
67
|
path: params.outputFile,
|
|
67
68
|
required: true,
|
|
69
|
+
manifest: {
|
|
70
|
+
publish: true,
|
|
71
|
+
logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
|
|
72
|
+
payloadFamily: "structured-json",
|
|
73
|
+
schemaId: "user-input/v1",
|
|
74
|
+
schemaVersion: 1,
|
|
75
|
+
},
|
|
68
76
|
},
|
|
69
77
|
],
|
|
70
78
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
3
|
+
export const structuredSummaryNode = {
|
|
4
|
+
kind: "structured-summary",
|
|
5
|
+
version: 1,
|
|
6
|
+
async run(_context, params) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = JSON.parse(readFileSync(params.path, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
throw new TaskRunnerError(`Structured summary node could not parse JSON from ${params.path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13
|
+
}
|
|
14
|
+
const summary = typeof parsed.summary === "string" ? parsed.summary.trim() : "";
|
|
15
|
+
if (!summary) {
|
|
16
|
+
throw new TaskRunnerError(`Structured summary node did not find a non-empty summary in ${params.path}.`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
value: {
|
|
20
|
+
summary,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { TaskRunnerError } from "../../errors.js";
|
|
3
|
+
import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
|
|
3
4
|
import { printSummary } from "../../tui.js";
|
|
4
|
-
import { requestUserInputInTerminal, validateUserInputValues, } from "../../user-input.js";
|
|
5
|
+
import { applyInitialUserInputValues, requestUserInputInTerminal, validateUserInputValues, } from "../../user-input.js";
|
|
5
6
|
function labelForSingleValue(field, value) {
|
|
6
7
|
if (field.type !== "single-select" && field.type !== "multi-select") {
|
|
7
8
|
return value;
|
|
@@ -64,6 +65,23 @@ function buildTaskDescribePromptSuffix(params, values) {
|
|
|
64
65
|
: `Task source: user-input\n\n${taskDescription}`,
|
|
65
66
|
};
|
|
66
67
|
}
|
|
68
|
+
function buildInstantTaskPromptSuffix(params, values) {
|
|
69
|
+
const taskDescription = typeof values.task_description === "string" ? values.task_description.trim() : "";
|
|
70
|
+
const additionalInstructions = typeof values.additional_instructions === "string" ? values.additional_instructions.trim() : "";
|
|
71
|
+
return {
|
|
72
|
+
promptSuffix: [
|
|
73
|
+
"Use the manual instant-task request below as the source of truth for task intent.",
|
|
74
|
+
`User input file: ${params.outputFile}`,
|
|
75
|
+
`Task description:\n${taskDescription}`,
|
|
76
|
+
additionalInstructions ? `Additional instructions:\n${additionalInstructions}` : "",
|
|
77
|
+
]
|
|
78
|
+
.filter((item) => item.trim().length > 0)
|
|
79
|
+
.join("\n\n"),
|
|
80
|
+
summaryText: additionalInstructions
|
|
81
|
+
? `Task source: instant-task\n\n${taskDescription}\n\nAdditional instructions:\n${additionalInstructions}`
|
|
82
|
+
: `Task source: instant-task\n\n${taskDescription}`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
67
85
|
function buildPromptSuffix(params, values) {
|
|
68
86
|
if (params.formId === "review-fix-selection") {
|
|
69
87
|
return buildReviewFixPromptSuffix(params, values);
|
|
@@ -71,6 +89,9 @@ function buildPromptSuffix(params, values) {
|
|
|
71
89
|
if (params.formId === "task-describe-source-input") {
|
|
72
90
|
return buildTaskDescribePromptSuffix(params, values);
|
|
73
91
|
}
|
|
92
|
+
if (params.formId === "instant-task-input") {
|
|
93
|
+
return buildInstantTaskPromptSuffix(params, values);
|
|
94
|
+
}
|
|
74
95
|
if (params.fields.length === 0) {
|
|
75
96
|
return {
|
|
76
97
|
promptSuffix: "",
|
|
@@ -101,12 +122,13 @@ export const userInputNode = {
|
|
|
101
122
|
kind: "user-input",
|
|
102
123
|
version: 1,
|
|
103
124
|
async run(context, params) {
|
|
125
|
+
const fields = applyInitialUserInputValues(params.fields, params.initialValues);
|
|
104
126
|
const form = {
|
|
105
127
|
formId: params.formId,
|
|
106
128
|
title: params.title,
|
|
107
129
|
...(params.description ? { description: params.description } : {}),
|
|
108
130
|
...(params.submitLabel ? { submitLabel: params.submitLabel } : {}),
|
|
109
|
-
fields
|
|
131
|
+
fields,
|
|
110
132
|
};
|
|
111
133
|
const requester = context.requestUserInput ?? requestUserInputInTerminal;
|
|
112
134
|
const result = await requester(form);
|
|
@@ -130,7 +152,20 @@ export const userInputNode = {
|
|
|
130
152
|
promptSuffix: rendered.promptSuffix,
|
|
131
153
|
summaryText: rendered.summaryText,
|
|
132
154
|
},
|
|
133
|
-
outputs: [
|
|
155
|
+
outputs: [
|
|
156
|
+
{
|
|
157
|
+
kind: "artifact",
|
|
158
|
+
path: params.outputFile,
|
|
159
|
+
required: true,
|
|
160
|
+
manifest: {
|
|
161
|
+
publish: true,
|
|
162
|
+
logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
|
|
163
|
+
payloadFamily: "structured-json",
|
|
164
|
+
schemaId: "user-input/v1",
|
|
165
|
+
schemaVersion: 1,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
134
169
|
};
|
|
135
170
|
},
|
|
136
171
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
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
|
+
import { normalizeReviewSeverity, resolveBlockingReviewSeverities } from "../../review-severity.js";
|
|
5
6
|
export const writeSelectionFileNode = {
|
|
6
7
|
kind: "write-selection-file",
|
|
7
8
|
version: 1,
|
|
@@ -15,15 +16,16 @@ export const writeSelectionFileNode = {
|
|
|
15
16
|
}
|
|
16
17
|
const reviewFindings = parsed;
|
|
17
18
|
const findings = Array.isArray(reviewFindings.findings) ? reviewFindings.findings : [];
|
|
19
|
+
const blockingSeverities = resolveBlockingReviewSeverities(params.blockingSeverities);
|
|
18
20
|
const selectedFindings = findings
|
|
19
21
|
.filter((finding) => {
|
|
20
|
-
const severity =
|
|
22
|
+
const severity = normalizeReviewSeverity(finding.severity);
|
|
21
23
|
const disposition = typeof finding.disposition === "string" ? finding.disposition.trim().toLowerCase() : null;
|
|
22
|
-
return
|
|
24
|
+
return severity !== null && blockingSeverities.includes(severity) && disposition !== "resolved";
|
|
23
25
|
})
|
|
24
26
|
.map((finding) => finding.title)
|
|
25
27
|
.filter((title) => typeof title === "string" && title.trim().length > 0);
|
|
26
|
-
const applyAll =
|
|
28
|
+
const applyAll = false;
|
|
27
29
|
const artifact = {
|
|
28
30
|
form_id: "review-fix-selection",
|
|
29
31
|
submitted_at: new Date().toISOString(),
|
|
@@ -41,6 +43,20 @@ export const writeSelectionFileNode = {
|
|
|
41
43
|
selectedFindings,
|
|
42
44
|
applyAll,
|
|
43
45
|
},
|
|
46
|
+
outputs: [
|
|
47
|
+
{
|
|
48
|
+
kind: "artifact",
|
|
49
|
+
path: params.outputFile,
|
|
50
|
+
required: true,
|
|
51
|
+
manifest: {
|
|
52
|
+
publish: true,
|
|
53
|
+
logicalKey: buildLogicalKeyForPayload(_context.issueKey, params.outputFile),
|
|
54
|
+
payloadFamily: "structured-json",
|
|
55
|
+
schemaId: "user-input/v1",
|
|
56
|
+
schemaVersion: 1,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
44
60
|
};
|
|
45
61
|
},
|
|
46
62
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { BUG_ANALYZE_PROMPT_TEMPLATE, COMMIT_MESSAGE_PROMPT_TEMPLATE, GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE, GITLAB_REVIEW_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, JIRA_DESCRIPTION_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_QUESTIONS_PROMPT_TEMPLATE,
|
|
1
|
+
import { BUG_ANALYZE_PROMPT_TEMPLATE, COMMIT_MESSAGE_PROMPT_TEMPLATE, DESIGN_REVIEW_PROMPT_TEMPLATE, GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE, GITLAB_REVIEW_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, JIRA_DESCRIPTION_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, PLAN_QUESTIONS_PROMPT_TEMPLATE, PLAN_REVISE_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROJECT_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_CONTEXT_FROM_JIRA_PROMPT_TEMPLATE, TASK_CONTEXT_FROM_MANUAL_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, } from "../prompts.js";
|
|
2
2
|
const promptTemplates = {
|
|
3
3
|
"bug-analyze": BUG_ANALYZE_PROMPT_TEMPLATE,
|
|
4
4
|
"bug-fix": BUG_FIX_PROMPT_TEMPLATE,
|
|
5
5
|
"commit-message": COMMIT_MESSAGE_PROMPT_TEMPLATE,
|
|
6
|
+
"design-review": DESIGN_REVIEW_PROMPT_TEMPLATE,
|
|
6
7
|
"gitlab-diff-review": GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE,
|
|
7
8
|
"gitlab-review": GITLAB_REVIEW_PROMPT_TEMPLATE,
|
|
8
9
|
implement: IMPLEMENT_PROMPT_TEMPLATE,
|
|
@@ -10,12 +11,15 @@ const promptTemplates = {
|
|
|
10
11
|
"mr-description": MR_DESCRIPTION_PROMPT_TEMPLATE,
|
|
11
12
|
"plan-questions": PLAN_QUESTIONS_PROMPT_TEMPLATE,
|
|
12
13
|
plan: PLAN_PROMPT_TEMPLATE,
|
|
14
|
+
"plan-revise": PLAN_REVISE_PROMPT_TEMPLATE,
|
|
13
15
|
review: REVIEW_PROMPT_TEMPLATE,
|
|
14
16
|
"review-project": REVIEW_PROJECT_PROMPT_TEMPLATE,
|
|
15
17
|
"review-fix": REVIEW_FIX_PROMPT_TEMPLATE,
|
|
16
18
|
"review-summary": REVIEW_SUMMARY_PROMPT_TEMPLATE,
|
|
17
19
|
"run-go-linter-loop-fix": RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE,
|
|
18
20
|
"run-go-tests-loop-fix": RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE,
|
|
21
|
+
"task-context-from-jira": TASK_CONTEXT_FROM_JIRA_PROMPT_TEMPLATE,
|
|
22
|
+
"task-context-from-manual": TASK_CONTEXT_FROM_MANUAL_PROMPT_TEMPLATE,
|
|
19
23
|
"task-summary": TASK_SUMMARY_PROMPT_TEMPLATE,
|
|
20
24
|
};
|
|
21
25
|
export function isPromptTemplateRef(value) {
|
|
@@ -2,12 +2,15 @@ import { TaskRunnerError } from "../errors.js";
|
|
|
2
2
|
import { STRUCTURED_JSON_LANGUAGE_INSTRUCTION, formatPrompt, formatTemplate } from "../prompts.js";
|
|
3
3
|
import { getPromptTemplate } from "./prompt-registry.js";
|
|
4
4
|
import { resolveValue } from "./value-resolver.js";
|
|
5
|
+
export function resolvePromptBindingInputs(binding, context) {
|
|
6
|
+
return Object.fromEntries(Object.entries(binding.vars ?? {}).map(([key, value]) => [key, resolveValue(value, context)]));
|
|
7
|
+
}
|
|
5
8
|
export function renderPrompt(binding, context) {
|
|
6
9
|
const baseTemplate = binding.inlineTemplate ?? (binding.templateRef ? getPromptTemplate(binding.templateRef) : null);
|
|
7
10
|
if (!baseTemplate) {
|
|
8
11
|
throw new TaskRunnerError("Prompt binding must define templateRef or inlineTemplate");
|
|
9
12
|
}
|
|
10
|
-
const vars = Object.fromEntries(Object.entries(binding
|
|
13
|
+
const vars = Object.fromEntries(Object.entries(resolvePromptBindingInputs(binding, context)).map(([key, value]) => [key, String(value)]));
|
|
11
14
|
const basePrompt = formatTemplate(baseTemplate, vars);
|
|
12
15
|
const resolvedExtraPrompt = binding.extraPrompt ? resolveValue(binding.extraPrompt, context) : null;
|
|
13
16
|
const extraPrompt = resolvedExtraPrompt === null || resolvedExtraPrompt === undefined ? null : String(resolvedExtraPrompt);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function parseReviewIterationCandidate(value) {
|
|
2
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export function resolveReviewLoopBaseIteration(params) {
|
|
8
|
+
const baseIteration = parseReviewIterationCandidate(params["baseIteration"]);
|
|
9
|
+
if (baseIteration !== undefined) {
|
|
10
|
+
return baseIteration;
|
|
11
|
+
}
|
|
12
|
+
return parseReviewIterationCandidate(params["iteration"]);
|
|
13
|
+
}
|
|
14
|
+
export function withCanonicalReviewLoopParams(flowKind, params) {
|
|
15
|
+
if (flowKind !== "review-loop-flow" && flowKind !== "review-project-loop-flow") {
|
|
16
|
+
return params;
|
|
17
|
+
}
|
|
18
|
+
const baseIteration = resolveReviewLoopBaseIteration(params);
|
|
19
|
+
if (baseIteration === undefined) {
|
|
20
|
+
return params;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
...params,
|
|
24
|
+
baseIteration,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -177,6 +177,7 @@ function expandPhase(phase, repeatVars) {
|
|
|
177
177
|
return {
|
|
178
178
|
id: interpolateText(step.id, repeatVars),
|
|
179
179
|
node: step.node,
|
|
180
|
+
...(step.routingGroup ? { routingGroup: step.routingGroup } : {}),
|
|
180
181
|
...(stepWhen ? { when: stepWhen } : {}),
|
|
181
182
|
...(step.prompt ? { prompt: interpolatePrompt(step.prompt, repeatVars) } : {}),
|
|
182
183
|
...(step.params
|
|
@@ -186,6 +187,7 @@ function expandPhase(phase, repeatVars) {
|
|
|
186
187
|
: {}),
|
|
187
188
|
...(step.expect ? { expect: step.expect.map((item) => interpolateExpectation(item, repeatVars)) } : {}),
|
|
188
189
|
...(stopFlowIf ? { stopFlowIf } : {}),
|
|
190
|
+
...(step.stopFlowOutcome ? { stopFlowOutcome: step.stopFlowOutcome } : {}),
|
|
189
191
|
...(step.after ? { after: step.after.map((item) => interpolateAfterAction(item, repeatVars)) } : {}),
|
|
190
192
|
repeatVars,
|
|
191
193
|
};
|
|
@@ -7,6 +7,8 @@ export const ARTIFACT_REF_KINDS = [
|
|
|
7
7
|
"bug-fix-plan-json-file",
|
|
8
8
|
"design-file",
|
|
9
9
|
"design-json-file",
|
|
10
|
+
"design-review-file",
|
|
11
|
+
"design-review-json-file",
|
|
10
12
|
"gitlab-diff-file",
|
|
11
13
|
"gitlab-diff-json-file",
|
|
12
14
|
"gitlab-diff-review-input-json-file",
|
|
@@ -18,6 +20,7 @@ export const ARTIFACT_REF_KINDS = [
|
|
|
18
20
|
"jira-description-file",
|
|
19
21
|
"jira-description-json-file",
|
|
20
22
|
"jira-task-file",
|
|
23
|
+
"instant-task-input-json-file",
|
|
21
24
|
"mr-description-file",
|
|
22
25
|
"mr-description-json-file",
|
|
23
26
|
"planning-answers-json-file",
|
|
@@ -36,6 +39,8 @@ export const ARTIFACT_REF_KINDS = [
|
|
|
36
39
|
"run-go-linter-result-json-file",
|
|
37
40
|
"run-go-tests-result-json-file",
|
|
38
41
|
"review-summary-file",
|
|
42
|
+
"task-context-file",
|
|
43
|
+
"task-context-json-file",
|
|
39
44
|
"task-summary-file",
|
|
40
45
|
"task-summary-json-file",
|
|
41
46
|
"task-describe-input-json-file",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TaskRunnerError } from "../errors.js";
|
|
2
2
|
import { STRUCTURED_ARTIFACT_SCHEMA_IDS } from "../structured-artifacts.js";
|
|
3
3
|
import { isPromptTemplateRef } from "./prompt-registry.js";
|
|
4
|
+
import { EXECUTION_ROUTING_GROUPS } from "./execution-routing-config.js";
|
|
4
5
|
import { ARTIFACT_LIST_REF_KINDS as artifactListRefKinds, ARTIFACT_REF_KINDS as artifactRefKinds, } from "./spec-types.js";
|
|
5
6
|
function isArtifactRefKind(value) {
|
|
6
7
|
return artifactRefKinds.includes(value);
|
|
@@ -102,6 +103,10 @@ function validateStep(step, nodeRegistry, executorRegistry, path, options) {
|
|
|
102
103
|
assert(executorRegistry.has(executorId), `Unknown executor '${executorId}' required by node '${step.node}' at ${path}.node`);
|
|
103
104
|
}
|
|
104
105
|
validateCondition(step.when, `${path}.when`);
|
|
106
|
+
if (step.routingGroup) {
|
|
107
|
+
assert(step.node === "llm-prompt", `routingGroup is only supported for llm-prompt steps at ${path}.routingGroup`);
|
|
108
|
+
assert(EXECUTION_ROUTING_GROUPS.includes(step.routingGroup), `Unknown routingGroup '${step.routingGroup}' at ${path}.routingGroup`);
|
|
109
|
+
}
|
|
105
110
|
if (step.prompt) {
|
|
106
111
|
assert(nodeMeta.prompt !== "forbidden", `Node '${step.node}' does not accept prompt binding at ${path}.prompt`);
|
|
107
112
|
assert(Boolean(step.prompt.templateRef || step.prompt.inlineTemplate), `Prompt binding at ${path}.prompt must define templateRef or inlineTemplate`);
|
|
@@ -110,6 +115,9 @@ function validateStep(step, nodeRegistry, executorRegistry, path, options) {
|
|
|
110
115
|
for (const requiredParam of nodeMeta.requiredParams ?? []) {
|
|
111
116
|
assert(step.params?.[requiredParam] !== undefined, `Node '${step.node}' requires param '${requiredParam}' at ${path}.params.${requiredParam}`);
|
|
112
117
|
}
|
|
118
|
+
if (step.node === "llm-prompt") {
|
|
119
|
+
assert(step.routingGroup !== undefined || step.params?.executor !== undefined, `Node 'llm-prompt' requires routingGroup or param 'executor' at ${path}`);
|
|
120
|
+
}
|
|
113
121
|
if (step.prompt?.templateRef) {
|
|
114
122
|
assert(isPromptTemplateRef(step.prompt.templateRef), `Unknown prompt template '${step.prompt.templateRef}' at ${path}.prompt.templateRef`);
|
|
115
123
|
}
|
|
@@ -147,6 +155,9 @@ function validateStep(step, nodeRegistry, executorRegistry, path, options) {
|
|
|
147
155
|
if (step.stopFlowIf) {
|
|
148
156
|
validateCondition(step.stopFlowIf, `${path}.stopFlowIf`);
|
|
149
157
|
}
|
|
158
|
+
if (step.stopFlowOutcome) {
|
|
159
|
+
assert(step.stopFlowOutcome === "success" || step.stopFlowOutcome === "stopped", `Unsupported stopFlowOutcome '${step.stopFlowOutcome}' at ${path}.stopFlowOutcome`);
|
|
160
|
+
}
|
|
150
161
|
if (step.after) {
|
|
151
162
|
step.after.forEach((action, index) => validateAfterAction(action, `${path}.after[${index}]`));
|
|
152
163
|
}
|
|
@@ -281,6 +292,9 @@ function validateExpandedCondition(condition, phases, currentPhaseIndex, current
|
|
|
281
292
|
}
|
|
282
293
|
}
|
|
283
294
|
export function validateFlowSpec(spec, nodeRegistry, executorRegistry, options = {}) {
|
|
295
|
+
if (spec.catalogVisibility !== undefined) {
|
|
296
|
+
assert(spec.catalogVisibility === "visible" || spec.catalogVisibility === "hidden", `Unsupported catalogVisibility '${spec.catalogVisibility}' at flow.catalogVisibility`);
|
|
297
|
+
}
|
|
284
298
|
assert(spec.kind.trim().length > 0, "Flow spec kind must be non-empty");
|
|
285
299
|
assert(Number.isInteger(spec.version) && spec.version > 0, "Flow spec version must be a positive integer");
|
|
286
300
|
spec.phases.forEach((item, index) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, gitlabDiffReviewInputJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraAttachmentsContextFile, jiraAttachmentsManifestFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planningAnswersJsonFile, planningQuestionsJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, runGoLinterResultJsonFile, runGoTestsResultJsonFile, taskSummaryFile, taskDescribeInputJsonFile, taskSummaryJsonFile, gitStatusJsonFile, gitCommitMessageJsonFile, gitCommitInputJsonFile, selectFilesOutputJsonFile, commitMessageOutputJsonFile, gitDiffFile as gitDiffFileHelper, } from "../artifacts.js";
|
|
2
|
+
import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, designReviewFile, designReviewJsonFile, gitlabDiffFile, gitlabDiffJsonFile, gitlabDiffReviewInputJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraAttachmentsContextFile, jiraAttachmentsManifestFile, jiraDescriptionFile, jiraDescriptionJsonFile, instantTaskInputJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planningAnswersJsonFile, planningQuestionsJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, runGoLinterResultJsonFile, runGoTestsResultJsonFile, taskSummaryFile, taskDescribeInputJsonFile, taskSummaryJsonFile, taskContextFile, taskContextJsonFile, gitStatusJsonFile, gitCommitMessageJsonFile, gitCommitInputJsonFile, selectFilesOutputJsonFile, commitMessageOutputJsonFile, gitDiffFile as gitDiffFileHelper, } from "../artifacts.js";
|
|
3
3
|
import { TaskRunnerError } from "../errors.js";
|
|
4
4
|
import { formatTemplate } from "../prompts.js";
|
|
5
5
|
function readStepRef(segments, context, originalPath) {
|
|
@@ -88,6 +88,16 @@ function resolveArtifact(spec, context) {
|
|
|
88
88
|
return designFile(taskKey, iteration);
|
|
89
89
|
case "design-json-file":
|
|
90
90
|
return designJsonFile(taskKey, iteration);
|
|
91
|
+
case "design-review-file":
|
|
92
|
+
if (iteration === undefined) {
|
|
93
|
+
throw new TaskRunnerError("design-review-file requires iteration");
|
|
94
|
+
}
|
|
95
|
+
return designReviewFile(taskKey, iteration);
|
|
96
|
+
case "design-review-json-file":
|
|
97
|
+
if (iteration === undefined) {
|
|
98
|
+
throw new TaskRunnerError("design-review-json-file requires iteration");
|
|
99
|
+
}
|
|
100
|
+
return designReviewJsonFile(taskKey, iteration);
|
|
91
101
|
case "gitlab-diff-file":
|
|
92
102
|
return gitlabDiffFile(taskKey, iteration);
|
|
93
103
|
case "gitlab-diff-json-file":
|
|
@@ -110,6 +120,8 @@ function resolveArtifact(spec, context) {
|
|
|
110
120
|
return jiraDescriptionJsonFile(taskKey, iteration);
|
|
111
121
|
case "jira-task-file":
|
|
112
122
|
return jiraTaskFile(taskKey);
|
|
123
|
+
case "instant-task-input-json-file":
|
|
124
|
+
return instantTaskInputJsonFile(taskKey);
|
|
113
125
|
case "mr-description-file":
|
|
114
126
|
return mrDescriptionFile(taskKey, iteration);
|
|
115
127
|
case "mr-description-json-file":
|
|
@@ -173,6 +185,10 @@ function resolveArtifact(spec, context) {
|
|
|
173
185
|
throw new TaskRunnerError("review-summary-file requires iteration");
|
|
174
186
|
}
|
|
175
187
|
return artifactFile("review-summary", taskKey, iteration);
|
|
188
|
+
case "task-context-file":
|
|
189
|
+
return taskContextFile(taskKey, iteration);
|
|
190
|
+
case "task-context-json-file":
|
|
191
|
+
return taskContextJsonFile(taskKey, iteration);
|
|
176
192
|
case "task-summary-file":
|
|
177
193
|
return taskSummaryFile(taskKey, iteration);
|
|
178
194
|
case "task-summary-json-file":
|
|
@@ -253,6 +269,73 @@ export function resolveParams(params, context) {
|
|
|
253
269
|
}
|
|
254
270
|
return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, resolveValue(value, context)]));
|
|
255
271
|
}
|
|
272
|
+
export const ARTIFACT_LINEAGE_REF_PATHS_PARAM = "__artifactLineageRefPaths";
|
|
273
|
+
function collectAnnotatedRefArtifactPathCandidates(refPath, context) {
|
|
274
|
+
const annotatedRefPaths = context.flowParams[ARTIFACT_LINEAGE_REF_PATHS_PARAM];
|
|
275
|
+
if (!annotatedRefPaths || typeof annotatedRefPaths !== "object" || Array.isArray(annotatedRefPaths)) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
const candidate = annotatedRefPaths[refPath];
|
|
279
|
+
const paths = typeof candidate === "string" ? [candidate] : Array.isArray(candidate) ? candidate : [];
|
|
280
|
+
return paths.filter((value) => typeof value === "string" && existsSync(value));
|
|
281
|
+
}
|
|
282
|
+
export function collectResolvedArtifactPathCandidates(value, context) {
|
|
283
|
+
const candidates = new Set();
|
|
284
|
+
const addCandidate = (candidatePath) => {
|
|
285
|
+
if (existsSync(candidatePath)) {
|
|
286
|
+
candidates.add(candidatePath);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const visit = (current) => {
|
|
290
|
+
if (!current || "const" in current) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if ("ref" in current) {
|
|
294
|
+
collectAnnotatedRefArtifactPathCandidates(current.ref, context).forEach((candidatePath) => addCandidate(candidatePath));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if ("artifact" in current) {
|
|
298
|
+
addCandidate(resolveArtifact(current.artifact, context));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if ("artifactList" in current) {
|
|
302
|
+
resolveArtifactList(current.artifactList, context).forEach((candidatePath) => addCandidate(candidatePath));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if ("template" in current) {
|
|
306
|
+
Object.values(current.vars ?? {}).forEach((candidate) => visit(candidate));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if ("appendPrompt" in current) {
|
|
310
|
+
visit(current.appendPrompt.base);
|
|
311
|
+
visit(current.appendPrompt.suffix);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if ("add" in current) {
|
|
315
|
+
current.add.forEach((candidate) => visit(candidate));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if ("concat" in current) {
|
|
319
|
+
current.concat.forEach((candidate) => visit(candidate));
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if ("list" in current) {
|
|
323
|
+
current.list.forEach((candidate) => visit(candidate));
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
visit(value);
|
|
327
|
+
return Array.from(candidates).sort();
|
|
328
|
+
}
|
|
329
|
+
export function collectResolvedPromptArtifactPathCandidates(binding, context) {
|
|
330
|
+
const candidates = new Set();
|
|
331
|
+
for (const value of Object.values(binding?.vars ?? {})) {
|
|
332
|
+
collectResolvedArtifactPathCandidates(value, context).forEach((candidate) => candidates.add(candidate));
|
|
333
|
+
}
|
|
334
|
+
if (binding?.extraPrompt) {
|
|
335
|
+
collectResolvedArtifactPathCandidates(binding.extraPrompt, context).forEach((candidate) => candidates.add(candidate));
|
|
336
|
+
}
|
|
337
|
+
return Array.from(candidates).sort();
|
|
338
|
+
}
|
|
256
339
|
function truthy(value) {
|
|
257
340
|
return Boolean(value);
|
|
258
341
|
}
|