@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
@@ -0,0 +1,31 @@
1
+ import type { ZodType } from "zod";
2
+ import type { DotPaths } from "../steps/define-step";
3
+ import { type WorkItemAccessor } from "./builder-helpers";
4
+ import type { FanOutItemBinding, LiteralBinding, PipelineInputBinding, SignalsListBinding, StepOutputBinding, StepSignalBinding } from "./bindings";
5
+ /**
6
+ * The `input:` mapper ctx every `step`/`fanOut`/`loop`/parallel-branch
7
+ * state receives (see docs/research/flat-pipeline-sdk-and-visual-designer.md
8
+ * §4). Nodes are addressed by their plain string state key rather than a
9
+ * typed step reference — the flat, forward-only authoring model has no
10
+ * "tuple of prior steps" to statically check a binding's source/signal
11
+ * key against (cross-node static proof is explicitly out of scope; see
12
+ * that doc's §1).
13
+ */
14
+ export type NodeInputCtx<TInput extends ZodType = ZodType> = {
15
+ /** Reads a value from the pipeline's own input, by dot-path. */
16
+ pipelineInput: (path: DotPaths<TInput["_output"]>) => PipelineInputBinding;
17
+ workItem: WorkItemAccessor;
18
+ /** Reads a signal declared by an earlier node, addressed by its state key. */
19
+ signal: (nodeKey: string, signalKey: string) => StepSignalBinding;
20
+ /** Reads an earlier node's whole result output, addressed by its state key. */
21
+ output: (nodeKey: string) => StepOutputBinding;
22
+ /** Reaches a fan-out's whole cohort (every terminal branch's signals + output). */
23
+ signalsList: (nodeKey: string) => SignalsListBinding;
24
+ literal: (value: unknown) => LiteralBinding;
25
+ };
26
+ /** `NodeInputCtx`, plus `item` — the current fan-out branch's own item. */
27
+ export type FanOutNodeInputCtx<TInput extends ZodType = ZodType> = NodeInputCtx<TInput> & {
28
+ item: FanOutItemBinding;
29
+ };
30
+ export declare function makeNodeInputCtx<TInput extends ZodType>(): NodeInputCtx<TInput>;
31
+ export declare function makeFanOutNodeInputCtx<TInput extends ZodType>(): FanOutNodeInputCtx<TInput>;
@@ -1,6 +1,6 @@
1
1
  import { PipelineDefinitions } from "../../generated/sdk.gen";
2
2
  import type { PutApiPipelineDefinitionsData } from "../../generated/types.gen";
3
- import type { PipelineDefinitionSpec } from "./define-pipeline";
3
+ import { type PipelineDefinitionSpec } from "./define-pipeline";
4
4
  type RequestOptions = {
5
5
  headers?: Record<string, unknown> | undefined;
6
6
  };
@@ -20,10 +20,13 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
20
20
  description: string | unknown;
21
21
  status: "draft" | "active" | "archived";
22
22
  archivedAt: string | unknown;
23
- inputSchemaJson: unknown;
23
+ inputSchemaJson: {
24
+ [key: string]: unknown;
25
+ } | unknown;
24
26
  stepDefinitions: Array<{
25
27
  id: string;
26
28
  pipelineDefinitionId: string;
29
+ kind: "step" | "fanOut" | "cohortGate" | "choice" | "parallel" | "loop" | "succeed" | "fail";
27
30
  stepDefinitionId: string;
28
31
  stepDefinitionVersion: number;
29
32
  key: string;
@@ -51,7 +54,9 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
51
54
  };
52
55
  } | unknown;
53
56
  timeoutSeconds: number | unknown;
54
- retryPolicyJson: unknown;
57
+ retryPolicyJson: {
58
+ [key: string]: unknown;
59
+ } | unknown;
55
60
  advancementPolicyDefinition: {
56
61
  id: string;
57
62
  pipelineStepDefinitionId: string;
@@ -100,7 +105,9 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
100
105
  }>;
101
106
  };
102
107
  defaultEventType: "continue" | "block" | "complete" | "route";
103
- defaultEventParamsJson: unknown;
108
+ defaultEventParamsJson: {
109
+ [key: string]: unknown;
110
+ } | unknown;
104
111
  allowedEventTypes: Array<"continue" | "block" | "complete" | "route">;
105
112
  createdAt: string;
106
113
  updatedAt: string;
@@ -110,7 +117,9 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
110
117
  key: string;
111
118
  type: "average" | "weighted_average" | "sum" | "min" | "max" | "count" | "boolean_any" | "boolean_all";
112
119
  inputSignalKeys: Array<string>;
113
- configJson: unknown;
120
+ configJson: {
121
+ [key: string]: unknown;
122
+ } | unknown;
114
123
  availableWhenResultStatusIn: Array<string> | unknown;
115
124
  createdAt: string;
116
125
  updatedAt: string;
@@ -123,11 +132,23 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
123
132
  }[]>;
124
133
  /**
125
134
  * Upserts a pipeline definition keyed by (projectId, key). Accepts the
126
- * `PipelineDefinitionSpec` produced by `pipeline().build()`, along with the
135
+ * `PipelineDefinitionSpec` produced by `definePipeline()`, along with the
127
136
  * list of pushed step definitions (used to resolve `stepDefinitionId` for
128
- * each pipeline step).
137
+ * each node/branch referencing a step by key).
138
+ *
139
+ * Pushes via the richer `nodeDefinitions[]`/`dependencyEdges[]` graph
140
+ * shape (see `buildGraphNodeInput`) rather than the old flat,
141
+ * `position`-ordered `stepDefinitions[]` shape — every node kind
142
+ * `definePipeline()` can produce (`choice`/`fanOut`/`cohortGate`/
143
+ * `parallel`/`loop`/`succeed`/`fail`, not just `step`) is representable
144
+ * this way; the flat shape could only ever represent a `step`-only
145
+ * chain. The generated `UpsertPipelineDefinitionInput` type doesn't yet
146
+ * reflect this (it's OpenAPI-codegen'd from the pre-existing flat
147
+ * contract), so the body is cast at the boundary — the server's zod
148
+ * schema (`createPipelineDefinitionInputSchema`, extended to accept
149
+ * either shape) is the real, enforced contract.
129
150
  *
130
- * Throws if any pipeline step references a step key/version that isn't
151
+ * Throws if any node/branch references a step key/version that isn't
131
152
  * present in `stepDefs`.
132
153
  */
133
154
  upsertFromSpec: (projectId: string, spec: PipelineDefinitionSpec, stepDefs: ReadonlyArray<StepDefinitionRef>, options?: RequestOptions) => Promise<{
@@ -138,10 +159,13 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
138
159
  description: string | unknown;
139
160
  status: "draft" | "active" | "archived";
140
161
  archivedAt: string | unknown;
141
- inputSchemaJson: unknown;
162
+ inputSchemaJson: {
163
+ [key: string]: unknown;
164
+ } | unknown;
142
165
  stepDefinitions: Array<{
143
166
  id: string;
144
167
  pipelineDefinitionId: string;
168
+ kind: "step" | "fanOut" | "cohortGate" | "choice" | "parallel" | "loop" | "succeed" | "fail";
145
169
  stepDefinitionId: string;
146
170
  stepDefinitionVersion: number;
147
171
  key: string;
@@ -169,7 +193,9 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
169
193
  };
170
194
  } | unknown;
171
195
  timeoutSeconds: number | unknown;
172
- retryPolicyJson: unknown;
196
+ retryPolicyJson: {
197
+ [key: string]: unknown;
198
+ } | unknown;
173
199
  advancementPolicyDefinition: {
174
200
  id: string;
175
201
  pipelineStepDefinitionId: string;
@@ -218,7 +244,9 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
218
244
  }>;
219
245
  };
220
246
  defaultEventType: "continue" | "block" | "complete" | "route";
221
- defaultEventParamsJson: unknown;
247
+ defaultEventParamsJson: {
248
+ [key: string]: unknown;
249
+ } | unknown;
222
250
  allowedEventTypes: Array<"continue" | "block" | "complete" | "route">;
223
251
  createdAt: string;
224
252
  updatedAt: string;
@@ -228,7 +256,9 @@ declare const buildPipelineDefinitionsClient: (pipelineDefinitions: PipelineDefi
228
256
  key: string;
229
257
  type: "average" | "weighted_average" | "sum" | "min" | "max" | "count" | "boolean_any" | "boolean_all";
230
258
  inputSignalKeys: Array<string>;
231
- configJson: unknown;
259
+ configJson: {
260
+ [key: string]: unknown;
261
+ } | unknown;
232
262
  availableWhenResultStatusIn: Array<string> | unknown;
233
263
  createdAt: string;
234
264
  updatedAt: string;
@@ -0,0 +1,104 @@
1
+ import type { ZodType } from "zod";
2
+ import type { RuleCondition } from "../advancement-policies/define-advancement-policy";
3
+ import type { AdvanceAllCtx, AdvanceAllResult, AdvanceEachCtx, AdvanceEachResult } from "../advancement-policies/cohort-fluent-rules";
4
+ import type { AnyTypedStep } from "./builder-helpers";
5
+ import type { AnyBinding } from "./bindings";
6
+ import type { FanOutNodeInputCtx, NodeInputCtx } from "./node-input-ctx";
7
+ /**
8
+ * The 7 authoring state kinds (see
9
+ * docs/research/flat-pipeline-sdk-and-visual-designer.md §4/§5). Every
10
+ * kind besides `choice`/`succeed`/`fail` does its own work (a `step`) and
11
+ * therefore declares exactly one authored `input`/`timeout`; `choice` and
12
+ * `loop` are the only kinds with more than one possible exit.
13
+ */
14
+ /** `next: "otherStateKey"`, or a special cross-pipeline route target. */
15
+ export type NextTarget = string | {
16
+ routeToPipeline: string;
17
+ input?: Record<string, unknown> | null;
18
+ };
19
+ export type StepState = {
20
+ kind: "step";
21
+ step: AnyTypedStep;
22
+ input?: (ctx: NodeInputCtx) => Partial<Record<string, AnyBinding>>;
23
+ timeout?: number | null;
24
+ /**
25
+ * A single-condition "pause for human review" gate — the narrow
26
+ * replacement for the old per-step rules-engine policy object (see §5's
27
+ * `complete`/`route`/`block` migration notes). When the condition
28
+ * matches, the node run blocks instead of advancing to `next`.
29
+ */
30
+ blockWhen?: RuleCondition;
31
+ next: NextTarget;
32
+ };
33
+ export type ChoiceCase = {
34
+ when: RuleCondition;
35
+ next: string;
36
+ };
37
+ export type ChoiceState = {
38
+ kind: "choice";
39
+ /** At least one of `choices`/`default` is required — see `compileChoiceState`. */
40
+ choices?: ReadonlyArray<ChoiceCase>;
41
+ default?: string | null;
42
+ };
43
+ export type FanOutState = {
44
+ kind: "fanOut";
45
+ step: AnyTypedStep;
46
+ /**
47
+ * The signal to resolve branch cardinality (and, in array mode, each
48
+ * branch's own item) from. A bare signal key (`"changedFiles"`) or a
49
+ * `"stateKey.signalKey"` dotted convenience form — the dotted prefix is
50
+ * author-facing documentation only; the runtime resolves the signal by
51
+ * scanning every upstream node's own signals for a matching key (see
52
+ * `resolveFanOutCardinality`), not by an explicit node reference.
53
+ */
54
+ over: string;
55
+ maxConcurrency?: number | null;
56
+ input?: (ctx: FanOutNodeInputCtx) => Partial<Record<string, AnyBinding>>;
57
+ timeout?: number | null;
58
+ advanceEach: (ctx: AdvanceEachCtx) => AdvanceEachResult;
59
+ advanceAll: (ctx: AdvanceAllCtx) => AdvanceAllResult;
60
+ next: string;
61
+ };
62
+ export type ParallelBranchConfig = {
63
+ step: AnyTypedStep;
64
+ input?: (ctx: NodeInputCtx) => Partial<Record<string, AnyBinding>>;
65
+ timeout?: number | null;
66
+ };
67
+ export type ParallelState = {
68
+ kind: "parallel";
69
+ /** Named branches (v1: single-step only — see decision 5). */
70
+ branches: Record<string, ParallelBranchConfig>;
71
+ /** Defaults to "continue iff every branch continued" when omitted. */
72
+ advanceAll?: (ctx: AdvanceAllCtx) => AdvanceAllResult;
73
+ next: string;
74
+ };
75
+ export type LoopState = {
76
+ kind: "loop";
77
+ step: AnyTypedStep;
78
+ maxIterations: number;
79
+ input?: (ctx: NodeInputCtx) => Partial<Record<string, AnyBinding>>;
80
+ timeout?: number | null;
81
+ until: RuleCondition;
82
+ next: string;
83
+ onExhausted: string;
84
+ };
85
+ export type SucceedState = {
86
+ kind: "succeed";
87
+ };
88
+ export type FailState = {
89
+ kind: "fail";
90
+ };
91
+ export type PipelineState = StepState | ChoiceState | FanOutState | ParallelState | LoopState | SucceedState | FailState;
92
+ export type DefinePipelineInput<TInput extends ZodType = ZodType> = {
93
+ key: string;
94
+ name?: string;
95
+ description?: string | null;
96
+ version?: number;
97
+ status?: "draft" | "active";
98
+ /** The pipeline's own input schema — drives `ctx.pipelineInput(path)`'s typing and `inputSchemaJson`. */
99
+ input?: TInput;
100
+ /** The entry state's key. Must not name a `choice`/`succeed`/`fail` state. */
101
+ startAt: string;
102
+ /** Every state in the pipeline, keyed by its unique node key. */
103
+ states: Record<string, PipelineState>;
104
+ };
@@ -0,0 +1,98 @@
1
+ import type { GetApiWorkItemsByWorkItemIdResponses } from "../../generated/types.gen";
2
+ /**
3
+ * Top-level work-item properties addressable by every work-item binding
4
+ * mechanism in the SDK: the default-pipeline-assignment DSL's
5
+ * `workItem.<field>` accessor (`define-default-pipeline-assignment.ts`) and
6
+ * the regular pipeline/step `workItem.<field>` accessor
7
+ * (`builder-helpers.ts`'s `WorkItemAccessor`) — and, reversed, both
8
+ * `boboddy pipelines pull` file generators
9
+ * (`default-pipeline-assignment-file-generator.ts`,
10
+ * `pipeline-file-generator.ts`). `fields` is deliberately excluded from this
11
+ * list: it's reached via `.field(name)` on every one of those accessors, not
12
+ * as a top-level property.
13
+ *
14
+ * This is the single source of truth for the list — it must stay in sync
15
+ * with the `workItem` fact/context shape every corresponding server-side
16
+ * resolver publishes (`buildFacts` in
17
+ * `evaluate-default-pipeline-assignment.ts`; `buildResolvedWorkItemContext`
18
+ * in `resolve-node-input.ts`).
19
+ */
20
+ export declare const WORK_ITEM_TOP_LEVEL_FIELDS: readonly ["id", "projectId", "platform", "platformId", "platformKey", "url", "title", "description", "sourceCreatedAt", "sourceUpdatedAt", "createdByUserId", "parentWorkItemId", "createdAt", "updatedAt"];
21
+ export type WorkItemTopLevelField = (typeof WORK_ITEM_TOP_LEVEL_FIELDS)[number];
22
+ /**
23
+ * The literal union of valid work-item platforms, sourced from the generated
24
+ * OpenAPI response type (`GetApiWorkItemsByWorkItemIdResponses[200]["platform"]`)
25
+ * rather than hand-duplicated, so it cannot drift from the API contract. It
26
+ * must mirror `WorkItemPlatform` in
27
+ * `packages/core/src/work-items/work-item/domain/work-item-platform.ts`.
28
+ */
29
+ type WorkItemPlatformLiteral = GetApiWorkItemsByWorkItemIdResponses[200]["platform"];
30
+ /**
31
+ * Per-field value types for every member of `WORK_ITEM_TOP_LEVEL_FIELDS`,
32
+ * mirroring exactly what the server-side resolvers publish at evaluation/
33
+ * resolution time (`buildFacts` for default-pipeline-assignment;
34
+ * `buildResolvedWorkItemContext` for regular pipeline/step input bindings).
35
+ * Notably, `sourceCreatedAt`/`sourceUpdatedAt`/`createdAt`/`updatedAt` are
36
+ * serialized as ISO date strings (or `null`), never `Date` objects.
37
+ */
38
+ export type WorkItemTopLevelFieldTypeMap = {
39
+ id: string;
40
+ projectId: string;
41
+ platform: WorkItemPlatformLiteral;
42
+ platformId: string | null;
43
+ platformKey: string;
44
+ url: string | null;
45
+ title: string;
46
+ description: string | null;
47
+ sourceCreatedAt: string | null;
48
+ sourceUpdatedAt: string | null;
49
+ createdByUserId: string | null;
50
+ parentWorkItemId: string | null;
51
+ createdAt: string | null;
52
+ updatedAt: string | null;
53
+ };
54
+ /**
55
+ * The path prefix marking a reference into a work item's platform-specific
56
+ * `fields` bag (a Jira custom field, a GitHub label, etc.), as opposed to a
57
+ * `WorkItemTopLevelField`. Shared by every accessor that authors these paths
58
+ * (`workItem.field(name)` in both the default-pipeline-assignment DSL and
59
+ * the regular pipeline/step `WorkItemAccessor`) and every resolver that
60
+ * reads them back (`resolveWorkItemFieldPath` below; the JSONPath-rooted
61
+ * variant in `evaluate-default-pipeline-assignment.ts`, which layers a `$.`
62
+ * prefix on top of this same convention).
63
+ */
64
+ export declare const WORK_ITEM_FIELDS_PATH_PREFIX = "fields.";
65
+ /**
66
+ * Resolves a work-item path — either a `WorkItemTopLevelField` name (a
67
+ * single, code-controlled segment) or a `fields.<name>` reference into the
68
+ * platform-specific `fields` bag — against a plain object shaped like
69
+ * `WorkItemTopLevelFieldTypeMap & { fields: ... }`.
70
+ *
71
+ * `<name>` in `fields.<name>` is an arbitrary, unescaped, platform-supplied
72
+ * field name (a Jira custom field label, a GitHub key, etc.) that may
73
+ * contain any character, including `.`, `[`, `]`, `~`, `^`, `;`, or even be
74
+ * the empty string. A naive dot-splitting path walker would silently
75
+ * misresolve a field literally named `"a.b"` as nested property access
76
+ * (`fields.a.b`) instead of the flat key `fields["a.b"]` — a silent-wrong-
77
+ * match hazard, not a visible error, since the walker degrades to "no
78
+ * match"/"wrong match" rather than throwing.
79
+ *
80
+ * This resolver sidesteps that hazard entirely: everything after `fields.`
81
+ * is treated as one atomic, unsplit key. Every work-item path resolver in
82
+ * the codebase must use this same convention for a field name to round-trip
83
+ * correctly — see `evaluate-default-pipeline-assignment.ts`'s
84
+ * `resolveWorkItemAssignmentPath` (which layers `$.` path-rooting on top of
85
+ * this function) and `resolve-node-input.ts`'s `work_item` binding
86
+ * resolution (which calls this function directly, replacing what used to be
87
+ * a dot-splitting walker shared with unrelated binding sources and
88
+ * susceptible to the exact hazard described above).
89
+ *
90
+ * `path` is untyped (`string`, not `WorkItemTopLevelField | \`fields.${string}\``)
91
+ * because callers validate membership separately
92
+ * (`WORK_ITEM_TOP_LEVEL_FIELDS`/`isSupportedWorkItemField`) before ever
93
+ * reaching a resolver — this function's contract is purely "resolve
94
+ * whatever path string you hand it, treating `fields.` as atomic," not
95
+ * "validate the path."
96
+ */
97
+ export declare function resolveWorkItemFieldPath(record: unknown, path: string): unknown;
98
+ export {};
@@ -0,0 +1,50 @@
1
+ import type { ZodType } from "zod";
2
+ import type { SignalKeysOf, SignalTypeMapOf, TypedStepDefinitionSpec } from "./define-step";
3
+ /**
4
+ * `codeStep()`'s own signal spec shape — unlike `defineStep()`'s
5
+ * `SignalSpecInput`, `type` is required rather than inferred from the
6
+ * result schema at a dot-path (`resolveZodSchemaAtPath`/
7
+ * `zodTypeToSignalType` are `define-step.ts`-internal, and a code step's
8
+ * result shape is arbitrary application data, not necessarily reflecting
9
+ * one at all) — a small, deliberate simplification over `defineStep`'s own
10
+ * signal authoring surface.
11
+ */
12
+ export type CodeStepSignalSpec = {
13
+ key?: string;
14
+ sourcePath: string;
15
+ type: "string" | "number" | "boolean" | "object" | "array";
16
+ required?: boolean;
17
+ availableWhenResultStatusIn?: string[] | null;
18
+ };
19
+ export type CodeStepFn<TInput, TResult> = (input: TInput) => TResult | Promise<TResult>;
20
+ export type DefineCodeStepInput<TInput extends ZodType = ZodType, TResult extends ZodType = ZodType> = {
21
+ key: string;
22
+ name: string;
23
+ description?: string | null;
24
+ version?: number;
25
+ /**
26
+ * The step's own implementation — resolved by `collect-definitions.ts`
27
+ * to a portable `{sourceFile, exportName}` pair (by identity-matching
28
+ * this reference against the declaring module's other exports) before
29
+ * the (unserializable) function reference is ever sent over the wire.
30
+ * Must be a plain named export of the same module `codeStep()` is
31
+ * called from (see docs/research/flat-pipeline-sdk-and-visual-designer.md
32
+ * §7.7/§8's "code-step entrypoints resolve against the target repo").
33
+ */
34
+ fn: CodeStepFn<TInput["_output"], TResult["_output"]>;
35
+ inputSchema?: TInput;
36
+ resultSchema?: TResult;
37
+ signals?: readonly CodeStepSignalSpec[];
38
+ status?: "draft" | "active";
39
+ };
40
+ /**
41
+ * Defines a `kind: "code"` step: a plain function instead of an LLM
42
+ * prompt. Produces the same phantom-typed `TypedStepDefinitionSpec` shape
43
+ * `defineStep()` does (so it plugs into `definePipeline()`'s
44
+ * `states[key].step` field unchanged), but with no `prompt` and a live
45
+ * `entrypoint.fn` reference attached instead — see `StepDefinitionSpec`'s
46
+ * own doc comment for what happens to it during collection.
47
+ */
48
+ export declare function codeStep<TInput extends ZodType = ZodType, TResult extends ZodType = ZodType, const TSignals extends ReadonlyArray<CodeStepSignalSpec> = never[]>(config: DefineCodeStepInput<TInput, TResult> & {
49
+ signals?: TSignals;
50
+ }): TypedStepDefinitionSpec<TInput["_output"], TResult["_output"], SignalKeysOf<TSignals>, SignalTypeMapOf<TSignals, TResult["_output"]>>;
@@ -1,6 +1,7 @@
1
1
  import type { ZodType } from "zod";
2
2
  import type { AnyStepFeature, FeatureResultExtensions, FeatureSignalKeys } from "./step-features";
3
3
  import { type PromptTemplateContext } from "./prompt-template";
4
+ import type { LiteralBinding, WorkItemBinding } from "../pipelines/define-pipeline";
4
5
  type OpenCodeMcpServers = Record<string, {
5
6
  type: "local";
6
7
  command: string[];
@@ -89,20 +90,39 @@ export type DefineStepInput<TInput extends ZodType = ZodType, TResult extends Zo
89
90
  status?: "draft" | "active";
90
91
  executionMode?: "workspace" | "no_workspace";
91
92
  };
92
- export type AdditionalStepInputLiteralBinding = {
93
- source: "literal";
94
- value: unknown;
93
+ /**
94
+ * @deprecated Alias for `LiteralBinding` (`define-pipeline.ts`) — the two
95
+ * used to be independently declared, structurally-identical types. Kept
96
+ * only so existing imports of this name keep working.
97
+ */
98
+ export type AdditionalStepInputLiteralBinding = LiteralBinding;
99
+ /**
100
+ * The binding sources available to `PipelineMeta.additionalStepInput`'s
101
+ * `bindings` callback (`workItemField(...)`/`literal(...)` in
102
+ * `builder-helpers.ts`'s `resolveAdditionalStepInputBindings`) — the same
103
+ * `WorkItemBinding`/`LiteralBinding` shapes `.step()`'s own `input` mapper
104
+ * uses (`define-pipeline.ts`'s `AnyBinding`), restricted to the subset
105
+ * `resolveAdditionalStepInputBindings` can actually produce. Previously an
106
+ * independently-declared `{ source: "work_item"; field: string }` union
107
+ * member that had drifted apart from `WorkItemBinding` in shape only by
108
+ * coincidence, not by design.
109
+ */
110
+ export type AdditionalStepInputBinding = WorkItemBinding | LiteralBinding;
111
+ /**
112
+ * A `kind: "code"` step's portable entrypoint, once resolved by
113
+ * `collect-definitions.ts`'s identity-capture pass — see
114
+ * docs/research/flat-pipeline-sdk-and-visual-designer.md §7.7/§8.
115
+ */
116
+ export type StepDefinitionEntrypointJson = {
117
+ sourceFile: string;
118
+ exportName: string;
95
119
  };
96
- export type AdditionalStepInputBinding = {
97
- source: "work_item";
98
- field: string;
99
- } | AdditionalStepInputLiteralBinding;
100
120
  export type StepDefinitionSpec = {
101
121
  key: string;
102
122
  name: string;
103
123
  description: string | null;
104
124
  version: number;
105
- kind: "user_defined";
125
+ kind: "user_defined" | "code";
106
126
  status: "draft" | "active" | "archived";
107
127
  executionMode?: "workspace" | "no_workspace";
108
128
  prompt: string | null;
@@ -118,6 +138,18 @@ export type StepDefinitionSpec = {
118
138
  opencodeMcpJson: OpenCodeMcpServers | null;
119
139
  opencodePluginJson: OpenCodePlugins | null;
120
140
  healthChecksJson: HealthChecks | null;
141
+ /**
142
+ * `kind === "code"` only, and only *before* collection —
143
+ * `collect-definitions.ts` resolves this live `fn` reference down to
144
+ * `entrypointJson` (by identity-matching against the declaring module's
145
+ * other exports) and strips this field before the spec is ever pushed;
146
+ * it can never be serialized into the push request.
147
+ */
148
+ entrypoint?: {
149
+ fn: (input: unknown) => unknown;
150
+ };
151
+ /** `kind === "code"` only, present after `collect-definitions.ts` has run. */
152
+ entrypointJson?: StepDefinitionEntrypointJson | null;
121
153
  };
122
154
  export type SignalTypeStrToTs<T extends SignalTypeStr> = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends "array" ? unknown[] : T extends "object" ? object : unknown;
123
155
  export type SignalTypeMapOf<TSignals extends readonly unknown[], TResult> = Prettify<{
@@ -1,4 +1,5 @@
1
1
  export * from "./define-step";
2
+ export * from "./define-code-step";
2
3
  export * from "./prompt-template";
3
4
  export * from "./step-definitions-client";
4
5
  export * from "./step-features";
@@ -12080,6 +12080,32 @@ ${feature._promptAddition}` : feature._promptAddition;
12080
12080
  };
12081
12081
  return spec;
12082
12082
  }
12083
+ // src/definitions/steps/define-code-step.ts
12084
+ function codeStep(config2) {
12085
+ const spec = {
12086
+ key: config2.key,
12087
+ name: config2.name,
12088
+ description: config2.description ?? null,
12089
+ version: config2.version ?? 1,
12090
+ kind: "code",
12091
+ status: config2.status ?? "active",
12092
+ prompt: null,
12093
+ inputSchemaJson: config2.inputSchema ? toJSONSchema(config2.inputSchema) : null,
12094
+ resultSchemaJson: config2.resultSchema ? toJSONSchema(config2.resultSchema) : null,
12095
+ signalExtractorDefinitions: (config2.signals ?? []).map((signal) => ({
12096
+ key: signal.key ?? signal.sourcePath,
12097
+ sourcePath: signal.sourcePath,
12098
+ type: signal.type,
12099
+ required: signal.required ?? true,
12100
+ availableWhenResultStatusIn: signal.availableWhenResultStatusIn ?? null
12101
+ })),
12102
+ opencodeMcpJson: null,
12103
+ opencodePluginJson: null,
12104
+ healthChecksJson: null,
12105
+ entrypoint: { fn: config2.fn }
12106
+ };
12107
+ return spec;
12108
+ }
12083
12109
  // src/generated/core/bodySerializer.gen.ts
12084
12110
  var jsonBodySerializer = {
12085
12111
  bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
@@ -13159,29 +13185,6 @@ class PipelineDefinitions extends HeyApiClient {
13159
13185
  unarchivePipelineDefinition(options) {
13160
13186
  return (options.client ?? this.client).put({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/unarchive", ...options });
13161
13187
  }
13162
- addPipelineStep(options) {
13163
- return (options.client ?? this.client).post({
13164
- url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps",
13165
- ...options,
13166
- headers: {
13167
- "Content-Type": "application/json",
13168
- ...options.headers
13169
- }
13170
- });
13171
- }
13172
- removePipelineStep(options) {
13173
- return (options.client ?? this.client).delete({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}", ...options });
13174
- }
13175
- updatePipelineStep(options) {
13176
- return (options.client ?? this.client).put({
13177
- url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}",
13178
- ...options,
13179
- headers: {
13180
- "Content-Type": "application/json",
13181
- ...options.headers
13182
- }
13183
- });
13184
- }
13185
13188
  setPipelineStepAdvancementPolicy(options) {
13186
13189
  return (options.client ?? this.client).put({
13187
13190
  url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}/advancement-policy",
@@ -13710,8 +13713,21 @@ var buildStepDefinitionsClient = (stepDefinitions) => {
13710
13713
  },
13711
13714
  upsertFromSpec: async (projectId, spec, options) => {
13712
13715
  const body = {
13713
- ...spec,
13716
+ key: spec.key,
13717
+ name: spec.name,
13718
+ description: spec.description,
13714
13719
  prompt: spec.prompt ?? "",
13720
+ version: spec.version,
13721
+ kind: spec.kind,
13722
+ entrypointJson: spec.entrypointJson ?? null,
13723
+ executionMode: spec.executionMode,
13724
+ inputSchemaJson: spec.inputSchemaJson,
13725
+ resultSchemaJson: spec.resultSchemaJson,
13726
+ opencodeMcpJson: spec.opencodeMcpJson,
13727
+ opencodePluginJson: spec.opencodePluginJson,
13728
+ healthChecksJson: spec.healthChecksJson,
13729
+ status: spec.status,
13730
+ signalExtractorDefinitions: spec.signalExtractorDefinitions,
13715
13731
  projectId
13716
13732
  };
13717
13733
  const result = await stepDefinitions.upsertStepDefinition({
@@ -16136,5 +16152,6 @@ export {
16136
16152
  createStepDefinitionsClient,
16137
16153
  createPromptTemplateContext,
16138
16154
  createPromptInputProxy,
16155
+ codeStep,
16139
16156
  Features
16140
16157
  };