@oisincoveney/pipeline 2.5.0 → 2.7.0

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.
@@ -221,8 +221,8 @@ declare const configSchema: z.ZodObject<{
221
221
  policy: z.ZodOptional<z.ZodObject<{
222
222
  commands: z.ZodOptional<z.ZodEnum<{
223
223
  allow: "allow";
224
- "trusted-only": "trusted-only";
225
224
  deny: "deny";
225
+ "trusted-only": "trusted-only";
226
226
  }>>;
227
227
  modules: z.ZodOptional<z.ZodEnum<{
228
228
  allow: "allow";
@@ -246,8 +246,8 @@ declare const configSchema: z.ZodObject<{
246
246
  }, z.core.$strict>>>;
247
247
  default_profile: z.ZodOptional<z.ZodString>;
248
248
  mode: z.ZodEnum<{
249
- hosted: "hosted";
250
249
  local: "local";
250
+ hosted: "hosted";
251
251
  }>;
252
252
  provider: z.ZodLiteral<"toolhive">;
253
253
  authorization_env: z.ZodDefault<z.ZodString>;
@@ -290,10 +290,10 @@ declare const configSchema: z.ZodObject<{
290
290
  }, z.core.$strict>>;
291
291
  output: z.ZodOptional<z.ZodObject<{
292
292
  format: z.ZodEnum<{
293
+ json_schema: "json_schema";
293
294
  text: "text";
294
295
  json: "json";
295
296
  jsonl: "jsonl";
296
- json_schema: "json_schema";
297
297
  }>;
298
298
  repair: z.ZodOptional<z.ZodObject<{
299
299
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -311,6 +311,7 @@ declare const configSchema: z.ZodObject<{
311
311
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
312
312
  timeout_ms: z.ZodOptional<z.ZodNumber>;
313
313
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
314
+ task: "task";
314
315
  read: "read";
315
316
  list: "list";
316
317
  grep: "grep";
@@ -318,7 +319,6 @@ declare const configSchema: z.ZodObject<{
318
319
  bash: "bash";
319
320
  edit: "edit";
320
321
  write: "write";
321
- task: "task";
322
322
  }>>>;
323
323
  }, z.core.$strict>>>;
324
324
  runner_command: z.ZodDefault<z.ZodObject<{
@@ -344,8 +344,8 @@ declare const configSchema: z.ZodObject<{
344
344
  rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
345
345
  path: z.ZodString;
346
346
  source_root: z.ZodDefault<z.ZodEnum<{
347
- package: "package";
348
347
  project: "project";
348
+ package: "package";
349
349
  }>>;
350
350
  }, z.core.$strict>>>;
351
351
  runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -362,14 +362,15 @@ declare const configSchema: z.ZodObject<{
362
362
  disabled: "disabled";
363
363
  }>>>;
364
364
  output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
365
+ json_schema: "json_schema";
365
366
  text: "text";
366
367
  json: "json";
367
368
  jsonl: "jsonl";
368
- json_schema: "json_schema";
369
369
  }>>>;
370
370
  rules: z.ZodOptional<z.ZodBoolean>;
371
371
  skills: z.ZodOptional<z.ZodBoolean>;
372
372
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
373
+ task: "task";
373
374
  read: "read";
374
375
  list: "list";
375
376
  grep: "grep";
@@ -377,7 +378,6 @@ declare const configSchema: z.ZodObject<{
377
378
  bash: "bash";
378
379
  edit: "edit";
379
380
  write: "write";
380
- task: "task";
381
381
  }>>>;
382
382
  }, z.core.$strict>;
383
383
  command: z.ZodOptional<z.ZodString>;
@@ -472,8 +472,8 @@ declare const configSchema: z.ZodObject<{
472
472
  schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
473
473
  description: z.ZodOptional<z.ZodString>;
474
474
  baseline: z.ZodEnum<{
475
- execute: "execute";
476
475
  quick: "quick";
476
+ execute: "execute";
477
477
  }>;
478
478
  max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
479
479
  node_catalog: z.ZodOptional<z.ZodString>;
@@ -485,8 +485,8 @@ declare const configSchema: z.ZodObject<{
485
485
  skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
486
486
  path: z.ZodString;
487
487
  source_root: z.ZodDefault<z.ZodEnum<{
488
- package: "package";
489
488
  project: "project";
489
+ package: "package";
490
490
  }>>;
491
491
  }, z.core.$strict>>>;
492
492
  task_context: z.ZodOptional<z.ZodObject<{
@@ -495,6 +495,7 @@ declare const configSchema: z.ZodObject<{
495
495
  best_of_n: z.ZodOptional<z.ZodObject<{
496
496
  categories: z.ZodDefault<z.ZodArray<z.ZodString>>;
497
497
  enabled: z.ZodDefault<z.ZodBoolean>;
498
+ judge_model: z.ZodOptional<z.ZodString>;
498
499
  n: z.ZodDefault<z.ZodNumber>;
499
500
  }, z.core.$strict>>;
500
501
  context_handoff: z.ZodOptional<z.ZodObject<{
@@ -469,6 +469,7 @@ const parallelWorktreesSchema = z.object({ enabled: z.boolean().default(false) }
469
469
  const bestOfNSchema = z.object({
470
470
  categories: z.array(z.string()).default(["green"]),
471
471
  enabled: z.boolean().default(false),
472
+ judge_model: z.string().optional(),
472
473
  n: z.number().int().positive().default(1)
473
474
  }).strict();
474
475
  const pipelineFileSchema = z.object({
@@ -160,8 +160,8 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
160
160
  }, z.core.$strict>>;
161
161
  serviceAccountName: z.ZodOptional<z.ZodString>;
162
162
  mode: z.ZodEnum<{
163
- quick: "quick";
164
163
  full: "full";
164
+ quick: "quick";
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
- quick: "quick";
47
46
  full: "full";
47
+ quick: "quick";
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
- quick: "quick";
108
107
  full: "full";
108
+ quick: "quick";
109
109
  }>;
110
110
  }, z.core.$strict>, z.ZodObject<{
111
111
  argv: z.ZodArray<z.ZodString>;
@@ -91,20 +91,40 @@ function createLinkedAbortController(signal) {
91
91
  controller
92
92
  };
93
93
  }
94
+ function childCategory(childId, fanOut) {
95
+ return fanOut ? Object.keys(fanOut.by_category).find((category) => childId.includes(category)) : void 0;
96
+ }
97
+ function makeCategoryGate(context) {
98
+ const fanOut = context.config.token_budget?.fan_out_width;
99
+ const limits = /* @__PURE__ */ new Map();
100
+ return (childId, run) => {
101
+ const category = childCategory(childId, fanOut);
102
+ if (!(category && fanOut)) return run();
103
+ let limit = limits.get(category);
104
+ if (!limit) {
105
+ limit = pLimit(fanOut.by_category[category]);
106
+ limits.set(category, limit);
107
+ }
108
+ return limit(run);
109
+ };
110
+ }
94
111
  function executeParallelChildren(children, context, runtime) {
95
112
  for (const child of children) runtime.markNodeReady(context, child.id);
96
- if (!context.maxParallelNodes) return Promise.all(children.map((child) => runChildInWorktree(child, context, runtime)));
113
+ const gate = makeCategoryGate(context);
114
+ const runChild = (child) => gate(child.id, () => runChildInWorktree(child, context, runtime));
115
+ if (!context.maxParallelNodes) return Promise.all(children.map((child) => runChild(child)));
97
116
  const limit = pLimit(context.maxParallelNodes);
98
- return Promise.all(children.map((child) => limit(() => runChildInWorktree(child, context, runtime))));
117
+ return Promise.all(children.map((child) => limit(() => runChild(child))));
99
118
  }
100
119
  async function executeFailFastParallelChildren(children, context, abortController, runtime) {
101
120
  for (const child of children) runtime.markNodeReady(context, child.id);
121
+ const gate = makeCategoryGate(context);
102
122
  const limit = pLimit({
103
123
  concurrency: context.maxParallelNodes ?? children.length,
104
124
  rejectOnClear: true
105
125
  });
106
126
  return (await Promise.allSettled(children.map((child) => limit(async () => {
107
- const result = await runChildInWorktree(child, context, runtime);
127
+ const result = await gate(child.id, () => runChildInWorktree(child, context, runtime));
108
128
  if (result.status === "failed") {
109
129
  abortController.abort();
110
130
  limit.clearQueue();
@@ -121,4 +141,4 @@ function parallelOutput(children, results) {
121
141
  return JSON.stringify({ children: Object.fromEntries(children.filter((child) => outputsByNode.has(child.id)).map((child) => [child.id, outputsByNode.get(child.id)])) });
122
142
  }
123
143
  //#endregion
124
- export { executeParallelNode, parallelEvidence, parallelOutput };
144
+ export { childCategory, executeParallelNode, parallelEvidence, parallelOutput };
@@ -1,13 +1,16 @@
1
+ import { createRunnerLaunchPlan } from "../../runner.js";
2
+ import { normalizeRunnerOutput } from "../../runner-output.js";
1
3
  import { parseJsonObject } from "../json-validation/json-validation.js";
2
4
  import "../json-validation/index.js";
3
5
  //#region src/runtime/select-candidate/select-candidate.ts
6
+ const SCORE_RE = /-?\d+(?:\.\d+)?/;
4
7
  function selectBestCandidate(candidates) {
5
8
  const passing = candidates.filter((candidate) => candidate.status === "PASS");
6
9
  if (passing.length === 0) return null;
7
10
  return passing.reduce((best, candidate) => (candidate.judgeScore ?? 0) > (best.judgeScore ?? 0) ? candidate : best);
8
11
  }
9
- function executeSelectCandidateBuiltin(context, node) {
10
- const candidates = readCandidates(context, node?.needs.at(0) ?? null);
12
+ async function executeSelectCandidateBuiltin(context, node) {
13
+ const candidates = await scoreCandidates(context, readCandidates(context, node?.needs.at(0) ?? null));
11
14
  const selected = selectBestCandidate(candidates);
12
15
  if (!selected) return {
13
16
  evidence: [`select-candidate: no passing candidate among ${candidates.length}`, ...candidates.map((candidate) => `- ${candidate.nodeId}: FAIL`)],
@@ -20,6 +23,61 @@ function executeSelectCandidateBuiltin(context, node) {
20
23
  output: selected.output
21
24
  };
22
25
  }
26
+ async function scoreCandidates(context, candidates) {
27
+ const model = context.config.best_of_n?.judge_model;
28
+ const runner = Object.keys(context.config.runners).at(0);
29
+ if (!(model && runner)) return candidates;
30
+ return await Promise.all(candidates.map((candidate) => scoreCandidate(context, candidate, runner, model)));
31
+ }
32
+ async function scoreCandidate(context, candidate, runner, model) {
33
+ const plan = judgePlan(context, candidate, runner, model);
34
+ context.agentInvocations.push(plan);
35
+ const judgeScore = parseScore(normalizeRunnerOutput(plan, (await context.executor(plan, { signal: context.signal })).stdout).output);
36
+ return judgeScore === null ? candidate : {
37
+ ...candidate,
38
+ judgeScore
39
+ };
40
+ }
41
+ function judgePlan(context, candidate, runner, model) {
42
+ const profileId = `select-candidate:judge:${candidate.nodeId}`;
43
+ return createRunnerLaunchPlan({
44
+ ...context.config,
45
+ profiles: {
46
+ ...context.config.profiles,
47
+ [profileId]: {
48
+ filesystem: { mode: "read-only" },
49
+ instructions: { inline: "Score the candidate implementation." },
50
+ network: { mode: "disabled" },
51
+ output: { format: "text" },
52
+ runner,
53
+ tools: []
54
+ }
55
+ }
56
+ }, {
57
+ model,
58
+ nodeId: profileId,
59
+ profileId,
60
+ prompt: judgePrompt(context.task, candidate.output),
61
+ worktreePath: context.worktreePath
62
+ });
63
+ }
64
+ function judgePrompt(task, output) {
65
+ return [
66
+ "Score how well this candidate implementation satisfies the task.",
67
+ "Return ONLY a number between 0 and 1 (1 = best). No prose, no fences.",
68
+ "",
69
+ `Task: ${task}`,
70
+ "",
71
+ "Candidate result:",
72
+ output
73
+ ].join("\n");
74
+ }
75
+ function parseScore(text) {
76
+ const match = SCORE_RE.exec(text);
77
+ if (!match) return null;
78
+ const value = Number(match[0]);
79
+ return Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : null;
80
+ }
23
81
  function readCandidates(context, upstreamNodeId) {
24
82
  if (!upstreamNodeId) return [];
25
83
  const upstream = context.plan.graph.node(upstreamNodeId);
package/package.json CHANGED
@@ -121,7 +121,7 @@
121
121
  "prepack": "bun run build:cli"
122
122
  },
123
123
  "type": "module",
124
- "version": "2.5.0",
124
+ "version": "2.7.0",
125
125
  "description": "Config-driven multi-agent pipeline runner for repository work",
126
126
  "main": "./dist/index.js",
127
127
  "types": "./dist/index.d.ts",