@oisincoveney/pipeline 1.27.10 → 1.27.12

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/dist/config.d.ts CHANGED
@@ -485,8 +485,8 @@ declare const configSchema: z.ZodObject<{
485
485
  rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
486
486
  path: z.ZodString;
487
487
  source_root: z.ZodDefault<z.ZodEnum<{
488
- project: "project";
489
488
  package: "package";
489
+ project: "project";
490
490
  }>>;
491
491
  }, z.core.$strict>>>;
492
492
  runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -613,8 +613,8 @@ declare const configSchema: z.ZodObject<{
613
613
  schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
614
614
  description: z.ZodOptional<z.ZodString>;
615
615
  baseline: z.ZodEnum<{
616
- quick: "quick";
617
616
  execute: "execute";
617
+ quick: "quick";
618
618
  }>;
619
619
  max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
620
620
  node_catalog: z.ZodOptional<z.ZodString>;
@@ -626,8 +626,8 @@ declare const configSchema: z.ZodObject<{
626
626
  skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
627
627
  path: z.ZodString;
628
628
  source_root: z.ZodDefault<z.ZodEnum<{
629
- project: "project";
630
629
  package: "package";
630
+ project: "project";
631
631
  }>>;
632
632
  }, z.core.$strict>>>;
633
633
  task_context: z.ZodOptional<z.ZodObject<{
@@ -160,8 +160,8 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
160
160
  }, z.core.$strict>>;
161
161
  serviceAccountName: z.ZodDefault<z.ZodString>;
162
162
  mode: z.ZodEnum<{
163
- full: "full";
164
163
  quick: "quick";
164
+ full: "full";
165
165
  }>;
166
166
  schedulePath: z.ZodOptional<z.ZodString>;
167
167
  scheduleYaml: z.ZodOptional<z.ZodString>;
@@ -43,8 +43,8 @@ declare const runnerDeliverySchema: z.ZodObject<{
43
43
  declare const mokaSubmissionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
44
44
  kind: z.ZodLiteral<"graph">;
45
45
  mode: z.ZodEnum<{
46
- full: "full";
47
46
  quick: "quick";
47
+ full: "full";
48
48
  }>;
49
49
  }, z.core.$strict>, z.ZodObject<{
50
50
  argv: z.ZodArray<z.ZodString>;
@@ -104,8 +104,8 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
104
104
  submission: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
105
105
  kind: z.ZodLiteral<"graph">;
106
106
  mode: z.ZodEnum<{
107
- full: "full";
108
107
  quick: "quick";
108
+ full: "full";
109
109
  }>;
110
110
  }, z.core.$strict>, z.ZodObject<{
111
111
  argv: z.ZodArray<z.ZodString>;
@@ -4,7 +4,7 @@ import { normalizeRunnerOutput, runnerTextCandidates } from "../../runner-output
4
4
  import { gatewayServerForProfile } from "../../mcp/gateway.js";
5
5
  import { selectNodeModel } from "../../model-resolver.js";
6
6
  import { resolvePackageAssetPath } from "../../package-assets.js";
7
- import { readJsonSchemaSource, validateJsonSchemaSource } from "../json-validation/json-validation.js";
7
+ import { normalizeJsonSource, readJsonSchemaSource, validateJsonSchemaSource } from "../json-validation/json-validation.js";
8
8
  import "../json-validation/index.js";
9
9
  import { emit, emitAgentFinish, emitAgentStart } from "../events/events.js";
10
10
  import "../events/index.js";
@@ -54,22 +54,33 @@ async function executeAgentNode(node, context, attempt) {
54
54
  }
55
55
  async function finalizeAgentOutput(inputs) {
56
56
  const { attempt, context, node, normalized, plan, result } = inputs;
57
- const validStructuredOutput = selectValidStructuredOutput(context, node, plan, result.stdout);
57
+ const validStructuredOutput = selectValidStructuredOutput(context, node, normalized, plan, result.stdout);
58
58
  if (validStructuredOutput) return validStructuredOutput;
59
59
  const repairContext = outputRepairContext(context, node, normalized, result);
60
60
  if (!repairContext) return normalized;
61
61
  return await runOutputRepair(context, node, normalized, repairContext, attempt);
62
62
  }
63
- function selectValidStructuredOutput(context, node, plan, stdout) {
63
+ function selectValidStructuredOutput(context, node, normalized, plan, stdout) {
64
64
  const output = (node.profile ? context.config.profiles[node.profile] : void 0)?.output;
65
65
  if (output?.format !== "json_schema" || !output.schema_path) return null;
66
- const candidates = runnerTextCandidates(plan, stdout);
67
- for (const candidate of [...candidates].reverse()) if (validateJsonSchemaSource(candidate.output, output.schema_path, context.worktreePath).passed) return {
68
- evidence: [candidate.evidence, `selected valid structured output for ${node.id}`],
69
- output: candidate.output
70
- };
66
+ const candidates = structuredOutputCandidates(plan, stdout, normalized);
67
+ for (const candidate of candidates) {
68
+ const candidateOutput = normalizeJsonSource(candidate.output);
69
+ if (validateJsonSchemaSource(candidateOutput, output.schema_path, context.worktreePath).passed) return {
70
+ evidence: [candidate.evidence, `selected valid structured output for ${node.id}`],
71
+ output: candidateOutput
72
+ };
73
+ }
71
74
  return null;
72
75
  }
76
+ function structuredOutputCandidates(plan, stdout, normalized) {
77
+ const candidates = runnerTextCandidates(plan, stdout);
78
+ if (candidates.length > 0) return [...candidates].reverse();
79
+ return [{
80
+ evidence: normalized.evidence.join("; ") || "selected runner stdout",
81
+ output: normalized.output
82
+ }];
83
+ }
73
84
  function outputRepairContext(context, node, normalized, result) {
74
85
  if (result.exitCode !== 0 || result.timedOut) return null;
75
86
  const profile = node.profile ? context.config.profiles[node.profile] : void 0;
@@ -110,14 +121,15 @@ async function runOutputRepair(context, node, normalized, repairContext, nodeAtt
110
121
  const repairResult = await context.executor(repairPlan, { signal: context.signal });
111
122
  emitAgentFinish(context, repairPlan, nodeAttempt, repairResult);
112
123
  const repaired = normalizeAgentOutput(repairPlan, repairResult.stdout);
113
- const repairedValidation = validateJsonSchemaSource(repaired.output, repairContext.schemaPath, context.worktreePath);
124
+ const repairedOutput = normalizeJsonSource(repaired.output);
125
+ const repairedValidation = validateJsonSchemaSource(repairedOutput, repairContext.schemaPath, context.worktreePath);
114
126
  latest = {
115
127
  evidence: [
116
128
  ...repaired.evidence,
117
129
  ...repairResult.stderr ? [`repair stderr: ${repairResult.stderr}`] : [],
118
130
  ...repairResult.timedOut ? ["output repair timed out"] : []
119
131
  ],
120
- output: repaired.output
132
+ output: repairedOutput
121
133
  };
122
134
  latestValidation = repairedValidation;
123
135
  const passed = repairResult.exitCode === 0 && repairedValidation.passed;
@@ -131,7 +143,7 @@ async function runOutputRepair(context, node, normalized, repairContext, nodeAtt
131
143
  });
132
144
  if (passed) return {
133
145
  evidence,
134
- output: repaired.output
146
+ output: repairedOutput
135
147
  };
136
148
  }
137
149
  return {
@@ -6,6 +6,7 @@ import Ajv from "ajv";
6
6
  import addFormats from "ajv-formats";
7
7
  //#region src/runtime/json-validation/json-validation.ts
8
8
  const LINE_RE = /\r?\n/;
9
+ const MARKDOWN_JSON_FENCE_RE = /^\s*```(?:json)?\s*\r?\n([\s\S]*?)\r?\n```\s*$/i;
9
10
  const jsonSchemaValidator = addFormats(new Ajv({
10
11
  allErrors: true,
11
12
  strict: false
@@ -15,7 +16,7 @@ function parseRuntimeOutput(format, output) {
15
16
  if (!(format === "json" || format === "json_schema" || format === "jsonl")) return { output };
16
17
  try {
17
18
  if (format === "jsonl") return { output: output.split(LINE_RE).filter((line) => line.trim().length > 0).map((line) => parseJson(line, "runtime JSONL line")) };
18
- return { output: parseJson(output, "runtime JSON output") };
19
+ return { output: parseJson(normalizeJsonSource(output), "runtime JSON output") };
19
20
  } catch (err) {
20
21
  return {
21
22
  error: err instanceof Error ? err.message : "failed to parse output",
@@ -26,7 +27,7 @@ function parseRuntimeOutput(format, output) {
26
27
  function validateJsonSchemaSource(source, schemaPath, worktreePath) {
27
28
  try {
28
29
  const schemaSource = readJsonSchemaSource(schemaPath, worktreePath);
29
- const value = parseJson(source, "JSON schema gate value");
30
+ const value = parseJson(normalizeJsonSource(source), "JSON schema gate value");
30
31
  const validate = compiledJsonSchemaValidator(schemaPath, schemaSource);
31
32
  const errors = validate(value) ? [] : formatJsonSchemaErrors(validate.errors ?? []);
32
33
  return {
@@ -42,6 +43,10 @@ function validateJsonSchemaSource(source, schemaPath, worktreePath) {
42
43
  };
43
44
  }
44
45
  }
46
+ function normalizeJsonSource(source) {
47
+ const trimmed = source.trim();
48
+ return MARKDOWN_JSON_FENCE_RE.exec(trimmed)?.[1].trim() ?? trimmed;
49
+ }
45
50
  function readJsonSchemaSource(schemaPath, worktreePath) {
46
51
  const standardName = standardOutputSchemaNameFromPath(schemaPath);
47
52
  if (standardName) return standardOutputSchemaJson(standardName);
@@ -82,4 +87,4 @@ function parseJsonObject(value) {
82
87
  }
83
88
  }
84
89
  //#endregion
85
- export { parseJsonObject, parseRuntimeOutput, readJsonSchemaSource, readOptionalFile, validateJsonSchemaSource };
90
+ export { normalizeJsonSource, parseJsonObject, parseRuntimeOutput, readJsonSchemaSource, readOptionalFile, validateJsonSchemaSource };
@@ -485,35 +485,71 @@ function executeBaselineWorkflow() {
485
485
  } };
486
486
  }
487
487
  async function planScheduleArtifact(baseline, plannerProfile, options, planningContext) {
488
- if (!plannerProfile) throw new ScheduleArtifactError(`schedule '${options.entrypointId}' requires planner_profile`);
489
- const source = await runSchedulePlanner(plannerProfile, plannerPrompt(options.entrypointId, options.task, baseline, options.config, planningContext), options);
490
- if (!source) throw new ScheduleArtifactError("schedule planner returned empty output");
488
+ const requiredPlannerProfile = requireSchedulePlannerProfile(plannerProfile, options.entrypointId);
489
+ const source = requireSchedulePlannerSource(await runSchedulePlanner(requiredPlannerProfile, plannerPrompt(options.entrypointId, options.task, baseline, options.config, planningContext), options));
491
490
  const initial = acceptedGeneratedSchedule(parseGeneratedSchedule(source, "planner output"));
492
491
  if (initial.ok) return initial.artifact;
493
- let latestFailure = initial.error;
494
- let latestSource = source;
492
+ return scheduleArtifactAfterRepair(await repairInvalidScheduleArtifact({
493
+ baseline,
494
+ initialFailure: initial.error,
495
+ initialSource: source,
496
+ options,
497
+ plannerProfile: requiredPlannerProfile
498
+ }), baseline, initial.error, source);
499
+ }
500
+ function requireSchedulePlannerProfile(plannerProfile, entrypointId) {
501
+ if (plannerProfile) return plannerProfile;
502
+ throw new ScheduleArtifactError(`schedule '${entrypointId}' requires planner_profile`);
503
+ }
504
+ function requireSchedulePlannerSource(source) {
505
+ if (source) return source;
506
+ throw new ScheduleArtifactError("schedule planner returned empty output");
507
+ }
508
+ function scheduleArtifactAfterRepair(repair, baseline, initialFailure, initialSource) {
509
+ if (repair.kind === "accepted") return repair.artifact;
510
+ if (repair.kind === "fallback") return baseline;
511
+ throw new ScheduleArtifactError([
512
+ "Schedule planner produced invalid output after repair.",
513
+ initialFailure.message,
514
+ "Original planner output:",
515
+ initialSource,
516
+ repair.latestFailure.message,
517
+ "Planner repair output:",
518
+ repair.latestSource
519
+ ].join("\n"));
520
+ }
521
+ async function repairInvalidScheduleArtifact(input) {
522
+ let latestFailure = input.initialFailure;
523
+ let latestSource = input.initialSource;
495
524
  for (let attempt = 1; attempt <= SCHEDULE_PLANNER_REPAIR_ATTEMPTS; attempt += 1) {
496
- const repairedSource = await runSchedulePlanner(plannerProfile, plannerRepairPrompt({
497
- attempt,
498
- baseline,
499
- error: latestFailure,
500
- source: latestSource
501
- }), options, "schedule-plan-repair");
502
- if (!repairedSource) throw new ScheduleArtifactError(`schedule planner repair returned empty output after invalid schedule\n${latestFailure.message}\nPlanner output:\n${latestSource}`);
525
+ const repairedSource = await runScheduleRepair(input, latestFailure, latestSource, attempt);
526
+ if (!repairedSource) return { kind: "fallback" };
503
527
  const repaired = acceptedGeneratedSchedule(parseGeneratedSchedule(repairedSource, "planner repair output"));
504
- if (repaired.ok) return repaired.artifact;
528
+ if (repaired.ok) return {
529
+ artifact: repaired.artifact,
530
+ kind: "accepted"
531
+ };
505
532
  latestFailure = repaired.error;
506
533
  latestSource = repairedSource;
507
534
  }
508
- throw new ScheduleArtifactError([
509
- "Schedule planner produced invalid output after repair.",
510
- initial.error.message,
511
- "Original planner output:",
512
- source,
513
- latestFailure.message,
514
- "Planner repair output:",
535
+ return {
536
+ kind: "invalid",
537
+ latestFailure,
515
538
  latestSource
516
- ].join("\n"));
539
+ };
540
+ }
541
+ async function runScheduleRepair(input, latestFailure, latestSource, attempt) {
542
+ try {
543
+ return await runSchedulePlanner(input.plannerProfile, plannerRepairPrompt({
544
+ attempt,
545
+ baseline: input.baseline,
546
+ error: latestFailure,
547
+ source: latestSource
548
+ }), input.options, "schedule-plan-repair");
549
+ } catch (err) {
550
+ if (err instanceof ScheduleArtifactError) return;
551
+ throw err;
552
+ }
517
553
  }
518
554
  function parseGeneratedSchedule(source, sourcePath) {
519
555
  const parseableSource = normalizeGeneratedScheduleSource(source);
@@ -644,18 +680,43 @@ function plannerRepairPrompt(inputs) {
644
680
  function allowedProfilePromptLine(config, id) {
645
681
  const profile = config.profiles[id];
646
682
  const runner = config.runners[profile.runner];
647
- const roles = effectiveSchedulingRoles(config, id);
648
- const model = profile.model ?? runner?.model;
649
- return `- ${id} (${[
650
- `runner: ${profile.runner}`,
651
- model ? `model: ${model}` : "",
652
- roles.length > 0 ? `scheduling_roles: ${roles.join(", ")}` : "",
653
- profile.description ? `description: ${profile.description}` : "",
654
- profile.tools?.length ? `tools: ${profile.tools.join(", ")}` : "",
655
- profile.filesystem?.mode ? `filesystem: ${profile.filesystem.mode}` : "",
656
- profile.network?.mode ? `network: ${profile.network.mode}` : "",
657
- `output: ${profile.output?.format ?? "text"}`
658
- ].filter(Boolean).join("; ")})`;
683
+ return `- ${id} (${profilePromptFields(config, id, profile, runner).join("; ")})`;
684
+ }
685
+ function profilePromptFields(config, id, profile, runner) {
686
+ return definedProfilePromptFields([
687
+ requiredProfilePromptField("runner", profile.runner),
688
+ optionalProfilePromptField("model", profileModel(profile, runner)),
689
+ optionalProfilePromptField("scheduling_roles", effectiveSchedulingRoles(config, id).join(", ")),
690
+ optionalProfilePromptField("description", profile.description),
691
+ optionalProfilePromptField("tools", profileTools(profile)),
692
+ optionalProfilePromptField("filesystem", profileFilesystemMode(profile)),
693
+ optionalProfilePromptField("network", profileNetworkMode(profile)),
694
+ requiredProfilePromptField("output", profileOutputFormat(profile))
695
+ ]);
696
+ }
697
+ function profileModel(profile, runner) {
698
+ return profile.model ?? runner?.model;
699
+ }
700
+ function profileTools(profile) {
701
+ return profile.tools?.join(", ");
702
+ }
703
+ function profileFilesystemMode(profile) {
704
+ return profile.filesystem?.mode;
705
+ }
706
+ function profileNetworkMode(profile) {
707
+ return profile.network?.mode;
708
+ }
709
+ function profileOutputFormat(profile) {
710
+ return profile.output?.format ?? "text";
711
+ }
712
+ function requiredProfilePromptField(label, value) {
713
+ return `${label}: ${value}`;
714
+ }
715
+ function optionalProfilePromptField(label, value) {
716
+ return value ? requiredProfilePromptField(label, value) : void 0;
717
+ }
718
+ function definedProfilePromptFields(fields) {
719
+ return fields.filter((field) => Boolean(field));
659
720
  }
660
721
  function schedulerCatalogPrompt(config, entrypointId) {
661
722
  const catalog = resolveSchedulerCatalog(config, entrypointId);
@@ -837,16 +898,7 @@ function nodesByAssignedWorkUnit(nodes) {
837
898
  return grouped;
838
899
  }
839
900
  function hasPathToNode(sourceId, targetId, dependentsByNeed) {
840
- const queue = [...dependentsByNeed.get(sourceId) ?? []];
841
- const seen = /* @__PURE__ */ new Set();
842
- while (queue.length > 0) {
843
- const node = queue.shift();
844
- if (!node || seen.has(node.id)) continue;
845
- if (node.id === targetId) return true;
846
- seen.add(node.id);
847
- queue.push(...dependentsByNeed.get(node.id) ?? []);
848
- }
849
- return false;
901
+ return hasReachableDependent(sourceId, dependentsByNeed, (node) => node.id === targetId);
850
902
  }
851
903
  function unsupportedGeneratedBuiltinIssues(artifact) {
852
904
  const allowed = new Set(SCHEDULE_BUILTINS);
package/package.json CHANGED
@@ -120,7 +120,7 @@
120
120
  "prepack": "bun run build:cli"
121
121
  },
122
122
  "type": "module",
123
- "version": "1.27.10",
123
+ "version": "1.27.12",
124
124
  "description": "Config-driven multi-agent pipeline runner for repository work",
125
125
  "main": "./dist/index.js",
126
126
  "types": "./dist/index.d.ts",