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.
Files changed (100) hide show
  1. package/README.md +29 -7
  2. package/dist/artifact-manifest.js +219 -0
  3. package/dist/artifacts.js +21 -1
  4. package/dist/doctor/checks/cwd-context.js +4 -3
  5. package/dist/doctor/checks/env-diagnostics.js +193 -71
  6. package/dist/doctor/checks/flow-readiness.js +212 -203
  7. package/dist/doctor/index.js +1 -1
  8. package/dist/doctor/orchestrator.js +18 -7
  9. package/dist/doctor/runner.js +9 -8
  10. package/dist/doctor/types.js +12 -0
  11. package/dist/flow-state.js +75 -15
  12. package/dist/index.js +499 -199
  13. package/dist/interactive/blessed-session.js +361 -0
  14. package/dist/interactive/controller.js +1293 -0
  15. package/dist/interactive/create-interactive-session.js +5 -0
  16. package/dist/interactive/ink/index.js +576 -0
  17. package/dist/interactive/progress.js +245 -0
  18. package/dist/interactive/selectors.js +14 -0
  19. package/dist/interactive/session.js +1 -0
  20. package/dist/interactive/state.js +34 -0
  21. package/dist/interactive/tree.js +155 -0
  22. package/dist/interactive/types.js +1 -0
  23. package/dist/interactive/view-model.js +1 -0
  24. package/dist/interactive-ui.js +159 -194
  25. package/dist/pipeline/context.js +1 -0
  26. package/dist/pipeline/declarative-flow-runner.js +212 -6
  27. package/dist/pipeline/declarative-flows.js +27 -0
  28. package/dist/pipeline/execution-routing-config.js +15 -0
  29. package/dist/pipeline/flow-catalog.js +23 -3
  30. package/dist/pipeline/flow-run-resume.js +29 -0
  31. package/dist/pipeline/flow-specs/auto-common.json +89 -360
  32. package/dist/pipeline/flow-specs/auto-golang.json +58 -363
  33. package/dist/pipeline/flow-specs/auto-simple.json +141 -0
  34. package/dist/pipeline/flow-specs/bugz/bug-analyze.json +2 -0
  35. package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
  36. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +304 -0
  37. package/dist/pipeline/flow-specs/design-review.json +249 -0
  38. package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +11 -0
  39. package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +2 -0
  40. package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
  41. package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +2 -0
  42. package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +2 -0
  43. package/dist/pipeline/flow-specs/implement.json +24 -5
  44. package/dist/pipeline/flow-specs/instant-task.json +177 -0
  45. package/dist/pipeline/flow-specs/normalize-task-source.json +311 -0
  46. package/dist/pipeline/flow-specs/plan-revise.json +267 -0
  47. package/dist/pipeline/flow-specs/plan.json +48 -70
  48. package/dist/pipeline/flow-specs/review/review-fix.json +24 -4
  49. package/dist/pipeline/flow-specs/review/review-loop.json +351 -45
  50. package/dist/pipeline/flow-specs/review/review-project-loop.json +590 -0
  51. package/dist/pipeline/flow-specs/review/review-project.json +12 -0
  52. package/dist/pipeline/flow-specs/review/review.json +37 -31
  53. package/dist/pipeline/flow-specs/task-describe.json +62 -2
  54. package/dist/pipeline/flow-specs/task-source/jira-fetch.json +70 -0
  55. package/dist/pipeline/flow-specs/task-source/manual-input.json +216 -0
  56. package/dist/pipeline/node-registry.js +49 -1
  57. package/dist/pipeline/node-runner.js +3 -2
  58. package/dist/pipeline/nodes/build-review-fix-prompt-node.js +5 -1
  59. package/dist/pipeline/nodes/clear-ready-to-merge-node.js +11 -0
  60. package/dist/pipeline/nodes/commit-message-form-node.js +8 -0
  61. package/dist/pipeline/nodes/design-review-verdict-node.js +36 -0
  62. package/dist/pipeline/nodes/ensure-summary-json-node.js +70 -0
  63. package/dist/pipeline/nodes/fetch-gitlab-diff-node.js +19 -2
  64. package/dist/pipeline/nodes/fetch-gitlab-review-node.js +19 -2
  65. package/dist/pipeline/nodes/flow-run-node.js +226 -7
  66. package/dist/pipeline/nodes/git-commit-form-node.js +8 -0
  67. package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +19 -2
  68. package/dist/pipeline/nodes/jira-fetch-node.js +50 -4
  69. package/dist/pipeline/nodes/llm-prompt-node.js +32 -12
  70. package/dist/pipeline/nodes/planning-bundle-node.js +10 -0
  71. package/dist/pipeline/nodes/review-verdict-node.js +86 -0
  72. package/dist/pipeline/nodes/select-files-form-node.js +8 -0
  73. package/dist/pipeline/nodes/structured-summary-node.js +24 -0
  74. package/dist/pipeline/nodes/user-input-node.js +38 -3
  75. package/dist/pipeline/nodes/write-selection-file-node.js +20 -4
  76. package/dist/pipeline/prompt-registry.js +5 -1
  77. package/dist/pipeline/prompt-runtime.js +4 -1
  78. package/dist/pipeline/review-iteration.js +26 -0
  79. package/dist/pipeline/spec-compiler.js +2 -0
  80. package/dist/pipeline/spec-types.js +5 -0
  81. package/dist/pipeline/spec-validator.js +14 -0
  82. package/dist/pipeline/value-resolver.js +84 -1
  83. package/dist/prompts.js +82 -13
  84. package/dist/review-severity.js +45 -0
  85. package/dist/runtime/artifact-registry.js +402 -0
  86. package/dist/runtime/design-review-input-contract.js +113 -0
  87. package/dist/runtime/env-loader.js +3 -0
  88. package/dist/runtime/execution-routing-store.js +134 -0
  89. package/dist/runtime/execution-routing.js +227 -0
  90. package/dist/runtime/interactive-execution-routing.js +462 -0
  91. package/dist/runtime/plan-revise-input-contract.js +147 -0
  92. package/dist/runtime/planning-bundle.js +123 -0
  93. package/dist/runtime/ready-to-merge.js +31 -0
  94. package/dist/runtime/review-input-contract.js +100 -0
  95. package/dist/scope.js +11 -2
  96. package/dist/structured-artifact-schema-registry.js +10 -0
  97. package/dist/structured-artifact-schemas.json +257 -1
  98. package/dist/structured-artifacts.js +83 -6
  99. package/dist/user-input.js +70 -3
  100. package/package.json +6 -3
@@ -1,9 +1,12 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { buildLogicalKeyForPayload } from "../artifact-manifest.js";
1
5
  import { TaskRunnerError } from "../errors.js";
2
- import { readFileSync } from "node:fs";
3
6
  import { runNodeChecks } from "./checks.js";
4
7
  import { runNodeByKind } from "./node-runner.js";
5
- import { renderPrompt } from "./prompt-runtime.js";
6
- import { evaluateCondition, resolveParams, resolveValue } from "./value-resolver.js";
8
+ import { renderPrompt, resolvePromptBindingInputs } from "./prompt-runtime.js";
9
+ import { collectResolvedArtifactPathCandidates, collectResolvedPromptArtifactPathCandidates, evaluateCondition, resolveParams, resolveValue, } from "./value-resolver.js";
7
10
  function nowIso8601() {
8
11
  return new Date().toISOString();
9
12
  }
@@ -24,12 +27,17 @@ function toJsonValue(value) {
24
27
  }
25
28
  function ensureExecutionState(options) {
26
29
  if (options.executionState) {
30
+ options.executionState.runId ??= randomUUID();
31
+ options.executionState.publicationRunId ??= randomUUID();
27
32
  return options.executionState;
28
33
  }
29
34
  return {
35
+ runId: randomUUID(),
36
+ publicationRunId: randomUUID(),
30
37
  flowKind: options.flowKind ?? "declarative-flow",
31
38
  flowVersion: options.flowVersion ?? 1,
32
39
  terminated: false,
40
+ terminationOutcome: "success",
33
41
  phases: [],
34
42
  };
35
43
  }
@@ -138,6 +146,129 @@ function runAfterAction(action, pipelineContext, context) {
138
146
  }
139
147
  throw new TaskRunnerError(`Unsupported after action kind: ${action.kind ?? "unknown"}`);
140
148
  }
149
+ function shouldPublishManifest(output) {
150
+ return output.manifest?.publish === true;
151
+ }
152
+ function resolveOutputLogicalKey(scopeKey, output) {
153
+ return output.manifest?.logicalKey ?? buildLogicalKeyForPayload(scopeKey, output.path);
154
+ }
155
+ function validateDistinctLogicalKeys(scopeKey, phaseId, stepId, outputs) {
156
+ const pathsByLogicalKey = new Map();
157
+ for (const output of outputs) {
158
+ const logicalKey = resolveOutputLogicalKey(scopeKey, output);
159
+ const existingPath = pathsByLogicalKey.get(logicalKey);
160
+ if (existingPath && existingPath !== output.path) {
161
+ throw new TaskRunnerError(`Step '${phaseId}:${stepId}' produced duplicate logical_key '${logicalKey}' for outputs '${existingPath}' and '${output.path}'.`);
162
+ }
163
+ pathsByLogicalKey.set(logicalKey, output.path);
164
+ }
165
+ }
166
+ function producerMetadata(resultValue) {
167
+ if (!resultValue || typeof resultValue !== "object" || Array.isArray(resultValue)) {
168
+ return {};
169
+ }
170
+ const record = resultValue;
171
+ return {
172
+ ...(typeof record["executor"] === "string" ? { executor: record["executor"] } : {}),
173
+ ...(typeof record["model"] === "string" ? { model: record["model"] } : {}),
174
+ };
175
+ }
176
+ function collectNestedPublishedArtifacts(resultValue) {
177
+ if (!resultValue || typeof resultValue !== "object" || Array.isArray(resultValue)) {
178
+ return [];
179
+ }
180
+ const rawArtifacts = resultValue["publishedArtifacts"];
181
+ if (!Array.isArray(rawArtifacts)) {
182
+ return [];
183
+ }
184
+ return rawArtifacts.filter((artifact) => {
185
+ if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) {
186
+ return false;
187
+ }
188
+ const record = artifact;
189
+ return typeof record["artifact_id"] === "string" && typeof record["payload_path"] === "string";
190
+ });
191
+ }
192
+ function mergePublishedArtifacts(...groups) {
193
+ const merged = [];
194
+ const seen = new Set();
195
+ for (const group of groups) {
196
+ for (const artifact of group) {
197
+ const key = artifact.artifact_id;
198
+ if (seen.has(key)) {
199
+ continue;
200
+ }
201
+ seen.add(key);
202
+ merged.push(artifact);
203
+ }
204
+ }
205
+ return merged;
206
+ }
207
+ function normalizeCandidatePath(value) {
208
+ return path.resolve(value);
209
+ }
210
+ function collectLineageInputs(values, artifactPaths, pipelineContext, excludedPaths = []) {
211
+ const seen = new Set();
212
+ const inputs = [];
213
+ const excluded = new Set(Array.from(excludedPaths, (candidate) => normalizeCandidatePath(candidate)));
214
+ const addInput = (input) => {
215
+ if (excluded.has(normalizeCandidatePath(input.path))) {
216
+ return;
217
+ }
218
+ const key = `${input.source}:${input.artifact_id ?? ""}:${normalizeCandidatePath(input.path)}`;
219
+ if (seen.has(key)) {
220
+ return;
221
+ }
222
+ seen.add(key);
223
+ inputs.push(input);
224
+ };
225
+ for (const artifactPath of artifactPaths) {
226
+ addInput(pipelineContext.runtime.artifactRegistry.resolveLineageInputFromPath(pipelineContext.issueKey, artifactPath));
227
+ }
228
+ const visit = (value) => {
229
+ if (Array.isArray(value)) {
230
+ value.forEach((item) => visit(item));
231
+ return;
232
+ }
233
+ if (!value || typeof value !== "object") {
234
+ return;
235
+ }
236
+ const record = value;
237
+ if (typeof record["artifact_id"] === "string" && typeof record["payload_path"] === "string") {
238
+ const payloadPath = String(record["payload_path"]);
239
+ if (!excluded.has(normalizeCandidatePath(payloadPath))) {
240
+ addInput({
241
+ source: "manifest",
242
+ path: payloadPath,
243
+ artifact_id: String(record["artifact_id"]),
244
+ ...(typeof record["logical_key"] === "string" ? { logical_key: String(record["logical_key"]) } : {}),
245
+ ...(typeof record["schema_id"] === "string" ? { schema_id: String(record["schema_id"]) } : {}),
246
+ ...(typeof record["schema_version"] === "number" ? { schema_version: Number(record["schema_version"]) } : {}),
247
+ });
248
+ }
249
+ }
250
+ for (const candidate of Object.values(record)) {
251
+ visit(candidate);
252
+ }
253
+ };
254
+ values.forEach((value) => visit(value));
255
+ return inputs.sort((left, right) => left.path.localeCompare(right.path));
256
+ }
257
+ function mergeLineageInputs(...groups) {
258
+ const seen = new Set();
259
+ const merged = [];
260
+ for (const group of groups) {
261
+ for (const input of group) {
262
+ const key = `${input.source}:${input.artifact_id ?? ""}:${normalizeCandidatePath(input.path)}`;
263
+ if (seen.has(key)) {
264
+ continue;
265
+ }
266
+ seen.add(key);
267
+ merged.push(input);
268
+ }
269
+ }
270
+ return merged.sort((left, right) => left.path.localeCompare(right.path));
271
+ }
141
272
  export async function runExpandedPhase(phase, pipelineContext, flowParams, flowConstants, options = {}) {
142
273
  const executionState = ensureExecutionState(options);
143
274
  const phaseState = ensurePhaseState(executionState, phase);
@@ -153,6 +284,9 @@ export async function runExpandedPhase(phase, pipelineContext, flowParams, flowC
153
284
  id: step.id,
154
285
  status: phaseState.steps[stepIndex]?.status === "skipped" ? "skipped" : "done",
155
286
  ...(phaseState.steps[stepIndex]?.outputs ? { outputs: phaseState.steps[stepIndex]?.outputs } : {}),
287
+ ...(phaseState.steps[stepIndex]?.publishedArtifacts
288
+ ? { publishedArtifacts: phaseState.steps[stepIndex]?.publishedArtifacts }
289
+ : {}),
156
290
  })),
157
291
  };
158
292
  }
@@ -206,7 +340,12 @@ export async function runExpandedPhase(phase, pipelineContext, flowParams, flowC
206
340
  throw new TaskRunnerError(`Missing execution state for step '${step.id}' in phase '${phase.id}'`);
207
341
  }
208
342
  if (stepState.status === "done" || stepState.status === "skipped") {
209
- steps.push({ id: step.id, status: stepState.status, ...(stepState.outputs ? { outputs: stepState.outputs } : {}) });
343
+ steps.push({
344
+ id: step.id,
345
+ status: stepState.status,
346
+ ...(stepState.outputs ? { outputs: stepState.outputs } : {}),
347
+ ...(stepState.publishedArtifacts ? { publishedArtifacts: stepState.publishedArtifacts } : {}),
348
+ });
210
349
  continue;
211
350
  }
212
351
  stepState.status = "running";
@@ -220,10 +359,23 @@ export async function runExpandedPhase(phase, pipelineContext, flowParams, flowC
220
359
  continue;
221
360
  }
222
361
  const params = resolveParams(step.params, stepContext);
362
+ if (step.routingGroup) {
363
+ params.routingGroup = step.routingGroup;
364
+ }
365
+ const promptInputs = step.prompt ? resolvePromptBindingInputs(step.prompt, stepContext) : {};
223
366
  if (step.prompt) {
224
367
  params.prompt = renderPrompt(step.prompt, stepContext);
225
368
  }
226
- const result = await runNodeByKind(step.node, pipelineContext, params, { skipChecks: step.expect !== undefined });
369
+ const result = await runNodeByKind(step.node, pipelineContext, params, {
370
+ skipChecks: step.expect !== undefined,
371
+ contextOverrides: {
372
+ ...(stepState.value !== undefined ? { resumeStepValue: stepState.value } : {}),
373
+ persistRunningStepValue: async (value) => {
374
+ stepState.value = value;
375
+ await options.onStateChange?.(executionState);
376
+ },
377
+ },
378
+ });
227
379
  stepState.value = toJsonValue(result.value);
228
380
  const stepOutputs = toStepOutputs(result.value);
229
381
  if (stepOutputs) {
@@ -249,15 +401,69 @@ export async function runExpandedPhase(phase, pipelineContext, flowParams, flowC
249
401
  runAfterAction(action, pipelineContext, stepContext);
250
402
  });
251
403
  }
404
+ const publishedArtifacts = [];
405
+ const nestedPublishedArtifacts = collectNestedPublishedArtifacts(result.value);
406
+ const publishableOutputs = (result.outputs ?? []).filter((output) => shouldPublishManifest(output));
407
+ if (!pipelineContext.dryRun && publishableOutputs.length > 0) {
408
+ const resolvedPublishableOutputs = [];
409
+ for (const output of publishableOutputs) {
410
+ if (!existsSync(output.path)) {
411
+ if (!output.required) {
412
+ continue;
413
+ }
414
+ throw new TaskRunnerError(`Cannot publish manifest for missing output ${output.path}.`);
415
+ }
416
+ resolvedPublishableOutputs.push(output);
417
+ }
418
+ validateDistinctLogicalKeys(pipelineContext.issueKey, phase.id, step.id, resolvedPublishableOutputs);
419
+ const publishedOutputPaths = resolvedPublishableOutputs.map((output) => output.path);
420
+ const lineageInputs = collectLineageInputs([result.value], [
421
+ ...Object.values(step.params ?? {}).flatMap((value) => collectResolvedArtifactPathCandidates(value, stepContext)),
422
+ ...collectResolvedPromptArtifactPathCandidates(step.prompt, stepContext),
423
+ ], pipelineContext, publishedOutputPaths);
424
+ for (const output of resolvedPublishableOutputs) {
425
+ publishedArtifacts.push(pipelineContext.runtime.artifactRegistry.publish({
426
+ scopeKey: pipelineContext.issueKey,
427
+ runId: executionState.runId ?? randomUUID(),
428
+ publicationRunId: executionState.publicationRunId ?? randomUUID(),
429
+ flowId: executionState.flowKind,
430
+ phaseId: phase.id,
431
+ stepId: step.id,
432
+ nodeKind: step.node,
433
+ nodeVersion: 1,
434
+ kind: output.kind,
435
+ payloadPath: output.path,
436
+ ...(output.manifest?.logicalKey ? { logicalKey: output.manifest.logicalKey } : {}),
437
+ ...(output.manifest?.schemaId ? { schemaId: output.manifest.schemaId } : {}),
438
+ ...(output.manifest?.schemaVersion ? { schemaVersion: output.manifest.schemaVersion } : {}),
439
+ ...(output.manifest?.payloadFamily ? { payloadFamily: output.manifest.payloadFamily } : {}),
440
+ inputs: mergeLineageInputs(lineageInputs, output.manifest?.inputRefs ?? []),
441
+ ...producerMetadata(result.value),
442
+ }));
443
+ }
444
+ }
445
+ const allPublishedArtifacts = mergePublishedArtifacts(publishedArtifacts, nestedPublishedArtifacts);
252
446
  const stopFlow = step.stopFlowIf ? evaluateCondition(step.stopFlowIf, stepContext) : false;
253
447
  stepState.status = "done";
254
448
  stepState.finishedAt = nowIso8601();
255
449
  stepState.stopFlow = stopFlow;
450
+ if (allPublishedArtifacts.length > 0) {
451
+ stepState.publishedArtifacts = allPublishedArtifacts;
452
+ }
453
+ else {
454
+ delete stepState.publishedArtifacts;
455
+ }
256
456
  await options.onStateChange?.(executionState);
257
- steps.push({ id: step.id, status: "done", ...(stepState.outputs ? { outputs: stepState.outputs } : {}) });
457
+ steps.push({
458
+ id: step.id,
459
+ status: "done",
460
+ ...(stepState.outputs ? { outputs: stepState.outputs } : {}),
461
+ ...(stepState.publishedArtifacts ? { publishedArtifacts: stepState.publishedArtifacts } : {}),
462
+ });
258
463
  if (stopFlow) {
259
464
  executionState.terminated = true;
260
465
  executionState.terminationReason = `Stopped by ${phase.id}:${step.id}`;
466
+ executionState.terminationOutcome = step.stopFlowOutcome ?? "success";
261
467
  phaseState.status = "done";
262
468
  phaseState.finishedAt = nowIso8601();
263
469
  await options.onStateChange?.(executionState);
@@ -29,6 +29,7 @@ export function loadDeclarativeFlow(flow) {
29
29
  kind: spec.kind,
30
30
  version: spec.version,
31
31
  ...(spec.description !== undefined ? { description: spec.description } : {}),
32
+ ...(spec.catalogVisibility !== undefined ? { catalogVisibility: spec.catalogVisibility } : {}),
32
33
  constants: spec.constants ?? {},
33
34
  phases,
34
35
  source: ref.source,
@@ -61,3 +62,29 @@ export function resolveNamedDeclarativeFlowRef(fileName, cwd) {
61
62
  export function loadNamedDeclarativeFlow(fileName, cwd) {
62
63
  return loadDeclarativeFlow(resolveNamedDeclarativeFlowRef(fileName, cwd));
63
64
  }
65
+ export function collectFlowRoutingGroups(flow, cwd, visited = new Set()) {
66
+ if (visited.has(flow.absolutePath)) {
67
+ return [];
68
+ }
69
+ visited.add(flow.absolutePath);
70
+ const groups = new Set();
71
+ for (const phase of flow.phases) {
72
+ for (const step of phase.steps) {
73
+ if (step.routingGroup) {
74
+ groups.add(step.routingGroup);
75
+ }
76
+ if (step.node !== "flow-run") {
77
+ continue;
78
+ }
79
+ const nestedFlowName = step.params?.fileName;
80
+ if (!nestedFlowName || !("const" in nestedFlowName) || typeof nestedFlowName.const !== "string") {
81
+ continue;
82
+ }
83
+ const nestedFlow = loadNamedDeclarativeFlow(nestedFlowName.const, cwd);
84
+ for (const nestedGroup of collectFlowRoutingGroups(nestedFlow, cwd, visited)) {
85
+ groups.add(nestedGroup);
86
+ }
87
+ }
88
+ }
89
+ return [...groups];
90
+ }
@@ -0,0 +1,15 @@
1
+ export const EXECUTION_ROUTING_GROUPS = [
2
+ "planning",
3
+ "design-review",
4
+ "implementation",
5
+ "review",
6
+ "repair-loop",
7
+ "local-fix-loop",
8
+ ];
9
+ export const BUILT_IN_EXECUTION_PRESET_IDS = [
10
+ "balanced",
11
+ "quality-first",
12
+ "cheap-first",
13
+ "codex-only",
14
+ "opencode-only",
15
+ ];
@@ -1,18 +1,22 @@
1
1
  import path from "node:path";
2
2
  import { TaskRunnerError } from "../errors.js";
3
3
  import { loadAutoGolangFlow } from "./auto-flow.js";
4
- import { loadDeclarativeFlow } from "./declarative-flows.js";
4
+ import { collectFlowRoutingGroups, loadDeclarativeFlow } from "./declarative-flows.js";
5
5
  import { listBuiltInFlowSpecFiles, listProjectFlowSpecFiles, projectFlowSpecsDir } from "./spec-loader.js";
6
6
  export const BUILT_IN_COMMAND_FLOW_IDS = [
7
7
  "auto-golang",
8
8
  "auto-common",
9
+ "auto-simple",
9
10
  "bug-analyze",
10
11
  "bug-fix",
12
+ "design-review",
11
13
  "git-commit",
12
14
  "gitlab-diff-review",
13
15
  "gitlab-review",
16
+ "instant-task",
14
17
  "mr-description",
15
18
  "plan",
19
+ "plan-revise",
16
20
  "task-describe",
17
21
  "implement",
18
22
  "review",
@@ -24,13 +28,17 @@ export const BUILT_IN_COMMAND_FLOW_IDS = [
24
28
  const BUILT_IN_COMMAND_FLOW_FILES = {
25
29
  "auto-golang": "auto-golang.json",
26
30
  "auto-common": "auto-common.json",
31
+ "auto-simple": "auto-simple.json",
27
32
  "bug-analyze": "bugz/bug-analyze.json",
28
33
  "bug-fix": "bugz/bug-fix.json",
34
+ "design-review": "design-review.json",
29
35
  "git-commit": "git-commit.json",
30
36
  "gitlab-diff-review": "gitlab/gitlab-diff-review.json",
31
37
  "gitlab-review": "gitlab/gitlab-review.json",
38
+ "instant-task": "instant-task.json",
32
39
  "mr-description": "gitlab/mr-description.json",
33
40
  plan: "plan.json",
41
+ "plan-revise": "plan-revise.json",
34
42
  "task-describe": "task-describe.json",
35
43
  implement: "implement.json",
36
44
  review: "review/review.json",
@@ -39,6 +47,9 @@ const BUILT_IN_COMMAND_FLOW_FILES = {
39
47
  "run-go-tests-loop": "go/run-go-tests-loop.json",
40
48
  "run-go-linter-loop": "go/run-go-linter-loop.json",
41
49
  };
50
+ export function builtInCommandFlowFile(flowId) {
51
+ return BUILT_IN_COMMAND_FLOW_FILES[flowId] ?? null;
52
+ }
42
53
  function builtInCommandIdForFile(fileName) {
43
54
  for (const [flowId, candidate] of Object.entries(BUILT_IN_COMMAND_FLOW_FILES)) {
44
55
  if (candidate === fileName) {
@@ -80,15 +91,16 @@ export function loadInteractiveFlowCatalog(cwd) {
80
91
  for (const filePath of listProjectFlowSpecFiles(cwd)) {
81
92
  entries.push(loadProjectCatalogEntry(cwd, filePath));
82
93
  }
94
+ const visibleEntries = entries.filter((entry) => entry.flow.catalogVisibility !== "hidden");
83
95
  const byId = new Map();
84
- for (const entry of entries) {
96
+ for (const entry of visibleEntries) {
85
97
  const duplicate = byId.get(entry.id);
86
98
  if (duplicate) {
87
99
  throw new TaskRunnerError(`Flow id '${entry.id}' conflicts between ${duplicate.absolutePath} and ${entry.absolutePath}. Rename one of the flow files.`);
88
100
  }
89
101
  byId.set(entry.id, entry);
90
102
  }
91
- return entries;
103
+ return visibleEntries;
92
104
  }
93
105
  export function findCatalogEntry(flowId, entries) {
94
106
  return entries.find((entry) => entry.id === flowId);
@@ -101,3 +113,11 @@ export function toDeclarativeFlowRef(entry) {
101
113
  ? { source: "built-in", fileName: entry.fileName }
102
114
  : { source: "project-local", filePath: entry.absolutePath };
103
115
  }
116
+ export function flowRoutingKey(entry) {
117
+ return entry.source === "project-local"
118
+ ? `project-local:${entry.absolutePath}`
119
+ : `built-in:${entry.id}`;
120
+ }
121
+ export function flowRoutingGroups(entry, cwd) {
122
+ return collectFlowRoutingGroups(entry.flow, cwd);
123
+ }
@@ -0,0 +1,29 @@
1
+ function isFlowExecutionState(value) {
2
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
3
+ return false;
4
+ }
5
+ const record = value;
6
+ return (typeof record["flowKind"] === "string"
7
+ && typeof record["flowVersion"] === "number"
8
+ && typeof record["terminated"] === "boolean"
9
+ && Array.isArray(record["phases"]));
10
+ }
11
+ function isPublishedArtifactRecord(value) {
12
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
13
+ return false;
14
+ }
15
+ const record = value;
16
+ return typeof record["artifact_id"] === "string" && typeof record["payload_path"] === "string";
17
+ }
18
+ export function isFlowRunResumeEnvelope(value) {
19
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
20
+ return false;
21
+ }
22
+ const record = value;
23
+ return (record["resumeKind"] === "flow-run"
24
+ && typeof record["flowKind"] === "string"
25
+ && typeof record["flowVersion"] === "number"
26
+ && isFlowExecutionState(record["executionState"])
27
+ && Array.isArray(record["publishedArtifacts"])
28
+ && record["publishedArtifacts"].every((artifact) => isPublishedArtifactRecord(artifact)));
29
+ }