@boboddy/sdk 0.4.2 → 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 (34) 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 +36 -94
  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-default-pipeline-assignment.d.ts +64 -14
  9. package/dist/definitions/pipelines/define-pipeline.d.ts +101 -154
  10. package/dist/definitions/pipelines/index.d.ts +1 -2
  11. package/dist/definitions/pipelines/index.js +485 -585
  12. package/dist/definitions/pipelines/node-input-ctx.d.ts +31 -0
  13. package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +42 -12
  14. package/dist/definitions/pipelines/pipeline-states.d.ts +104 -0
  15. package/dist/definitions/pipelines/work-item-fields.d.ts +98 -0
  16. package/dist/definitions/steps/define-code-step.d.ts +50 -0
  17. package/dist/definitions/steps/define-step.d.ts +40 -8
  18. package/dist/definitions/steps/index.d.ts +1 -0
  19. package/dist/definitions/steps/index.js +41 -24
  20. package/dist/definitions/steps/step-definitions-client.d.ts +47 -11
  21. package/dist/definitions/validation/index.js +15148 -94
  22. package/dist/definitions/validation/validate-definition-specs.d.ts +19 -1
  23. package/dist/generated/index.d.ts +1 -1
  24. package/dist/generated/sdk.gen.d.ts +1 -4
  25. package/dist/generated/types.gen.d.ts +469 -875
  26. package/dist/index.js +525 -585
  27. package/dist/push/collect-definitions.d.ts +40 -1
  28. package/dist/push/index.d.ts +2 -2
  29. package/dist/push/index.js +756 -664
  30. package/dist/step-execution-plane-client.d.ts +9 -4
  31. package/package.json +2 -2
  32. package/dist/definitions/pipelines/builder.d.ts +0 -80
  33. package/dist/definitions/pipelines/fan-out-builder.d.ts +0 -66
  34. 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,402 +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/builder-helpers.ts
16786
- var WORK_ITEM_ACCESSOR = Object.freeze({
16787
- title: Object.freeze({ source: "work_item", field: "title" }),
16788
- description: Object.freeze({
16789
- source: "work_item",
16790
- field: "description"
16791
- }),
16792
- field: (fieldName) => Object.freeze({ source: "work_item", field: `fields.${fieldName}` })
16793
- });
16794
- var WORK_ITEM_FIELD_BINDINGS = {
16795
- workItemTitle: { source: "work_item", field: "title" },
16796
- workItemDescription: { source: "work_item", field: "description" }
16797
- };
16798
- function makeStepInputCtx(inputSchema) {
16799
- const baseAccessor = createInputAccessor(inputSchema);
16800
- const input = new Proxy(baseAccessor, {
16801
- get(target, prop) {
16802
- if (typeof prop === "string" && prop in WORK_ITEM_FIELD_BINDINGS) {
16803
- return WORK_ITEM_FIELD_BINDINGS[prop];
16804
- }
16805
- return target[prop];
16806
- }
16807
- });
16808
- return {
16809
- input,
16810
- signal(step, key) {
16811
- return { source: "step_signal", step, signalKey: key };
16812
- },
16813
- output(step) {
16814
- return { source: "step_output", step };
16815
- },
16816
- literal: literal2,
16817
- signalsList(fanOutStep) {
16818
- return { source: "signals_list", fanOutStep };
16819
- }
16820
- };
16821
- }
16822
- function literal2(value) {
16823
- return { source: "literal", value };
16824
- }
16825
- function normalizeInputMapping(mapping) {
16826
- if (!mapping)
16827
- return;
16828
- const out = {};
16829
- for (const [key, value] of Object.entries(mapping)) {
16830
- if (value === undefined)
16831
- continue;
16832
- out[key] = isInputAccessor(value) ? materializeAccessor(value) : value;
16833
- }
16834
- return out;
16835
- }
16836
- function resolveAdditionalStepInputBindings(label, definition) {
16837
- if (!definition) {
16838
- return {};
16839
- }
16840
- const raw = definition.bindings({
16841
- workItemField: (fieldName) => ({
16842
- source: "work_item",
16843
- field: `fields.${fieldName}`
16844
- }),
16845
- literal: literal2
16846
- });
16847
- if (definition.schema instanceof exports_external.ZodObject) {
16848
- const validKeys = new Set(Object.keys(definition.schema.shape));
16849
- const unknown2 = Object.keys(raw).filter((key) => !validKeys.has(key));
16850
- if (unknown2.length > 0) {
16851
- throw new Error(`${label}.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((key) => `"${key}"`).join(", ")}`);
16852
- }
16853
- }
16854
- return normalizeInputMapping(raw) ?? {};
16855
- }
16856
- function mergeStepBindings(pipelineBindings, explicitBindings) {
16857
- const merged = {
16858
- ...pipelineBindings,
16859
- ...explicitBindings ?? {}
16860
- };
16861
- return Object.keys(merged).length > 0 ? merged : undefined;
16862
- }
16863
-
16864
16496
  // src/definitions/advancement-policies/cohort-fluent-rules.ts
16865
- var LEAF_BRAND2 = Symbol("boboddy.cohortRule.leaf");
16866
- var GROUP_BRAND2 = Symbol("boboddy.cohortRule.group");
16497
+ var LEAF_BRAND = Symbol("boboddy.cohortRule.leaf");
16498
+ var GROUP_BRAND = Symbol("boboddy.cohortRule.group");
16867
16499
  function createCohortSignalRef(signal2) {
16868
16500
  const leaf = (operator, value) => {
16869
16501
  const condition = {
@@ -16873,7 +16505,7 @@ function createCohortSignalRef(signal2) {
16873
16505
  value
16874
16506
  };
16875
16507
  return {
16876
- [LEAF_BRAND2]: condition,
16508
+ [LEAF_BRAND]: condition,
16877
16509
  then(outcome, paramsJson) {
16878
16510
  return {
16879
16511
  _tag: "rule",
@@ -16900,7 +16532,7 @@ function createCohortSignalRef(signal2) {
16900
16532
  }
16901
16533
  function createCohortLeafFromCondition(condition) {
16902
16534
  return {
16903
- [LEAF_BRAND2]: condition,
16535
+ [LEAF_BRAND]: condition,
16904
16536
  then(outcome, paramsJson) {
16905
16537
  return {
16906
16538
  _tag: "rule",
@@ -16913,15 +16545,15 @@ function createCohortLeafFromCondition(condition) {
16913
16545
  };
16914
16546
  }
16915
16547
  function extractCohortCondition(ref) {
16916
- if (LEAF_BRAND2 in ref)
16917
- return ref[LEAF_BRAND2];
16918
- const group = ref[GROUP_BRAND2];
16548
+ if (LEAF_BRAND in ref)
16549
+ return ref[LEAF_BRAND];
16550
+ const group = ref[GROUP_BRAND];
16919
16551
  return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
16920
16552
  }
16921
16553
  function createCohortGroup(mode, refs) {
16922
16554
  const conditions = refs.map(extractCohortCondition);
16923
16555
  return {
16924
- [GROUP_BRAND2]: { mode, conditions },
16556
+ [GROUP_BRAND]: { mode, conditions },
16925
16557
  then(outcome, paramsJson) {
16926
16558
  return {
16927
16559
  _tag: "rule",
@@ -17026,138 +16658,461 @@ function makeAdvanceAllCtx() {
17026
16658
  };
17027
16659
  }
17028
16660
 
17029
- // src/definitions/pipelines/fan-out-builder.ts
17030
- function beginFanOut(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, config2) {
17031
- const baseCtx = makeStepInputCtx(inputSchema);
17032
- const ctx = {
17033
- ...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(),
17034
16764
  item: { source: "fan_out_item" }
17035
16765
  };
17036
- const rawInput = config2.input ? config2.input(ctx) : {};
17037
- const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
17038
- const fanOutNodeConfig = {
17039
- nodeType: "fanOut",
17040
- fanOutStep: step,
17041
- overSignalKey: config2.over,
17042
- ...input ? { input } : {},
17043
- ...config2.timeout !== undefined ? { timeout: config2.timeout } : {}
17044
- };
17045
- const advanceEachCtx = makeAdvanceEachCtx();
17046
- 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());
17047
16851
  const advanceEachPolicy = {
17048
16852
  default: advanceEachResult.default,
17049
16853
  ...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
17050
16854
  };
17051
- fanOutNodeConfig.advanceEach = advanceEachPolicy;
17052
- const cohortGateNodeConfig = {
17053
- nodeType: "cohortGate",
17054
- nodeKey: `${step.key}__cohortGate`
17055
- };
17056
- const advanceAllCtx = makeAdvanceAllCtx();
17057
- const advanceAllResult = config2.advanceAll(advanceAllCtx);
16855
+ const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
17058
16856
  const advanceAllPolicy = {
17059
16857
  default: advanceAllResult.default,
17060
16858
  ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
17061
16859
  };
17062
- cohortGateNodeConfig.advanceAll = advanceAllPolicy;
17063
- nodes.push(fanOutNodeConfig, cohortGateNodeConfig);
17064
- return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
17065
- }
17066
-
17067
- // src/definitions/pipelines/builder.ts
17068
- function pushStep(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, rawOptions) {
17069
- const options = rawOptions;
17070
- const ctx = makeStepInputCtx(inputSchema);
17071
- const rawInput = options.input ? options.input(ctx) : {};
17072
- const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
17073
- const stepConfig = { step, input };
17074
- if (options.timeout !== undefined)
17075
- stepConfig.timeout = options.timeout;
17076
- const advanceCtx = makeAdvanceCtx();
17077
- const result = options.advance(advanceCtx);
17078
- const policy = {
17079
- defaultOutcome: result.default,
17080
- ...result.rules !== undefined ? { rules: result.rules } : {}
17081
- };
17082
- stepConfig.advancement = policy;
17083
- nodes.push(stepConfig);
17084
- return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
17085
- }
17086
-
17087
- class PipelineStepBuilder {
17088
- inputSchema;
17089
- meta;
17090
- nodes;
17091
- pipelineInputBindings;
17092
- pipelineStepInputBindings;
17093
- constructor(inputSchema, meta3, nodes, pipelineInputBindings = {}, pipelineStepInputBindings = {}) {
17094
- this.inputSchema = inputSchema;
17095
- this.meta = meta3;
17096
- this.nodes = nodes;
17097
- this.pipelineInputBindings = pipelineInputBindings;
17098
- this.pipelineStepInputBindings = pipelineStepInputBindings;
17099
- }
17100
- step(step, options) {
17101
- return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
17102
- }
17103
- fanOutStep(step, config2) {
17104
- return beginFanOut(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, config2);
17105
- }
17106
- build() {
17107
- const config2 = {
17108
- key: this.meta.key,
17109
- name: this.meta.name,
17110
- description: this.meta.description,
17111
- version: this.meta.version,
17112
- status: this.meta.status,
17113
- input: this.inputSchema,
17114
- nodes: this.nodes,
17115
- 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)
17116
16907
  };
17117
- return buildPipelineSpec(config2);
17118
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
+ };
17119
16930
  }
17120
-
17121
- class PipelineBuilder {
17122
- inputSchema;
17123
- meta;
17124
- nodes = [];
17125
- pipelineInputBindings;
17126
- pipelineStepInputBindings;
17127
- constructor(meta3) {
17128
- const { additionalPipelineInput, additionalStepInput, ...rest } = meta3;
17129
- this.inputSchema = additionalPipelineInput?.schema ?? exports_external.unknown();
17130
- this.meta = rest;
17131
- if (additionalPipelineInput) {
17132
- const raw = additionalPipelineInput.bindings({
17133
- workItem: WORK_ITEM_ACCESSOR,
17134
- literal: literal2
17135
- });
17136
- if (additionalPipelineInput.schema instanceof exports_external.ZodObject) {
17137
- const validKeys = new Set(Object.keys(additionalPipelineInput.schema.shape));
17138
- const unknown2 = Object.keys(raw).filter((k) => !validKeys.has(k));
17139
- if (unknown2.length > 0) {
17140
- throw new Error(`additionalPipelineInput.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((k) => `"${k}"`).join(", ")}`);
17141
- }
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)
17142
16949
  }
17143
- this.pipelineInputBindings = normalizeInputMapping(raw) ?? {};
17144
- } else {
17145
- this.pipelineInputBindings = {};
17146
- }
17147
- 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);
17148
16970
  }
17149
- step(step, options) {
17150
- 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
+ }
17151
16981
  }
17152
16982
  }
17153
- function pipeline(meta3) {
17154
- 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
+ };
17155
17040
  }
17156
17041
  // src/definitions/pipelines/pipeline-definitions-client.ts
17157
17042
  function createPipelineDefinitionsClient(baseUrl) {
17158
17043
  const client2 = createClient({ baseUrl });
17159
17044
  return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
17160
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
+ }
17161
17116
  var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17162
17117
  return {
17163
17118
  listByProjectId: async (projectId, options) => {
@@ -17177,37 +17132,12 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17177
17132
  stepDefMap.set(s.key, s);
17178
17133
  }
17179
17134
  }
17180
- const nonStepNode = spec.nodeDefinitions.find((node) => node.kind !== "step");
17181
- if (nonStepNode) {
17182
- 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.`);
17183
- }
17184
- const ordered = tryOrderChainNodeDefinitions(spec.nodeDefinitions, spec.dependencyEdges);
17185
- if (ordered === null) {
17186
- 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.`);
17187
- }
17188
- const stepDefinitions = ordered.map((node, index) => {
17189
- const stepKey = node.stepKey;
17190
- if (!stepKey) {
17191
- throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
17192
- }
17193
- const stepDef = stepDefMap.get(stepKey);
17194
- if (!stepDef) {
17195
- 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.`);
17196
- }
17197
- return {
17198
- stepDefinitionId: stepDef.id,
17199
- stepDefinitionVersion: stepDef.version,
17200
- key: node.nodeKey,
17201
- name: node.stepName ?? "",
17202
- description: node.stepDescription ?? null,
17203
- position: index + 1,
17204
- inputBindingsJson: node.inputBindingsJson ?? {},
17205
- timeoutSeconds: node.timeoutSeconds ?? null,
17206
- retryPolicyJson: null,
17207
- advancementPolicyDefinition: node.advancementPolicyDefinition,
17208
- computedSignalDefinitions: node.computedSignalDefinitions ?? []
17209
- };
17210
- });
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
+ }));
17211
17141
  const body = {
17212
17142
  projectId,
17213
17143
  key: spec.key,
@@ -17215,7 +17145,8 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17215
17145
  description: spec.description,
17216
17146
  status: spec.status,
17217
17147
  inputSchemaJson: spec.inputSchemaJson,
17218
- stepDefinitions
17148
+ nodeDefinitions,
17149
+ dependencyEdges
17219
17150
  };
17220
17151
  const result = await pipelineDefinitions.upsertPipelineDefinition({
17221
17152
  body,
@@ -17250,7 +17181,7 @@ function makeLeaf(fact, path, operator, value) {
17250
17181
  };
17251
17182
  }
17252
17183
  function makeFieldRef(fact, path) {
17253
- return {
17184
+ const untyped = {
17254
17185
  eq: (v) => makeLeaf(fact, path, "equal", v),
17255
17186
  ne: (v) => makeLeaf(fact, path, "notEqual", v),
17256
17187
  gt: (v) => makeLeaf(fact, path, "greaterThan", v),
@@ -17262,18 +17193,28 @@ function makeFieldRef(fact, path) {
17262
17193
  contains: (v) => makeLeaf(fact, path, "contains", v),
17263
17194
  doesNotContain: (v) => makeLeaf(fact, path, "doesNotContain", v)
17264
17195
  };
17196
+ return untyped;
17265
17197
  }
17266
- function makeAssign(pipeline2) {
17267
- if (typeof pipeline2 !== "object" || typeof pipeline2["key"] !== "string" || !Array.isArray(pipeline2["nodeDefinitions"])) {
17268
- throw new Error("assign() requires a pipeline spec produced by pipeline().build(). " + "Pass the default-exported value from a pipeline definition file.");
17198
+ function buildWorkItemAccessor() {
17199
+ const accessor = {
17200
+ field: (name) => makeFieldRef("workItem", `$.${WORK_ITEM_FIELDS_PATH_PREFIX}${name}`)
17201
+ };
17202
+ for (const key of WORK_ITEM_TOP_LEVEL_FIELDS) {
17203
+ accessor[key] = makeFieldRef("workItem", `$.${key}`);
17204
+ }
17205
+ return accessor;
17206
+ }
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.");
17269
17210
  }
17270
- return { _tag: "assign", pipeline: pipeline2 };
17211
+ return { _tag: "assign", pipeline };
17271
17212
  }
17272
- function extractCondition2(ref) {
17213
+ function extractCondition(ref) {
17273
17214
  return ref._condition;
17274
17215
  }
17275
17216
  function makeGroup(mode, refs) {
17276
- const conditions = refs.map(extractCondition2);
17217
+ const conditions = refs.map(extractCondition);
17277
17218
  const condition = { _tag: "group", mode, conditions };
17278
17219
  return {
17279
17220
  _condition: condition,
@@ -17284,9 +17225,7 @@ function makeGroup(mode, refs) {
17284
17225
  }
17285
17226
  function buildCtx() {
17286
17227
  return {
17287
- workItem: {
17288
- field: (name) => makeFieldRef("workItem", `$.fields.${name}`)
17289
- },
17228
+ workItem: buildWorkItemAccessor(),
17290
17229
  context: {
17291
17230
  isNew: makeFieldRef("context", "$.isNew")
17292
17231
  },
@@ -17678,9 +17617,10 @@ async function parseBoboddyConfig(workspacePath) {
17678
17617
  export {
17679
17618
  stripTrailingCommas,
17680
17619
  stripJsoncComments,
17620
+ serializeInputBindings,
17681
17621
  serializeDefaultPipelineAssignment,
17622
+ resolveWorkItemFieldPath,
17682
17623
  renderPromptTemplate,
17683
- pipeline,
17684
17624
  parseJsonc,
17685
17625
  parseBoboddyConfig,
17686
17626
  openCodeMcpServersSchema,
@@ -17689,24 +17629,26 @@ export {
17689
17629
  openCodeMcpOAuthConfigSchema,
17690
17630
  openCodeMcpLocalConfigSchema,
17691
17631
  openCodeMcpEnabledOverrideSchema,
17692
- materializeAccessor,
17693
- literal2 as literal,
17694
- isInputAccessor,
17632
+ makeNodeInputCtx,
17633
+ makeFanOutNodeInputCtx,
17634
+ isWorkingNodeDefinition,
17695
17635
  isDefaultPipelineAssignmentSpec,
17696
17636
  defineStep,
17637
+ definePipeline,
17697
17638
  defaultPipelineAssignment,
17698
17639
  createStepExecutionPlaneClient,
17699
17640
  createStepDefinitionsClient,
17700
17641
  createPromptTemplateContext,
17701
17642
  createPromptInputProxy,
17702
17643
  createPipelineDefinitionsClient,
17703
- createInputAccessor,
17704
17644
  createBoboddyClient,
17705
- buildPipelineSpec,
17645
+ codeStep,
17706
17646
  artifactKindSchema,
17707
17647
  WorkItems,
17708
17648
  WorkItemComments,
17709
17649
  WorkItemBlocks,
17650
+ WORK_ITEM_TOP_LEVEL_FIELDS,
17651
+ WORK_ITEM_FIELDS_PATH_PREFIX,
17710
17652
  Username,
17711
17653
  UserNotifications,
17712
17654
  StepExecutions,
@@ -17717,10 +17659,8 @@ export {
17717
17659
  ProjectInvites,
17718
17660
  ProjectIntegrations,
17719
17661
  ProjectContext,
17720
- PipelineStepBuilder,
17721
17662
  PipelineExecutions,
17722
17663
  PipelineDefinitions,
17723
- PipelineBuilder,
17724
17664
  NotificationRules,
17725
17665
  GitHubIntegrations,
17726
17666
  Features,