agentweaver 0.1.16 → 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 (40) hide show
  1. package/README.md +50 -10
  2. package/dist/artifacts.js +73 -3
  3. package/dist/doctor/checks/executors.js +2 -2
  4. package/dist/flow-state.js +138 -1
  5. package/dist/index.js +175 -61
  6. package/dist/interactive/controller.js +56 -23
  7. package/dist/interactive/ink/index.js +22 -1
  8. package/dist/interactive/tree.js +2 -2
  9. package/dist/pipeline/auto-flow.js +9 -6
  10. package/dist/pipeline/context.js +6 -5
  11. package/dist/pipeline/declarative-flows.js +39 -20
  12. package/dist/pipeline/flow-catalog.js +36 -14
  13. package/dist/pipeline/flow-specs/auto-common.json +1 -0
  14. package/dist/pipeline/flow-specs/auto-golang.json +27 -1
  15. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +13 -1
  16. package/dist/pipeline/flow-specs/plan.json +4 -2
  17. package/dist/pipeline/launch-profile-config.js +30 -18
  18. package/dist/pipeline/node-contract.js +1 -0
  19. package/dist/pipeline/node-registry.js +74 -5
  20. package/dist/pipeline/nodes/flow-run-node.js +188 -173
  21. package/dist/pipeline/nodes/llm-prompt-node.js +15 -33
  22. package/dist/pipeline/plugin-loader.js +389 -0
  23. package/dist/pipeline/plugin-types.js +1 -0
  24. package/dist/pipeline/registry.js +71 -4
  25. package/dist/pipeline/spec-compiler.js +1 -0
  26. package/dist/pipeline/spec-loader.js +14 -0
  27. package/dist/pipeline/spec-validator.js +6 -0
  28. package/dist/pipeline/value-resolver.js +2 -1
  29. package/dist/plugin-sdk.js +1 -0
  30. package/dist/runtime/artifact-registry.js +3 -0
  31. package/dist/runtime/execution-routing.js +25 -19
  32. package/dist/runtime/interactive-execution-routing.js +66 -57
  33. package/docs/example/.flows/examples/claude-example.json +50 -0
  34. package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
  35. package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
  36. package/docs/examples/.flows/claude-example.json +50 -0
  37. package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
  38. package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
  39. package/docs/plugin-sdk.md +731 -0
  40. package/package.json +6 -2
@@ -32,6 +32,43 @@ import { summaryFileLoadNode } from "./nodes/summary-file-load-node.js";
32
32
  import { telegramNotifierNode } from "./nodes/telegram-notifier-node.js";
33
33
  import { userInputNode } from "./nodes/user-input-node.js";
34
34
  import { writeSelectionFileNode } from "./nodes/write-selection-file-node.js";
35
+ import { TaskRunnerError } from "../errors.js";
36
+ export const BUILT_IN_NODE_KINDS = [
37
+ "build-failure-summary",
38
+ "build-review-fix-prompt",
39
+ "clear-ready-to-merge",
40
+ "codex-prompt",
41
+ "command-check",
42
+ "commit-message-form",
43
+ "design-review-verdict",
44
+ "ensure-summary-json",
45
+ "fetch-gitlab-diff",
46
+ "fetch-gitlab-review",
47
+ "file-check",
48
+ "flow-run",
49
+ "git-commit",
50
+ "git-commit-form",
51
+ "git-status",
52
+ "gitlab-review-artifacts",
53
+ "jira-context",
54
+ "jira-fetch",
55
+ "jira-issue-check",
56
+ "local-script-check",
57
+ "llm-prompt",
58
+ "opencode-prompt",
59
+ "plan-codex",
60
+ "planning-bundle",
61
+ "planning-questions-form",
62
+ "read-file",
63
+ "review-findings-form",
64
+ "review-verdict",
65
+ "select-files-form",
66
+ "structured-summary",
67
+ "summary-file-load",
68
+ "telegram-notify",
69
+ "user-input",
70
+ "write-selection-file",
71
+ ];
35
72
  const builtInNodes = {
36
73
  "build-failure-summary": buildFailureSummaryNode,
37
74
  "build-review-fix-prompt": buildReviewFixPromptNode,
@@ -249,19 +286,51 @@ const builtInNodeMetadata = {
249
286
  requiredParams: ["outputFile", "reviewFindingsJsonFile", "selectionMode"],
250
287
  },
251
288
  };
252
- export function createNodeRegistry() {
289
+ function coreOwner(id) {
290
+ return {
291
+ kind: "core",
292
+ id: `core:${id}`,
293
+ manifestPath: "built-in node registry",
294
+ };
295
+ }
296
+ export function createNodeRegistry(pluginNodes = []) {
297
+ const definitions = new Map(Object.entries(builtInNodes));
298
+ const metadata = new Map(Object.entries(builtInNodeMetadata));
299
+ const owners = new Map(Object.keys(builtInNodes).map((id) => [id, coreOwner(id)]));
300
+ for (const registration of pluginNodes) {
301
+ const existingOwner = owners.get(registration.id);
302
+ if (existingOwner) {
303
+ throw new TaskRunnerError(`Duplicate node id '${registration.id}' conflicts between ${existingOwner.id} (${existingOwner.manifestPath}) and plugin '${registration.pluginId}' (${registration.manifestPath}).`);
304
+ }
305
+ definitions.set(registration.id, registration.definition);
306
+ metadata.set(registration.id, registration.metadata);
307
+ owners.set(registration.id, {
308
+ kind: "plugin",
309
+ id: registration.pluginId,
310
+ manifestPath: registration.manifestPath,
311
+ entrypointPath: registration.entrypointPath,
312
+ });
313
+ }
253
314
  return {
254
315
  get(kind) {
255
- return builtInNodes[kind];
316
+ const definition = definitions.get(kind);
317
+ if (!definition) {
318
+ throw new TaskRunnerError(`Unknown node kind '${kind}'.`);
319
+ }
320
+ return definition;
256
321
  },
257
322
  getMeta(kind) {
258
- return builtInNodeMetadata[kind];
323
+ const definition = metadata.get(kind);
324
+ if (!definition) {
325
+ throw new TaskRunnerError(`Unknown node metadata '${kind}'.`);
326
+ }
327
+ return definition;
259
328
  },
260
329
  has(kind) {
261
- return kind in builtInNodes;
330
+ return definitions.has(kind);
262
331
  },
263
332
  kinds() {
264
- return Object.keys(builtInNodes);
333
+ return [...definitions.keys()];
265
334
  },
266
335
  };
267
336
  }
@@ -1,4 +1,5 @@
1
1
  import { printInfo } from "../../tui.js";
2
+ import { nextArtifactIteration } from "../../artifacts.js";
2
3
  import { resolveDesignReviewInputContract } from "../../runtime/design-review-input-contract.js";
3
4
  import { resolvePlanReviseInputContract } from "../../runtime/plan-revise-input-contract.js";
4
5
  import { inspectReviewInputContract } from "../../runtime/review-input-contract.js";
@@ -20,6 +21,189 @@ function withArtifactLineageRefPaths(params, lineageRefs) {
20
21
  [ARTIFACT_LINEAGE_REF_PATHS_PARAM]: merged,
21
22
  };
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
+ }
23
207
  export const flowRunNode = {
24
208
  kind: "flow-run",
25
209
  version: 1,
@@ -31,179 +215,10 @@ export const flowRunNode = {
31
215
  if (labelText) {
32
216
  printInfo(String(labelText));
33
217
  }
34
- const flow = loadNamedDeclarativeFlow(fileName, context.cwd);
35
- let resolvedFlowParams = withCanonicalReviewLoopParams(flow.kind, flowParams);
36
- if (flow.kind === "design-review-flow") {
37
- const taskKey = String(flowParams["taskKey"] ?? "");
38
- if (taskKey) {
39
- const contract = resolveDesignReviewInputContract(taskKey);
40
- resolvedFlowParams = withArtifactLineageRefPaths({
41
- ...flowParams,
42
- iteration: contract.planningIteration,
43
- planningIteration: contract.planningIteration,
44
- designFile: contract.designFile,
45
- designJsonFile: contract.designJsonFile,
46
- planFile: contract.planFile,
47
- planJsonFile: contract.planJsonFile,
48
- hasQaArtifacts: contract.hasQaArtifacts,
49
- qaFilePath: contract.qaFilePath,
50
- qaJsonFilePath: contract.qaJsonFilePath,
51
- qaFile: contract.qaFile,
52
- qaJsonFile: contract.qaJsonFile,
53
- hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
54
- taskContextJsonFilePath: contract.taskContextJsonFilePath,
55
- taskContextJsonFile: contract.taskContextJsonFile,
56
- hasJiraTaskFile: contract.hasJiraTaskFile,
57
- jiraTaskFilePath: contract.jiraTaskFilePath,
58
- jiraTaskFile: contract.jiraTaskFile,
59
- hasJiraAttachmentsManifestFile: contract.hasJiraAttachmentsManifestFile,
60
- jiraAttachmentsManifestFilePath: contract.jiraAttachmentsManifestFilePath,
61
- jiraAttachmentsManifestFile: contract.jiraAttachmentsManifestFile,
62
- hasJiraAttachmentsContextFile: contract.hasJiraAttachmentsContextFile,
63
- jiraAttachmentsContextFilePath: contract.jiraAttachmentsContextFilePath,
64
- jiraAttachmentsContextFile: contract.jiraAttachmentsContextFile,
65
- hasPlanningAnswersJsonFile: contract.hasPlanningAnswersJsonFile,
66
- planningAnswersJsonFilePath: contract.planningAnswersJsonFilePath,
67
- planningAnswersJsonFile: contract.planningAnswersJsonFile,
68
- hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
69
- taskInputJsonFilePath: contract.taskInputJsonFilePath,
70
- taskInputJsonFile: contract.taskInputJsonFile,
71
- }, {
72
- "params.designFile": contract.designFile,
73
- "params.designJsonFile": contract.designJsonFile,
74
- "params.planFile": contract.planFile,
75
- "params.planJsonFile": contract.planJsonFile,
76
- ...(contract.qaFilePath ? { "params.qaFile": contract.qaFilePath } : {}),
77
- ...(contract.qaJsonFilePath ? { "params.qaJsonFile": contract.qaJsonFilePath } : {}),
78
- ...(contract.taskContextJsonFilePath
79
- ? { "params.taskContextJsonFile": contract.taskContextJsonFilePath }
80
- : {}),
81
- ...(contract.jiraTaskFilePath ? { "params.jiraTaskFile": contract.jiraTaskFilePath } : {}),
82
- ...(contract.jiraAttachmentsManifestFilePath
83
- ? { "params.jiraAttachmentsManifestFile": contract.jiraAttachmentsManifestFilePath }
84
- : {}),
85
- ...(contract.jiraAttachmentsContextFilePath
86
- ? { "params.jiraAttachmentsContextFile": contract.jiraAttachmentsContextFilePath }
87
- : {}),
88
- ...(contract.planningAnswersJsonFilePath
89
- ? { "params.planningAnswersJsonFile": contract.planningAnswersJsonFilePath }
90
- : {}),
91
- ...(contract.taskInputJsonFilePath
92
- ? { "params.taskInputJsonFile": contract.taskInputJsonFilePath }
93
- : {}),
94
- });
95
- }
96
- }
97
- else if (flow.kind === "plan-revise-flow") {
98
- const taskKey = String(flowParams["taskKey"] ?? "");
99
- if (taskKey) {
100
- const contract = resolvePlanReviseInputContract(taskKey);
101
- resolvedFlowParams = withArtifactLineageRefPaths({
102
- ...flowParams,
103
- reviewIteration: contract.reviewIteration,
104
- reviewFile: contract.reviewFile,
105
- reviewJsonFile: contract.reviewJsonFile,
106
- sourcePlanningIteration: contract.sourcePlanningIteration,
107
- outputIteration: contract.outputIteration,
108
- designFile: contract.designFile,
109
- designJsonFile: contract.designJsonFile,
110
- planFile: contract.planFile,
111
- planJsonFile: contract.planJsonFile,
112
- hasQaArtifacts: contract.hasQaArtifacts,
113
- qaFilePath: contract.qaFilePath,
114
- qaJsonFilePath: contract.qaJsonFilePath,
115
- qaFile: contract.qaFile,
116
- qaJsonFile: contract.qaJsonFile,
117
- revisedDesignFile: contract.revisedDesignFile,
118
- revisedDesignJsonFile: contract.revisedDesignJsonFile,
119
- revisedPlanFile: contract.revisedPlanFile,
120
- revisedPlanJsonFile: contract.revisedPlanJsonFile,
121
- revisedQaFile: contract.revisedQaFile,
122
- revisedQaJsonFile: contract.revisedQaJsonFile,
123
- hasTaskContextJsonFile: contract.hasTaskContextJsonFile,
124
- taskContextJsonFilePath: contract.taskContextJsonFilePath,
125
- taskContextJsonFile: contract.taskContextJsonFile,
126
- hasJiraTaskFile: contract.hasJiraTaskFile,
127
- jiraTaskFilePath: contract.jiraTaskFilePath,
128
- jiraTaskFile: contract.jiraTaskFile,
129
- hasJiraAttachmentsManifestFile: contract.hasJiraAttachmentsManifestFile,
130
- jiraAttachmentsManifestFilePath: contract.jiraAttachmentsManifestFilePath,
131
- jiraAttachmentsManifestFile: contract.jiraAttachmentsManifestFile,
132
- hasJiraAttachmentsContextFile: contract.hasJiraAttachmentsContextFile,
133
- jiraAttachmentsContextFilePath: contract.jiraAttachmentsContextFilePath,
134
- jiraAttachmentsContextFile: contract.jiraAttachmentsContextFile,
135
- hasPlanningAnswersJsonFile: contract.hasPlanningAnswersJsonFile,
136
- planningAnswersJsonFilePath: contract.planningAnswersJsonFilePath,
137
- planningAnswersJsonFile: contract.planningAnswersJsonFile,
138
- hasTaskInputJsonFile: contract.hasTaskInputJsonFile,
139
- taskInputJsonFilePath: contract.taskInputJsonFilePath,
140
- taskInputJsonFile: contract.taskInputJsonFile,
141
- }, {
142
- "params.reviewFile": contract.reviewFile,
143
- "params.reviewJsonFile": contract.reviewJsonFile,
144
- "params.designFile": contract.designFile,
145
- "params.designJsonFile": contract.designJsonFile,
146
- "params.planFile": contract.planFile,
147
- "params.planJsonFile": contract.planJsonFile,
148
- ...(contract.qaFilePath ? { "params.qaFile": contract.qaFilePath } : {}),
149
- ...(contract.qaJsonFilePath ? { "params.qaJsonFile": contract.qaJsonFilePath } : {}),
150
- ...(contract.taskContextJsonFilePath
151
- ? { "params.taskContextJsonFile": contract.taskContextJsonFilePath }
152
- : {}),
153
- ...(contract.jiraTaskFilePath ? { "params.jiraTaskFile": contract.jiraTaskFilePath } : {}),
154
- ...(contract.jiraAttachmentsManifestFilePath
155
- ? { "params.jiraAttachmentsManifestFile": contract.jiraAttachmentsManifestFilePath }
156
- : {}),
157
- ...(contract.jiraAttachmentsContextFilePath
158
- ? { "params.jiraAttachmentsContextFile": contract.jiraAttachmentsContextFilePath }
159
- : {}),
160
- ...(contract.planningAnswersJsonFilePath
161
- ? { "params.planningAnswersJsonFile": contract.planningAnswersJsonFilePath }
162
- : {}),
163
- ...(contract.taskInputJsonFilePath
164
- ? { "params.taskInputJsonFile": contract.taskInputJsonFilePath }
165
- : {}),
166
- });
167
- }
168
- }
169
- else if (flow.kind === "review-flow") {
170
- const taskKey = String(flowParams["taskKey"] ?? "");
171
- if (taskKey) {
172
- const inspection = inspectReviewInputContract(taskKey);
173
- if (inspection.status === "ready") {
174
- const { contract } = inspection;
175
- resolvedFlowParams = 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
- }
206
- }
218
+ const flow = await loadNamedDeclarativeFlow(fileName, context.cwd, {
219
+ ...(context.registryContext ? { registryContext: context.registryContext } : {}),
220
+ });
221
+ const resolvedFlowParams = resolveNestedFlowParams(flow.kind, flowParams);
207
222
  const resumeValue = isFlowRunResumeEnvelope(context.resumeStepValue)
208
223
  && context.resumeStepValue.flowKind === flow.kind
209
224
  && context.resumeStepValue.flowVersion === flow.version
@@ -26,46 +26,28 @@ export const llmPromptNode = {
26
26
  const model = params.routingGroup
27
27
  ? routedProfile?.model ?? params.model ?? fallbackProfile?.model
28
28
  : params.model ?? fallbackProfile?.model;
29
- if (!executor || !isLlmExecutorId(executor)) {
29
+ if (!executor || !isLlmExecutorId(executor, context.executors)) {
30
30
  throw new TaskRunnerError(`Unsupported llm executor '${String(executor ?? params.executor ?? "undefined")}'.`);
31
31
  }
32
- if (model && !isAllowedModelForExecutor(executor, model)) {
32
+ if (model && !isAllowedModelForExecutor(executor, model, context.executors)) {
33
33
  throw new TaskRunnerError(`Model '${model}' is not allowed for executor '${executor}'.`);
34
34
  }
35
35
  printInfo(params.labelText);
36
36
  printPrompt(`LLM:${executor}`, params.prompt);
37
37
  const executorContext = toExecutorContext(context);
38
- if (executor === "codex") {
39
- const executor = context.executors.get("codex");
40
- const value = await executor.execute(executorContext, {
41
- prompt: params.prompt,
42
- ...(model ? { model } : {}),
43
- env: { ...context.env },
44
- }, executor.defaultConfig);
45
- return {
46
- value: {
47
- ...value,
48
- executor: "codex",
49
- },
50
- outputs: outputsForArtifacts(params.requiredArtifacts),
51
- };
52
- }
53
- if (executor === "opencode") {
54
- const executor = context.executors.get("opencode");
55
- const value = await executor.execute(executorContext, {
56
- prompt: params.prompt,
57
- ...(model ? { model } : {}),
58
- env: { ...context.env },
59
- }, executor.defaultConfig);
60
- return {
61
- value: {
62
- ...value,
63
- executor: "opencode",
64
- },
65
- outputs: outputsForArtifacts(params.requiredArtifacts),
66
- };
67
- }
68
- throw new TaskRunnerError(`Unsupported llm executor '${executor}'.`);
38
+ const resolvedExecutor = context.executors.get(executor);
39
+ const value = await resolvedExecutor.execute(executorContext, {
40
+ prompt: params.prompt,
41
+ ...(model ? { model } : {}),
42
+ env: { ...context.env },
43
+ }, resolvedExecutor.defaultConfig);
44
+ return {
45
+ value: {
46
+ ...value,
47
+ executor,
48
+ },
49
+ outputs: outputsForArtifacts(params.requiredArtifacts),
50
+ };
69
51
  },
70
52
  checks(_context, params) {
71
53
  if (!params.requiredArtifacts || params.requiredArtifacts.length === 0) {