@boboddy/sdk 0.4.3 → 0.5.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.
Files changed (32) hide show
  1. package/dist/client.js +0 -23
  2. package/dist/definitions/advancement-policies/define-advancement-policy.d.ts +30 -5
  3. package/dist/definitions/advancement-policies/index.js +11 -1
  4. package/dist/definitions/pipelines/bindings.d.ts +62 -0
  5. package/dist/definitions/pipelines/builder-helpers.d.ts +19 -106
  6. package/dist/definitions/pipelines/chain-graph.d.ts +15 -14
  7. package/dist/definitions/pipelines/compile-node-definitions.d.ts +27 -0
  8. package/dist/definitions/pipelines/define-pipeline.d.ts +101 -154
  9. package/dist/definitions/pipelines/index.d.ts +0 -2
  10. package/dist/definitions/pipelines/index.js +470 -615
  11. package/dist/definitions/pipelines/node-input-ctx.d.ts +31 -0
  12. package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +18 -4
  13. package/dist/definitions/pipelines/pipeline-states.d.ts +104 -0
  14. package/dist/definitions/steps/define-code-step.d.ts +50 -0
  15. package/dist/definitions/steps/define-step.d.ts +22 -1
  16. package/dist/definitions/steps/index.d.ts +1 -0
  17. package/dist/definitions/steps/index.js +41 -24
  18. package/dist/definitions/steps/step-definitions-client.d.ts +29 -5
  19. package/dist/definitions/validation/index.js +15148 -94
  20. package/dist/definitions/validation/validate-definition-specs.d.ts +19 -1
  21. package/dist/generated/index.d.ts +1 -1
  22. package/dist/generated/sdk.gen.d.ts +1 -4
  23. package/dist/generated/types.gen.d.ts +296 -878
  24. package/dist/index.js +510 -615
  25. package/dist/push/collect-definitions.d.ts +40 -1
  26. package/dist/push/index.d.ts +2 -2
  27. package/dist/push/index.js +788 -738
  28. package/dist/step-execution-plane-client.d.ts +9 -4
  29. package/package.json +2 -2
  30. package/dist/definitions/pipelines/builder.d.ts +0 -80
  31. package/dist/definitions/pipelines/fan-out-builder.d.ts +0 -66
  32. package/dist/definitions/pipelines/input-accessor.d.ts +0 -25
package/dist/index.js CHANGED
@@ -1093,29 +1093,6 @@ class PipelineDefinitions extends HeyApiClient {
1093
1093
  unarchivePipelineDefinition(options) {
1094
1094
  return (options.client ?? this.client).put({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/unarchive", ...options });
1095
1095
  }
1096
- addPipelineStep(options) {
1097
- return (options.client ?? this.client).post({
1098
- url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps",
1099
- ...options,
1100
- headers: {
1101
- "Content-Type": "application/json",
1102
- ...options.headers
1103
- }
1104
- });
1105
- }
1106
- removePipelineStep(options) {
1107
- return (options.client ?? this.client).delete({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}", ...options });
1108
- }
1109
- updatePipelineStep(options) {
1110
- return (options.client ?? this.client).put({
1111
- url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}",
1112
- ...options,
1113
- headers: {
1114
- "Content-Type": "application/json",
1115
- ...options.headers
1116
- }
1117
- });
1118
- }
1119
1096
  setPipelineStepAdvancementPolicy(options) {
1120
1097
  return (options.client ?? this.client).put({
1121
1098
  url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}/advancement-policy",
@@ -16147,6 +16124,32 @@ ${feature._promptAddition}` : feature._promptAddition;
16147
16124
  };
16148
16125
  return spec;
16149
16126
  }
16127
+ // src/definitions/steps/define-code-step.ts
16128
+ function codeStep(config2) {
16129
+ const spec = {
16130
+ key: config2.key,
16131
+ name: config2.name,
16132
+ description: config2.description ?? null,
16133
+ version: config2.version ?? 1,
16134
+ kind: "code",
16135
+ status: config2.status ?? "active",
16136
+ prompt: null,
16137
+ inputSchemaJson: config2.inputSchema ? toJSONSchema(config2.inputSchema) : null,
16138
+ resultSchemaJson: config2.resultSchema ? toJSONSchema(config2.resultSchema) : null,
16139
+ signalExtractorDefinitions: (config2.signals ?? []).map((signal) => ({
16140
+ key: signal.key ?? signal.sourcePath,
16141
+ sourcePath: signal.sourcePath,
16142
+ type: signal.type,
16143
+ required: signal.required ?? true,
16144
+ availableWhenResultStatusIn: signal.availableWhenResultStatusIn ?? null
16145
+ })),
16146
+ opencodeMcpJson: null,
16147
+ opencodePluginJson: null,
16148
+ healthChecksJson: null,
16149
+ entrypoint: { fn: config2.fn }
16150
+ };
16151
+ return spec;
16152
+ }
16150
16153
  // src/definitions/steps/step-definitions-client.ts
16151
16154
  function createStepDefinitionsClient(baseUrl) {
16152
16155
  const client2 = createClient({ baseUrl });
@@ -16174,8 +16177,21 @@ var buildStepDefinitionsClient = (stepDefinitions) => {
16174
16177
  },
16175
16178
  upsertFromSpec: async (projectId, spec, options) => {
16176
16179
  const body = {
16177
- ...spec,
16180
+ key: spec.key,
16181
+ name: spec.name,
16182
+ description: spec.description,
16178
16183
  prompt: spec.prompt ?? "",
16184
+ version: spec.version,
16185
+ kind: spec.kind,
16186
+ entrypointJson: spec.entrypointJson ?? null,
16187
+ executionMode: spec.executionMode,
16188
+ inputSchemaJson: spec.inputSchemaJson,
16189
+ resultSchemaJson: spec.resultSchemaJson,
16190
+ opencodeMcpJson: spec.opencodeMcpJson,
16191
+ opencodePluginJson: spec.opencodePluginJson,
16192
+ healthChecksJson: spec.healthChecksJson,
16193
+ status: spec.status,
16194
+ signalExtractorDefinitions: spec.signalExtractorDefinitions,
16179
16195
  projectId
16180
16196
  };
16181
16197
  const result = await stepDefinitions.upsertStepDefinition({
@@ -16289,10 +16305,19 @@ function any2(conditions, outcome) {
16289
16305
  return { _tag: "rule", mode: "any", conditions, outcome };
16290
16306
  }
16291
16307
  function when(signal2, operator, value, outcome) {
16308
+ const condition = {
16309
+ _tag: "signal",
16310
+ signal: signal2,
16311
+ operator,
16312
+ value
16313
+ };
16314
+ if (outcome === undefined) {
16315
+ return condition;
16316
+ }
16292
16317
  return {
16293
16318
  _tag: "rule",
16294
16319
  mode: "all",
16295
- conditions: [{ _tag: "signal", signal: signal2, operator, value }],
16320
+ conditions: [condition],
16296
16321
  outcome
16297
16322
  };
16298
16323
  }
@@ -16468,436 +16493,9 @@ function extractInlineStepSignalsListDefinitions(policy) {
16468
16493
  return [...byKey.values()];
16469
16494
  }
16470
16495
 
16471
- // src/definitions/pipelines/chain-graph.ts
16472
- function tryOrderChainNodeDefinitions(nodeDefinitions, dependencyEdges) {
16473
- const nodesByKey = new Map;
16474
- for (const nodeDefinition of nodeDefinitions) {
16475
- nodesByKey.set(nodeDefinition.nodeKey, nodeDefinition);
16476
- }
16477
- const outgoing = new Map;
16478
- const incomingCount = new Map;
16479
- for (const nodeDefinition of nodeDefinitions) {
16480
- incomingCount.set(nodeDefinition.nodeKey, 0);
16481
- }
16482
- for (const edge of dependencyEdges) {
16483
- if (!nodesByKey.has(edge.fromNodeKey) || !nodesByKey.has(edge.toNodeKey)) {
16484
- return null;
16485
- }
16486
- if (outgoing.has(edge.fromNodeKey))
16487
- return null;
16488
- outgoing.set(edge.fromNodeKey, edge.toNodeKey);
16489
- incomingCount.set(edge.toNodeKey, (incomingCount.get(edge.toNodeKey) ?? 0) + 1);
16490
- }
16491
- for (const count of incomingCount.values()) {
16492
- if (count > 1)
16493
- return null;
16494
- }
16495
- if (nodeDefinitions.length === 0)
16496
- return [];
16497
- const roots = nodeDefinitions.filter((nodeDefinition) => (incomingCount.get(nodeDefinition.nodeKey) ?? 0) === 0);
16498
- if (roots.length !== 1)
16499
- return null;
16500
- const rootNode = roots[0];
16501
- if (!rootNode)
16502
- return null;
16503
- const ordered = [];
16504
- const visited = new Set;
16505
- let currentKey = rootNode.nodeKey;
16506
- while (currentKey !== undefined) {
16507
- if (visited.has(currentKey))
16508
- return null;
16509
- visited.add(currentKey);
16510
- const currentNode = nodesByKey.get(currentKey);
16511
- if (!currentNode)
16512
- return null;
16513
- ordered.push(currentNode);
16514
- currentKey = outgoing.get(currentKey);
16515
- }
16516
- if (ordered.length !== nodeDefinitions.length)
16517
- return null;
16518
- return ordered;
16519
- }
16520
- function buildChainDependencyEdges(orderedNodes) {
16521
- const dependencyEdges = [];
16522
- for (let index = 0;index < orderedNodes.length - 1; index += 1) {
16523
- const from = orderedNodes[index];
16524
- const to = orderedNodes[index + 1];
16525
- if (!from || !to)
16526
- continue;
16527
- dependencyEdges.push({ fromNodeKey: from.nodeKey, toNodeKey: to.nodeKey });
16528
- }
16529
- return dependencyEdges;
16530
- }
16531
-
16532
- // src/definitions/pipelines/define-pipeline.ts
16533
- function serializeBinding(binding) {
16534
- if (binding.source === "pipeline_input") {
16535
- return { source: "pipeline_input", path: binding.path };
16536
- }
16537
- if (binding.source === "work_item") {
16538
- return { source: "work_item", field: binding.field };
16539
- }
16540
- if (binding.source === "step_signal") {
16541
- return {
16542
- source: "step_signal",
16543
- stepKey: binding.step.key,
16544
- signalKey: binding.signalKey
16545
- };
16546
- }
16547
- if (binding.source === "literal") {
16548
- return { source: "literal", value: binding.value };
16549
- }
16550
- if (binding.source === "signals_list") {
16551
- return { source: "signals_list", stepKey: binding.fanOutStep.key };
16552
- }
16553
- if (binding.source === "fan_out_item") {
16554
- return { source: "fan_out_item" };
16555
- }
16556
- return { source: "step_output", stepKey: binding.step.key };
16557
- }
16558
- var isFanOutConfig = (node) => ("nodeType" in node) && node.nodeType === "fanOut";
16559
- var isCohortGateConfig = (node) => ("nodeType" in node) && node.nodeType === "cohortGate";
16560
- function serializeInputBindings(input, pipelineInputBindings) {
16561
- const autoBindings = {
16562
- workItemTitle: { source: "work_item", field: "title" },
16563
- workItemDescription: { source: "work_item", field: "description" }
16564
- };
16565
- const pipelineBindings = {};
16566
- for (const [key, binding] of Object.entries(pipelineInputBindings ?? {})) {
16567
- pipelineBindings[key] = serializeBinding(binding);
16568
- }
16569
- const explicitBindings = Object.fromEntries(Object.entries(input ?? {}).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
16570
- return { ...autoBindings, ...pipelineBindings, ...explicitBindings };
16571
- }
16572
- function buildPipelineSpec(config2) {
16573
- const nodes = config2.nodes;
16574
- const stepDefMap = new Map;
16575
- const registerStepDef = (step) => {
16576
- const mapKey = `${step.key}@v${String(step.version)}`;
16577
- if (!stepDefMap.has(mapKey)) {
16578
- stepDefMap.set(mapKey, step);
16579
- }
16580
- };
16581
- for (const node of nodes) {
16582
- if (isFanOutConfig(node)) {
16583
- registerStepDef(node.fanOutStep);
16584
- } else if (!isCohortGateConfig(node)) {
16585
- registerStepDef(node.step);
16586
- }
16587
- }
16588
- let inputSchemaJson = null;
16589
- if (config2.input) {
16590
- try {
16591
- inputSchemaJson = exports_external.toJSONSchema(config2.input);
16592
- } catch {
16593
- inputSchemaJson = null;
16594
- }
16595
- }
16596
- const nodeDefinitions = nodes.map((node) => {
16597
- if (isCohortGateConfig(node)) {
16598
- const serializedPolicy = serializeCohortAdvancementPolicy(node.advanceAll);
16599
- const inlineStepSignalsListDefinitions = extractInlineStepSignalsListDefinitions(node.advanceAll);
16600
- return {
16601
- nodeKey: node.nodeKey,
16602
- kind: "cohortGate",
16603
- advanceAllPolicyDefinition: serializedPolicy,
16604
- stepSignalsListDefinitions: [
16605
- ...inlineStepSignalsListDefinitions,
16606
- ...node.stepSignalsListDefinitions ?? []
16607
- ]
16608
- };
16609
- }
16610
- if (isFanOutConfig(node)) {
16611
- return {
16612
- nodeKey: node.fanOutStep.key,
16613
- kind: "fanOut",
16614
- stepKey: node.fanOutStep.key,
16615
- stepName: node.fanOutStep.name,
16616
- stepDescription: node.fanOutStep.description,
16617
- inputBindingsJson: serializeInputBindings(node.input, config2.pipelineInputBindings),
16618
- timeoutSeconds: node.timeout ?? null,
16619
- overSignalKey: node.overSignalKey,
16620
- advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(node.advanceEach)
16621
- };
16622
- }
16623
- return {
16624
- nodeKey: node.step.key,
16625
- kind: "step",
16626
- stepKey: node.step.key,
16627
- stepName: node.step.name,
16628
- stepDescription: node.step.description,
16629
- inputBindingsJson: serializeInputBindings(node.input, config2.pipelineInputBindings),
16630
- timeoutSeconds: node.timeout ?? null,
16631
- advancementPolicyDefinition: serializeAdvancementPolicy(node.advancement),
16632
- computedSignalDefinitions: extractInlineComputedSignals(node.advancement)
16633
- };
16634
- });
16635
- const dependencyEdges = buildChainDependencyEdges(nodeDefinitions);
16636
- return {
16637
- key: config2.key,
16638
- name: config2.name,
16639
- description: config2.description ?? null,
16640
- version: config2.version ?? 1,
16641
- status: config2.status ?? "active",
16642
- inputSchemaJson,
16643
- _stepDefinitions: [...stepDefMap.values()],
16644
- nodeDefinitions,
16645
- dependencyEdges
16646
- };
16647
- }
16648
- // src/definitions/advancement-policies/fluent-rules.ts
16649
- var LEAF_BRAND = Symbol.for("boboddy.fluentRule.leaf");
16650
- var GROUP_BRAND = Symbol.for("boboddy.fluentRule.group");
16651
- var SIGNAL_KEY = Symbol.for("boboddy.fluentRule.signalKey");
16652
- function createSignalRef(signal2) {
16653
- const leaf = (operator, value) => {
16654
- const condition = {
16655
- _tag: "signal",
16656
- signal: signal2,
16657
- operator,
16658
- value
16659
- };
16660
- return {
16661
- [LEAF_BRAND]: condition,
16662
- then(outcome) {
16663
- return {
16664
- _tag: "rule",
16665
- mode: "all",
16666
- conditions: [condition],
16667
- outcome
16668
- };
16669
- }
16670
- };
16671
- };
16672
- return {
16673
- eq: (v) => leaf("equal", v),
16674
- ne: (v) => leaf("notEqual", v),
16675
- gt: (v) => leaf("greaterThan", v),
16676
- gte: (v) => leaf("greaterThanInclusive", v),
16677
- lt: (v) => leaf("lessThan", v),
16678
- lte: (v) => leaf("lessThanInclusive", v),
16679
- in: (vs) => leaf("in", vs),
16680
- notIn: (vs) => leaf("notIn", vs),
16681
- contains: (v) => leaf("contains", v),
16682
- doesNotContain: (v) => leaf("doesNotContain", v)
16683
- };
16684
- }
16685
- function extractCondition(ref) {
16686
- if (LEAF_BRAND in ref)
16687
- return ref[LEAF_BRAND];
16688
- const group = ref[GROUP_BRAND];
16689
- return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
16690
- }
16691
- function createGroup(mode, refs) {
16692
- const conditions = refs.map(extractCondition);
16693
- return {
16694
- [GROUP_BRAND]: { mode, conditions },
16695
- then(outcome) {
16696
- return { _tag: "rule", mode, conditions, outcome };
16697
- }
16698
- };
16699
- }
16700
- function resolveComputedArg(arg) {
16701
- return arg[SIGNAL_KEY];
16702
- }
16703
- function makeKeyedSignalRef(key) {
16704
- const ref = createSignalRef(key);
16705
- return Object.assign(ref, {
16706
- [SIGNAL_KEY]: key
16707
- });
16708
- }
16709
- function makeAdvanceCtx() {
16710
- const wrapComputed = (token) => createSignalRef(token);
16711
- return {
16712
- signal: (key) => makeKeyedSignalRef(key),
16713
- stepSignals: new Proxy({}, {
16714
- get(_, key) {
16715
- if (typeof key === "string")
16716
- return makeKeyedSignalRef(key);
16717
- return;
16718
- }
16719
- }),
16720
- avg: (...args) => wrapComputed(Computed.average(args.map(resolveComputedArg))),
16721
- weightedAvg: (...args) => wrapComputed(Computed.weightedAverage(args.map(resolveComputedArg))),
16722
- sum: (...args) => wrapComputed(Computed.sum(args.map(resolveComputedArg))),
16723
- min: (...args) => wrapComputed(Computed.min(args.map(resolveComputedArg))),
16724
- max: (...args) => wrapComputed(Computed.max(args.map(resolveComputedArg))),
16725
- count: (...args) => wrapComputed(Computed.count(args.map(resolveComputedArg))),
16726
- booleanAny: (...args) => wrapComputed(Computed.booleanAny(args.map(resolveComputedArg))),
16727
- booleanAll: (...args) => wrapComputed(Computed.booleanAll(args.map(resolveComputedArg))),
16728
- all: (...refs) => createGroup("all", refs),
16729
- any: (...refs) => createGroup("any", refs),
16730
- route: (pipelineKey, inputJson) => inputJson !== undefined ? { outcome: "route", pipelineKey, inputJson } : { outcome: "route", pipelineKey }
16731
- };
16732
- }
16733
-
16734
- // src/definitions/pipelines/input-accessor.ts
16735
- var ACCESSOR_BRAND = Symbol.for("boboddy.inputAccessor.brand");
16736
- var ACCESSOR_PATH = Symbol.for("boboddy.inputAccessor.path");
16737
- function createInputAccessor(_schema) {
16738
- return createProxy([]);
16739
- }
16740
- function createProxy(path) {
16741
- const pathStr = path.join(".");
16742
- const target = Object.freeze({});
16743
- return new Proxy(target, {
16744
- get(_t, prop) {
16745
- if (prop === ACCESSOR_BRAND)
16746
- return true;
16747
- if (prop === ACCESSOR_PATH)
16748
- return pathStr;
16749
- if (prop === "toJSON") {
16750
- return () => ({
16751
- source: "pipeline_input",
16752
- path: pathStr
16753
- });
16754
- }
16755
- if (prop === Symbol.toPrimitive) {
16756
- return () => {
16757
- throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" cannot be coerced to a primitive. ` + `Pass it to a step input field instead of using it in a string/number expression.`);
16758
- };
16759
- }
16760
- if (typeof prop === "symbol")
16761
- return;
16762
- return createProxy([...path, prop]);
16763
- },
16764
- has(_t, prop) {
16765
- return prop === ACCESSOR_BRAND || prop === ACCESSOR_PATH;
16766
- },
16767
- ownKeys() {
16768
- throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" cannot be enumerated. ` + `Drill into specific fields instead of spreading the input.`);
16769
- },
16770
- set() {
16771
- throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" is read-only.`);
16772
- }
16773
- });
16774
- }
16775
- function isInputAccessor(value) {
16776
- return typeof value === "object" && value !== null && value[ACCESSOR_BRAND] === true;
16777
- }
16778
- function materializeAccessor(accessor) {
16779
- return {
16780
- source: "pipeline_input",
16781
- path: accessor[ACCESSOR_PATH]
16782
- };
16783
- }
16784
-
16785
- // src/definitions/pipelines/work-item-fields.ts
16786
- var WORK_ITEM_TOP_LEVEL_FIELDS = [
16787
- "id",
16788
- "projectId",
16789
- "platform",
16790
- "platformId",
16791
- "platformKey",
16792
- "url",
16793
- "title",
16794
- "description",
16795
- "sourceCreatedAt",
16796
- "sourceUpdatedAt",
16797
- "createdByUserId",
16798
- "parentWorkItemId",
16799
- "createdAt",
16800
- "updatedAt"
16801
- ];
16802
- var WORK_ITEM_FIELDS_PATH_PREFIX = "fields.";
16803
- function resolveWorkItemFieldPath(record2, path) {
16804
- if (typeof record2 !== "object" || record2 === null)
16805
- return;
16806
- const asRecord = record2;
16807
- if (path.startsWith(WORK_ITEM_FIELDS_PATH_PREFIX)) {
16808
- const fieldName = path.slice(WORK_ITEM_FIELDS_PATH_PREFIX.length);
16809
- const fields = asRecord["fields"];
16810
- if (typeof fields !== "object" || fields === null)
16811
- return;
16812
- return fields[fieldName];
16813
- }
16814
- return asRecord[path];
16815
- }
16816
-
16817
- // src/definitions/pipelines/builder-helpers.ts
16818
- var WORK_ITEM_ACCESSOR = Object.freeze({
16819
- ...Object.fromEntries(WORK_ITEM_TOP_LEVEL_FIELDS.map((field) => [
16820
- field,
16821
- Object.freeze({ source: "work_item", field })
16822
- ])),
16823
- field: (fieldName) => Object.freeze({
16824
- source: "work_item",
16825
- field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
16826
- })
16827
- });
16828
- var WORK_ITEM_FIELD_BINDINGS = {
16829
- workItemTitle: { source: "work_item", field: "title" },
16830
- workItemDescription: { source: "work_item", field: "description" }
16831
- };
16832
- function makeStepInputCtx(inputSchema) {
16833
- const baseAccessor = createInputAccessor(inputSchema);
16834
- const input = new Proxy(baseAccessor, {
16835
- get(target, prop) {
16836
- if (typeof prop === "string" && prop in WORK_ITEM_FIELD_BINDINGS) {
16837
- return WORK_ITEM_FIELD_BINDINGS[prop];
16838
- }
16839
- return target[prop];
16840
- }
16841
- });
16842
- return {
16843
- input,
16844
- signal(step, key) {
16845
- return { source: "step_signal", step, signalKey: key };
16846
- },
16847
- output(step) {
16848
- return { source: "step_output", step };
16849
- },
16850
- literal: literal2,
16851
- signalsList(fanOutStep) {
16852
- return { source: "signals_list", fanOutStep };
16853
- }
16854
- };
16855
- }
16856
- function literal2(value) {
16857
- return { source: "literal", value };
16858
- }
16859
- function normalizeInputMapping(mapping) {
16860
- if (!mapping)
16861
- return;
16862
- const out = {};
16863
- for (const [key, value] of Object.entries(mapping)) {
16864
- if (value === undefined)
16865
- continue;
16866
- out[key] = isInputAccessor(value) ? materializeAccessor(value) : value;
16867
- }
16868
- return out;
16869
- }
16870
- function resolveAdditionalStepInputBindings(label, definition) {
16871
- if (!definition) {
16872
- return {};
16873
- }
16874
- const raw = definition.bindings({
16875
- workItemField: (fieldName) => ({
16876
- source: "work_item",
16877
- field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
16878
- }),
16879
- literal: literal2
16880
- });
16881
- if (definition.schema instanceof exports_external.ZodObject) {
16882
- const validKeys = new Set(Object.keys(definition.schema.shape));
16883
- const unknown2 = Object.keys(raw).filter((key) => !validKeys.has(key));
16884
- if (unknown2.length > 0) {
16885
- throw new Error(`${label}.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((key) => `"${key}"`).join(", ")}`);
16886
- }
16887
- }
16888
- return normalizeInputMapping(raw) ?? {};
16889
- }
16890
- function mergeStepBindings(pipelineBindings, explicitBindings) {
16891
- const merged = {
16892
- ...pipelineBindings,
16893
- ...explicitBindings ?? {}
16894
- };
16895
- return Object.keys(merged).length > 0 ? merged : undefined;
16896
- }
16897
-
16898
16496
  // src/definitions/advancement-policies/cohort-fluent-rules.ts
16899
- var LEAF_BRAND2 = Symbol("boboddy.cohortRule.leaf");
16900
- var GROUP_BRAND2 = Symbol("boboddy.cohortRule.group");
16497
+ var LEAF_BRAND = Symbol("boboddy.cohortRule.leaf");
16498
+ var GROUP_BRAND = Symbol("boboddy.cohortRule.group");
16901
16499
  function createCohortSignalRef(signal2) {
16902
16500
  const leaf = (operator, value) => {
16903
16501
  const condition = {
@@ -16907,7 +16505,7 @@ function createCohortSignalRef(signal2) {
16907
16505
  value
16908
16506
  };
16909
16507
  return {
16910
- [LEAF_BRAND2]: condition,
16508
+ [LEAF_BRAND]: condition,
16911
16509
  then(outcome, paramsJson) {
16912
16510
  return {
16913
16511
  _tag: "rule",
@@ -16934,7 +16532,7 @@ function createCohortSignalRef(signal2) {
16934
16532
  }
16935
16533
  function createCohortLeafFromCondition(condition) {
16936
16534
  return {
16937
- [LEAF_BRAND2]: condition,
16535
+ [LEAF_BRAND]: condition,
16938
16536
  then(outcome, paramsJson) {
16939
16537
  return {
16940
16538
  _tag: "rule",
@@ -16947,15 +16545,15 @@ function createCohortLeafFromCondition(condition) {
16947
16545
  };
16948
16546
  }
16949
16547
  function extractCohortCondition(ref) {
16950
- if (LEAF_BRAND2 in ref)
16951
- return ref[LEAF_BRAND2];
16952
- const group = ref[GROUP_BRAND2];
16548
+ if (LEAF_BRAND in ref)
16549
+ return ref[LEAF_BRAND];
16550
+ const group = ref[GROUP_BRAND];
16953
16551
  return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
16954
16552
  }
16955
16553
  function createCohortGroup(mode, refs) {
16956
16554
  const conditions = refs.map(extractCohortCondition);
16957
16555
  return {
16958
- [GROUP_BRAND2]: { mode, conditions },
16556
+ [GROUP_BRAND]: { mode, conditions },
16959
16557
  then(outcome, paramsJson) {
16960
16558
  return {
16961
16559
  _tag: "rule",
@@ -17060,138 +16658,461 @@ function makeAdvanceAllCtx() {
17060
16658
  };
17061
16659
  }
17062
16660
 
17063
- // src/definitions/pipelines/fan-out-builder.ts
17064
- function beginFanOut(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, config2) {
17065
- const baseCtx = makeStepInputCtx(inputSchema);
17066
- const ctx = {
17067
- ...baseCtx,
16661
+ // src/definitions/pipelines/bindings.ts
16662
+ function serializeBinding(binding) {
16663
+ if (binding.source === "pipeline_input") {
16664
+ return { source: "pipeline_input", path: binding.path };
16665
+ }
16666
+ if (binding.source === "work_item") {
16667
+ return { source: "work_item", field: binding.field };
16668
+ }
16669
+ if (binding.source === "step_signal") {
16670
+ return {
16671
+ source: "step_signal",
16672
+ stepKey: binding.nodeKey,
16673
+ signalKey: binding.signalKey
16674
+ };
16675
+ }
16676
+ if (binding.source === "literal") {
16677
+ return { source: "literal", value: binding.value };
16678
+ }
16679
+ if (binding.source === "signals_list") {
16680
+ return { source: "signals_list", stepKey: binding.nodeKey };
16681
+ }
16682
+ if (binding.source === "fan_out_item") {
16683
+ return { source: "fan_out_item" };
16684
+ }
16685
+ return { source: "step_output", stepKey: binding.nodeKey };
16686
+ }
16687
+ function serializeInputBindings(input) {
16688
+ const autoBindings = {
16689
+ workItemTitle: { source: "work_item", field: "title" },
16690
+ workItemDescription: { source: "work_item", field: "description" }
16691
+ };
16692
+ const explicitBindings = Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
16693
+ return { ...autoBindings, ...explicitBindings };
16694
+ }
16695
+
16696
+ // src/definitions/pipelines/work-item-fields.ts
16697
+ var WORK_ITEM_TOP_LEVEL_FIELDS = [
16698
+ "id",
16699
+ "projectId",
16700
+ "platform",
16701
+ "platformId",
16702
+ "platformKey",
16703
+ "url",
16704
+ "title",
16705
+ "description",
16706
+ "sourceCreatedAt",
16707
+ "sourceUpdatedAt",
16708
+ "createdByUserId",
16709
+ "parentWorkItemId",
16710
+ "createdAt",
16711
+ "updatedAt"
16712
+ ];
16713
+ var WORK_ITEM_FIELDS_PATH_PREFIX = "fields.";
16714
+ function resolveWorkItemFieldPath(record2, path) {
16715
+ if (typeof record2 !== "object" || record2 === null)
16716
+ return;
16717
+ const asRecord = record2;
16718
+ if (path.startsWith(WORK_ITEM_FIELDS_PATH_PREFIX)) {
16719
+ const fieldName = path.slice(WORK_ITEM_FIELDS_PATH_PREFIX.length);
16720
+ const fields = asRecord["fields"];
16721
+ if (typeof fields !== "object" || fields === null)
16722
+ return;
16723
+ return fields[fieldName];
16724
+ }
16725
+ return asRecord[path];
16726
+ }
16727
+
16728
+ // src/definitions/pipelines/builder-helpers.ts
16729
+ var WORK_ITEM_ACCESSOR = Object.freeze({
16730
+ ...Object.fromEntries(WORK_ITEM_TOP_LEVEL_FIELDS.map((field) => [
16731
+ field,
16732
+ Object.freeze({ source: "work_item", field })
16733
+ ])),
16734
+ field: (fieldName) => Object.freeze({
16735
+ source: "work_item",
16736
+ field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
16737
+ })
16738
+ });
16739
+ function literal2(value) {
16740
+ return { source: "literal", value };
16741
+ }
16742
+
16743
+ // src/definitions/pipelines/node-input-ctx.ts
16744
+ function makeBaseNodeInputCtx() {
16745
+ return {
16746
+ pipelineInput: (path) => ({ source: "pipeline_input", path }),
16747
+ workItem: WORK_ITEM_ACCESSOR,
16748
+ signal: (nodeKey, signalKey) => ({
16749
+ source: "step_signal",
16750
+ nodeKey,
16751
+ signalKey
16752
+ }),
16753
+ output: (nodeKey) => ({ source: "step_output", nodeKey }),
16754
+ signalsList: (nodeKey) => ({ source: "signals_list", nodeKey }),
16755
+ literal: literal2
16756
+ };
16757
+ }
16758
+ function makeNodeInputCtx() {
16759
+ return makeBaseNodeInputCtx();
16760
+ }
16761
+ function makeFanOutNodeInputCtx() {
16762
+ return {
16763
+ ...makeBaseNodeInputCtx(),
17068
16764
  item: { source: "fan_out_item" }
17069
16765
  };
17070
- const rawInput = config2.input ? config2.input(ctx) : {};
17071
- const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
17072
- const fanOutNodeConfig = {
17073
- nodeType: "fanOut",
17074
- fanOutStep: step,
17075
- overSignalKey: config2.over,
17076
- ...input ? { input } : {},
17077
- ...config2.timeout !== undefined ? { timeout: config2.timeout } : {}
17078
- };
17079
- const advanceEachCtx = makeAdvanceEachCtx();
17080
- const advanceEachResult = config2.advance(advanceEachCtx);
16766
+ }
16767
+
16768
+ // src/definitions/pipelines/compile-node-definitions.ts
16769
+ function assertTargetExists(ctx, fromKey, toKey) {
16770
+ if (!ctx.stateKeys.has(toKey)) {
16771
+ throw new Error(`Pipeline "${ctx.pipelineKey}": state "${fromKey}" targets unknown state "${toKey}"`);
16772
+ }
16773
+ }
16774
+ function overSignalKeyOf(over) {
16775
+ const lastDot = over.lastIndexOf(".");
16776
+ return lastDot === -1 ? over : over.slice(lastDot + 1);
16777
+ }
16778
+ function compileStepState(stateKey, state, ctx) {
16779
+ ctx.registerStep(state.step);
16780
+ const inputCtx = makeNodeInputCtx();
16781
+ const rawInput = state.input ? state.input(inputCtx) : {};
16782
+ const inputBindingsJson = serializeInputBindings(rawInput);
16783
+ const routeTarget = typeof state.next === "object" ? state.next : null;
16784
+ const defaultOutcome = routeTarget ? {
16785
+ outcome: "route",
16786
+ pipelineKey: routeTarget.routeToPipeline,
16787
+ ...routeTarget.input ? { inputJson: routeTarget.input } : {}
16788
+ } : "continue";
16789
+ const rules = state.blockWhen ? [{ _tag: "rule", mode: "all", conditions: [state.blockWhen], outcome: "block" }] : [];
16790
+ const policy = { defaultOutcome, rules };
16791
+ const nodeDefinitions = [
16792
+ {
16793
+ nodeKey: stateKey,
16794
+ kind: "step",
16795
+ stepKey: state.step.key,
16796
+ stepName: state.step.name,
16797
+ stepDescription: state.step.description,
16798
+ inputBindingsJson,
16799
+ timeoutSeconds: state.timeout ?? null,
16800
+ advancementPolicyDefinition: serializeAdvancementPolicy(policy),
16801
+ computedSignalDefinitions: extractInlineComputedSignals(policy)
16802
+ }
16803
+ ];
16804
+ const edges = [];
16805
+ if (!routeTarget) {
16806
+ const nextKey = state.next;
16807
+ assertTargetExists(ctx, stateKey, nextKey);
16808
+ edges.push({ fromNodeKey: stateKey, toNodeKey: nextKey });
16809
+ }
16810
+ return { nodeDefinitions, edges };
16811
+ }
16812
+ function compileChoiceState(stateKey, state, ctx) {
16813
+ const stateChoices = state.choices ?? [];
16814
+ if (stateChoices.length === 0 && !state.default) {
16815
+ throw new Error(`Pipeline "${ctx.pipelineKey}": choice state "${stateKey}" requires at least one entry in choices or a default target`);
16816
+ }
16817
+ const choices = stateChoices.map((choiceCase) => {
16818
+ assertTargetExists(ctx, stateKey, choiceCase.next);
16819
+ return {
16820
+ conditionJson: serializeCondition(choiceCase.when),
16821
+ targetNodeKey: choiceCase.next
16822
+ };
16823
+ });
16824
+ if (state.default)
16825
+ assertTargetExists(ctx, stateKey, state.default);
16826
+ const edges = stateChoices.map((choiceCase) => ({
16827
+ fromNodeKey: stateKey,
16828
+ toNodeKey: choiceCase.next,
16829
+ discriminantJson: { conditionSummary: serializeCondition(choiceCase.when) }
16830
+ }));
16831
+ if (state.default) {
16832
+ edges.push({
16833
+ fromNodeKey: stateKey,
16834
+ toNodeKey: state.default,
16835
+ discriminantJson: { default: true }
16836
+ });
16837
+ }
16838
+ return {
16839
+ nodeDefinitions: [
16840
+ { nodeKey: stateKey, kind: "choice", choices, default: state.default ?? null }
16841
+ ],
16842
+ edges
16843
+ };
16844
+ }
16845
+ function compileFanOutState(stateKey, state, ctx) {
16846
+ ctx.registerStep(state.step);
16847
+ const inputCtx = makeFanOutNodeInputCtx();
16848
+ const rawInput = state.input ? state.input(inputCtx) : {};
16849
+ const inputBindingsJson = serializeInputBindings(rawInput);
16850
+ const advanceEachResult = state.advanceEach(makeAdvanceEachCtx());
17081
16851
  const advanceEachPolicy = {
17082
16852
  default: advanceEachResult.default,
17083
16853
  ...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
17084
16854
  };
17085
- fanOutNodeConfig.advanceEach = advanceEachPolicy;
17086
- const cohortGateNodeConfig = {
17087
- nodeType: "cohortGate",
17088
- nodeKey: `${step.key}__cohortGate`
17089
- };
17090
- const advanceAllCtx = makeAdvanceAllCtx();
17091
- const advanceAllResult = config2.advanceAll(advanceAllCtx);
16855
+ const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
17092
16856
  const advanceAllPolicy = {
17093
16857
  default: advanceAllResult.default,
17094
16858
  ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
17095
16859
  };
17096
- cohortGateNodeConfig.advanceAll = advanceAllPolicy;
17097
- nodes.push(fanOutNodeConfig, cohortGateNodeConfig);
17098
- return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
17099
- }
17100
-
17101
- // src/definitions/pipelines/builder.ts
17102
- function pushStep(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, rawOptions) {
17103
- const options = rawOptions;
17104
- const ctx = makeStepInputCtx(inputSchema);
17105
- const rawInput = options.input ? options.input(ctx) : {};
17106
- const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
17107
- const stepConfig = { step, input };
17108
- if (options.timeout !== undefined)
17109
- stepConfig.timeout = options.timeout;
17110
- const advanceCtx = makeAdvanceCtx();
17111
- const result = options.advance(advanceCtx);
17112
- const policy = {
17113
- defaultOutcome: result.default,
17114
- ...result.rules !== undefined ? { rules: result.rules } : {}
17115
- };
17116
- stepConfig.advancement = policy;
17117
- nodes.push(stepConfig);
17118
- return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
17119
- }
17120
-
17121
- class PipelineStepBuilder {
17122
- inputSchema;
17123
- meta;
17124
- nodes;
17125
- pipelineInputBindings;
17126
- pipelineStepInputBindings;
17127
- constructor(inputSchema, meta3, nodes, pipelineInputBindings = {}, pipelineStepInputBindings = {}) {
17128
- this.inputSchema = inputSchema;
17129
- this.meta = meta3;
17130
- this.nodes = nodes;
17131
- this.pipelineInputBindings = pipelineInputBindings;
17132
- this.pipelineStepInputBindings = pipelineStepInputBindings;
17133
- }
17134
- step(step, options) {
17135
- return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
17136
- }
17137
- fanOutStep(step, config2) {
17138
- return beginFanOut(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, config2);
17139
- }
17140
- build() {
17141
- const config2 = {
17142
- key: this.meta.key,
17143
- name: this.meta.name,
17144
- description: this.meta.description,
17145
- version: this.meta.version,
17146
- status: this.meta.status,
17147
- input: this.inputSchema,
17148
- nodes: this.nodes,
17149
- pipelineInputBindings: this.pipelineInputBindings
16860
+ const gateKey = `${stateKey}__cohortGate`;
16861
+ if (ctx.stateKeys.has(gateKey)) {
16862
+ throw new Error(`Pipeline "${ctx.pipelineKey}": synthesized cohortGate key "${gateKey}" collides with an author-declared state \u2014 rename state "${stateKey}"`);
16863
+ }
16864
+ assertTargetExists(ctx, stateKey, state.next);
16865
+ return {
16866
+ nodeDefinitions: [
16867
+ {
16868
+ nodeKey: stateKey,
16869
+ kind: "fanOut",
16870
+ stepKey: state.step.key,
16871
+ stepName: state.step.name,
16872
+ stepDescription: state.step.description,
16873
+ inputBindingsJson,
16874
+ timeoutSeconds: state.timeout ?? null,
16875
+ overSignalKey: overSignalKeyOf(state.over),
16876
+ advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(advanceEachPolicy),
16877
+ maxConcurrency: state.maxConcurrency ?? null
16878
+ },
16879
+ {
16880
+ nodeKey: gateKey,
16881
+ kind: "cohortGate",
16882
+ advanceAllPolicyDefinition: serializeCohortAdvancementPolicy(advanceAllPolicy),
16883
+ stepSignalsListDefinitions: extractInlineStepSignalsListDefinitions(advanceAllPolicy)
16884
+ }
16885
+ ],
16886
+ edges: [
16887
+ { fromNodeKey: stateKey, toNodeKey: gateKey },
16888
+ { fromNodeKey: gateKey, toNodeKey: state.next }
16889
+ ]
16890
+ };
16891
+ }
16892
+ function compileParallelState(stateKey, state, ctx) {
16893
+ const branchEntries = Object.entries(state.branches);
16894
+ if (branchEntries.length === 0) {
16895
+ throw new Error(`Pipeline "${ctx.pipelineKey}": parallel state "${stateKey}" requires at least one branch`);
16896
+ }
16897
+ const branches = {};
16898
+ for (const [branchKey, branchConfig] of branchEntries) {
16899
+ ctx.registerStep(branchConfig.step);
16900
+ const inputCtx = makeNodeInputCtx();
16901
+ const rawInput = branchConfig.input ? branchConfig.input(inputCtx) : {};
16902
+ branches[branchKey] = {
16903
+ stepKey: branchConfig.step.key,
16904
+ stepName: branchConfig.step.name,
16905
+ stepDescription: branchConfig.step.description,
16906
+ inputBindingsJson: serializeInputBindings(rawInput)
17150
16907
  };
17151
- return buildPipelineSpec(config2);
17152
16908
  }
16909
+ let advanceAllPolicyDefinition;
16910
+ if (state.advanceAll) {
16911
+ const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
16912
+ const advanceAllPolicy = {
16913
+ default: advanceAllResult.default,
16914
+ ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
16915
+ };
16916
+ advanceAllPolicyDefinition = serializeCohortAdvancementPolicy(advanceAllPolicy);
16917
+ }
16918
+ assertTargetExists(ctx, stateKey, state.next);
16919
+ return {
16920
+ nodeDefinitions: [
16921
+ {
16922
+ nodeKey: stateKey,
16923
+ kind: "parallel",
16924
+ branches,
16925
+ ...advanceAllPolicyDefinition ? { advanceAllPolicyDefinition } : {}
16926
+ }
16927
+ ],
16928
+ edges: [{ fromNodeKey: stateKey, toNodeKey: state.next }]
16929
+ };
17153
16930
  }
17154
-
17155
- class PipelineBuilder {
17156
- inputSchema;
17157
- meta;
17158
- nodes = [];
17159
- pipelineInputBindings;
17160
- pipelineStepInputBindings;
17161
- constructor(meta3) {
17162
- const { additionalPipelineInput, additionalStepInput, ...rest } = meta3;
17163
- this.inputSchema = additionalPipelineInput?.schema ?? exports_external.unknown();
17164
- this.meta = rest;
17165
- if (additionalPipelineInput) {
17166
- const raw = additionalPipelineInput.bindings({
17167
- workItem: WORK_ITEM_ACCESSOR,
17168
- literal: literal2
17169
- });
17170
- if (additionalPipelineInput.schema instanceof exports_external.ZodObject) {
17171
- const validKeys = new Set(Object.keys(additionalPipelineInput.schema.shape));
17172
- const unknown2 = Object.keys(raw).filter((k) => !validKeys.has(k));
17173
- if (unknown2.length > 0) {
17174
- throw new Error(`additionalPipelineInput.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((k) => `"${k}"`).join(", ")}`);
17175
- }
16931
+ function compileLoopState(stateKey, state, ctx) {
16932
+ ctx.registerStep(state.step);
16933
+ const inputCtx = makeNodeInputCtx();
16934
+ const rawInput = state.input ? state.input(inputCtx) : {};
16935
+ assertTargetExists(ctx, stateKey, state.next);
16936
+ assertTargetExists(ctx, stateKey, state.onExhausted);
16937
+ return {
16938
+ nodeDefinitions: [
16939
+ {
16940
+ nodeKey: stateKey,
16941
+ kind: "loop",
16942
+ stepKey: state.step.key,
16943
+ stepName: state.step.name,
16944
+ stepDescription: state.step.description,
16945
+ inputBindingsJson: serializeInputBindings(rawInput),
16946
+ timeoutSeconds: state.timeout ?? null,
16947
+ maxIterations: state.maxIterations,
16948
+ untilConditionJson: serializeCondition(state.until)
17176
16949
  }
17177
- this.pipelineInputBindings = normalizeInputMapping(raw) ?? {};
17178
- } else {
17179
- this.pipelineInputBindings = {};
17180
- }
17181
- this.pipelineStepInputBindings = resolveAdditionalStepInputBindings("additionalStepInput", additionalStepInput);
16950
+ ],
16951
+ edges: [
16952
+ { fromNodeKey: stateKey, toNodeKey: state.next, discriminantJson: { loopExit: "next" } },
16953
+ {
16954
+ fromNodeKey: stateKey,
16955
+ toNodeKey: state.onExhausted,
16956
+ discriminantJson: { loopExit: "onExhausted" }
16957
+ }
16958
+ ]
16959
+ };
16960
+ }
16961
+ function compileTerminalState(stateKey, kind) {
16962
+ return { nodeDefinitions: [{ nodeKey: stateKey, kind }], edges: [] };
16963
+ }
16964
+ function assertNoIllegalConvergentEdges(pipelineKey, nodeKindByKey, edges) {
16965
+ const incoming = new Map;
16966
+ for (const edge of edges) {
16967
+ const list = incoming.get(edge.toNodeKey) ?? [];
16968
+ list.push(edge);
16969
+ incoming.set(edge.toNodeKey, list);
17182
16970
  }
17183
- step(step, options) {
17184
- return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
16971
+ for (const [targetKey, incomingEdges] of incoming) {
16972
+ if (incomingEdges.length <= 1)
16973
+ continue;
16974
+ const hasInvalidSource = incomingEdges.some((edge) => {
16975
+ const kind = nodeKindByKey.get(edge.fromNodeKey);
16976
+ return kind !== "choice" && kind !== "loop";
16977
+ });
16978
+ if (hasInvalidSource) {
16979
+ throw new Error(`Pipeline "${pipelineKey}": state "${targetKey}" has more than one incoming edge, but not every source is a 'choice'/'loop' state (unconditional convergent edges are not allowed \u2014 see docs/research/flat-pipeline-sdk-and-visual-designer.md \xA76).`);
16980
+ }
17185
16981
  }
17186
16982
  }
17187
- function pipeline(meta3) {
17188
- return new PipelineBuilder(meta3);
16983
+
16984
+ // src/definitions/pipelines/define-pipeline.ts
16985
+ function isWorkingNodeDefinition(node) {
16986
+ return node.kind === "step" || node.kind === "fanOut" || node.kind === "loop";
16987
+ }
16988
+ function definePipeline(config2) {
16989
+ const stateEntries = Object.entries(config2.states);
16990
+ if (stateEntries.length === 0) {
16991
+ throw new Error(`Pipeline "${config2.key}" must declare at least one state`);
16992
+ }
16993
+ const startState = config2.states[config2.startAt];
16994
+ if (!startState) {
16995
+ throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" does not name a declared state`);
16996
+ }
16997
+ if (startState.kind === "choice" || startState.kind === "succeed" || startState.kind === "fail") {
16998
+ throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" names a '${startState.kind}' state, which cannot be an entry point`);
16999
+ }
17000
+ const stateKeys = new Set(Object.keys(config2.states));
17001
+ const stepDefMap = new Map;
17002
+ const registerStep = (step) => {
17003
+ const mapKey = `${step.key}@v${String(step.version)}`;
17004
+ if (!stepDefMap.has(mapKey))
17005
+ stepDefMap.set(mapKey, step);
17006
+ };
17007
+ const compileContext = {
17008
+ pipelineKey: config2.key,
17009
+ stateKeys,
17010
+ registerStep
17011
+ };
17012
+ const nodeDefinitions = [];
17013
+ const dependencyEdges = [];
17014
+ for (const [stateKey, state] of stateEntries) {
17015
+ const compiled = state.kind === "step" ? compileStepState(stateKey, state, compileContext) : state.kind === "choice" ? compileChoiceState(stateKey, state, compileContext) : state.kind === "fanOut" ? compileFanOutState(stateKey, state, compileContext) : state.kind === "parallel" ? compileParallelState(stateKey, state, compileContext) : state.kind === "loop" ? compileLoopState(stateKey, state, compileContext) : compileTerminalState(stateKey, state.kind);
17016
+ nodeDefinitions.push(...compiled.nodeDefinitions);
17017
+ dependencyEdges.push(...compiled.edges);
17018
+ }
17019
+ const nodeKindByKey = new Map(nodeDefinitions.map((node) => [node.nodeKey, node.kind]));
17020
+ assertNoIllegalConvergentEdges(config2.key, nodeKindByKey, dependencyEdges);
17021
+ let inputSchemaJson = null;
17022
+ if (config2.input) {
17023
+ try {
17024
+ inputSchemaJson = exports_external.toJSONSchema(config2.input);
17025
+ } catch {
17026
+ inputSchemaJson = null;
17027
+ }
17028
+ }
17029
+ return {
17030
+ key: config2.key,
17031
+ name: config2.name ?? config2.key,
17032
+ description: config2.description ?? null,
17033
+ version: config2.version ?? 1,
17034
+ status: config2.status ?? "active",
17035
+ inputSchemaJson,
17036
+ _stepDefinitions: [...stepDefMap.values()],
17037
+ nodeDefinitions,
17038
+ dependencyEdges
17039
+ };
17189
17040
  }
17190
17041
  // src/definitions/pipelines/pipeline-definitions-client.ts
17191
17042
  function createPipelineDefinitionsClient(baseUrl) {
17192
17043
  const client2 = createClient({ baseUrl });
17193
17044
  return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
17194
17045
  }
17046
+ function resolveStepRef(pipelineKey, nodeKey, stepKey, stepDefMap) {
17047
+ const stepDef = stepDefMap.get(stepKey);
17048
+ if (!stepDef) {
17049
+ throw new Error(`Step "${stepKey}" referenced by node "${nodeKey}" in pipeline "${pipelineKey}" was not found on ` + `the server. Run \`boboddy steps push\` first to push your step definitions.`);
17050
+ }
17051
+ return {
17052
+ stepDefinitionId: stepDef.id,
17053
+ stepDefinitionVersion: stepDef.version
17054
+ };
17055
+ }
17056
+ function buildConfigJson(pipelineKey, node, stepDefMap) {
17057
+ if (node.kind === "choice") {
17058
+ return { choices: node.choices, default: node.default };
17059
+ }
17060
+ if (node.kind === "loop") {
17061
+ return {
17062
+ maxIterations: node.maxIterations,
17063
+ untilConditionJson: node.untilConditionJson
17064
+ };
17065
+ }
17066
+ if (node.kind === "parallel") {
17067
+ const branches = {};
17068
+ for (const [branchKey, branch] of Object.entries(node.branches)) {
17069
+ branches[branchKey] = {
17070
+ ...resolveStepRef(pipelineKey, node.nodeKey, branch.stepKey, stepDefMap),
17071
+ inputBindingsJson: branch.inputBindingsJson
17072
+ };
17073
+ }
17074
+ return {
17075
+ branches,
17076
+ ...node.advanceAllPolicyDefinition ? { advanceAllPolicyDefinition: node.advanceAllPolicyDefinition } : {}
17077
+ };
17078
+ }
17079
+ if (node.kind === "fanOut") {
17080
+ return {
17081
+ overSignalKey: node.overSignalKey,
17082
+ advanceEachPolicyDefinition: node.advanceEachPolicyDefinition,
17083
+ maxConcurrency: node.maxConcurrency
17084
+ };
17085
+ }
17086
+ if (node.kind === "cohortGate") {
17087
+ return {
17088
+ advanceAllPolicyDefinition: node.advanceAllPolicyDefinition,
17089
+ stepSignalsListDefinitions: node.stepSignalsListDefinitions
17090
+ };
17091
+ }
17092
+ return null;
17093
+ }
17094
+ function buildGraphNodeInput(pipelineKey, node, stepDefMap) {
17095
+ const working = isWorkingNodeDefinition(node) ? node : null;
17096
+ const stepRef = working ? resolveStepRef(pipelineKey, node.nodeKey, working.stepKey, stepDefMap) : null;
17097
+ const policy = node.kind === "step" ? node.advancementPolicyDefinition : undefined;
17098
+ return {
17099
+ key: node.nodeKey,
17100
+ kind: node.kind,
17101
+ name: working?.stepName ?? node.nodeKey,
17102
+ ...stepRef ?? {},
17103
+ description: working?.stepDescription ?? null,
17104
+ inputBindingsJson: working ? working.inputBindingsJson : null,
17105
+ timeoutSeconds: working?.timeoutSeconds ?? null,
17106
+ ...policy ? {
17107
+ advancementPolicyRulesJson: policy.rulesJson,
17108
+ advancementPolicyDefaultEventType: policy.defaultEventType,
17109
+ advancementPolicyDefaultEventParamsJson: policy.defaultEventParamsJson,
17110
+ advancementPolicyAllowedEventTypes: policy.allowedEventTypes
17111
+ } : {},
17112
+ configJson: buildConfigJson(pipelineKey, node, stepDefMap),
17113
+ computedSignalDefinitions: node.kind === "step" ? node.computedSignalDefinitions : []
17114
+ };
17115
+ }
17195
17116
  var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17196
17117
  return {
17197
17118
  listByProjectId: async (projectId, options) => {
@@ -17211,37 +17132,12 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17211
17132
  stepDefMap.set(s.key, s);
17212
17133
  }
17213
17134
  }
17214
- const nonStepNode = spec.nodeDefinitions.find((node) => node.kind !== "step");
17215
- if (nonStepNode) {
17216
- throw new Error(`Pipeline "${spec.key}" contains a "${nonStepNode.kind}" node ("${nonStepNode.nodeKey}"), ` + `but fan-out pipelines cannot be pushed via the current wire contract yet. ` + `Only "step"-only (chain) pipelines can be pushed with \`upsertFromSpec\` today.`);
17217
- }
17218
- const ordered = tryOrderChainNodeDefinitions(spec.nodeDefinitions, spec.dependencyEdges);
17219
- if (ordered === null) {
17220
- throw new Error(`Pipeline "${spec.key}" has a malformed step graph: its node and ` + `dependency-edge definitions do not form a single connected, ` + `acyclic chain. This should not happen from the \`pipeline()\` ` + `builder \u2014 check for hand-edited or generated pipeline specs.`);
17221
- }
17222
- const stepDefinitions = ordered.map((node, index) => {
17223
- const stepKey = node.stepKey;
17224
- if (!stepKey) {
17225
- throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
17226
- }
17227
- const stepDef = stepDefMap.get(stepKey);
17228
- if (!stepDef) {
17229
- throw new Error(`Step "${stepKey}" referenced in pipeline "${spec.key}" was not found on the server. ` + `Run \`boboddy steps push\` first to push your step definitions.`);
17230
- }
17231
- return {
17232
- stepDefinitionId: stepDef.id,
17233
- stepDefinitionVersion: stepDef.version,
17234
- key: node.nodeKey,
17235
- name: node.stepName ?? "",
17236
- description: node.stepDescription ?? null,
17237
- position: index + 1,
17238
- inputBindingsJson: node.inputBindingsJson ?? {},
17239
- timeoutSeconds: node.timeoutSeconds ?? null,
17240
- retryPolicyJson: null,
17241
- advancementPolicyDefinition: node.advancementPolicyDefinition,
17242
- computedSignalDefinitions: node.computedSignalDefinitions ?? []
17243
- };
17244
- });
17135
+ const nodeDefinitions = spec.nodeDefinitions.map((node) => buildGraphNodeInput(spec.key, node, stepDefMap));
17136
+ const dependencyEdges = spec.dependencyEdges.map((edge) => ({
17137
+ fromNodeKey: edge.fromNodeKey,
17138
+ toNodeKey: edge.toNodeKey,
17139
+ discriminantJson: edge.discriminantJson ?? null
17140
+ }));
17245
17141
  const body = {
17246
17142
  projectId,
17247
17143
  key: spec.key,
@@ -17249,7 +17145,8 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17249
17145
  description: spec.description,
17250
17146
  status: spec.status,
17251
17147
  inputSchemaJson: spec.inputSchemaJson,
17252
- stepDefinitions
17148
+ nodeDefinitions,
17149
+ dependencyEdges
17253
17150
  };
17254
17151
  const result = await pipelineDefinitions.upsertPipelineDefinition({
17255
17152
  body,
@@ -17307,17 +17204,17 @@ function buildWorkItemAccessor() {
17307
17204
  }
17308
17205
  return accessor;
17309
17206
  }
17310
- function makeAssign(pipeline2) {
17311
- if (typeof pipeline2 !== "object" || typeof pipeline2["key"] !== "string" || !Array.isArray(pipeline2["nodeDefinitions"])) {
17312
- throw new Error("assign() requires a pipeline spec produced by pipeline().build(). " + "Pass the default-exported value from a pipeline definition file.");
17207
+ function makeAssign(pipeline) {
17208
+ if (typeof pipeline !== "object" || typeof pipeline["key"] !== "string" || !Array.isArray(pipeline["nodeDefinitions"])) {
17209
+ throw new Error("assign() requires a pipeline spec produced by definePipeline(). " + "Pass the default-exported value from a pipeline definition file.");
17313
17210
  }
17314
- return { _tag: "assign", pipeline: pipeline2 };
17211
+ return { _tag: "assign", pipeline };
17315
17212
  }
17316
- function extractCondition2(ref) {
17213
+ function extractCondition(ref) {
17317
17214
  return ref._condition;
17318
17215
  }
17319
17216
  function makeGroup(mode, refs) {
17320
- const conditions = refs.map(extractCondition2);
17217
+ const conditions = refs.map(extractCondition);
17321
17218
  const condition = { _tag: "group", mode, conditions };
17322
17219
  return {
17323
17220
  _condition: condition,
@@ -17720,10 +17617,10 @@ async function parseBoboddyConfig(workspacePath) {
17720
17617
  export {
17721
17618
  stripTrailingCommas,
17722
17619
  stripJsoncComments,
17620
+ serializeInputBindings,
17723
17621
  serializeDefaultPipelineAssignment,
17724
17622
  resolveWorkItemFieldPath,
17725
17623
  renderPromptTemplate,
17726
- pipeline,
17727
17624
  parseJsonc,
17728
17625
  parseBoboddyConfig,
17729
17626
  openCodeMcpServersSchema,
@@ -17732,20 +17629,20 @@ export {
17732
17629
  openCodeMcpOAuthConfigSchema,
17733
17630
  openCodeMcpLocalConfigSchema,
17734
17631
  openCodeMcpEnabledOverrideSchema,
17735
- materializeAccessor,
17736
- literal2 as literal,
17737
- isInputAccessor,
17632
+ makeNodeInputCtx,
17633
+ makeFanOutNodeInputCtx,
17634
+ isWorkingNodeDefinition,
17738
17635
  isDefaultPipelineAssignmentSpec,
17739
17636
  defineStep,
17637
+ definePipeline,
17740
17638
  defaultPipelineAssignment,
17741
17639
  createStepExecutionPlaneClient,
17742
17640
  createStepDefinitionsClient,
17743
17641
  createPromptTemplateContext,
17744
17642
  createPromptInputProxy,
17745
17643
  createPipelineDefinitionsClient,
17746
- createInputAccessor,
17747
17644
  createBoboddyClient,
17748
- buildPipelineSpec,
17645
+ codeStep,
17749
17646
  artifactKindSchema,
17750
17647
  WorkItems,
17751
17648
  WorkItemComments,
@@ -17762,10 +17659,8 @@ export {
17762
17659
  ProjectInvites,
17763
17660
  ProjectIntegrations,
17764
17661
  ProjectContext,
17765
- PipelineStepBuilder,
17766
17662
  PipelineExecutions,
17767
17663
  PipelineDefinitions,
17768
- PipelineBuilder,
17769
17664
  NotificationRules,
17770
17665
  GitHubIntegrations,
17771
17666
  Features,