@cat-factory/contracts 0.308.1 → 0.310.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 (54) hide show
  1. package/dist/agent-presentation.d.ts +16 -2
  2. package/dist/agent-presentation.d.ts.map +1 -1
  3. package/dist/agent-presentation.js +7 -0
  4. package/dist/agent-presentation.js.map +1 -1
  5. package/dist/companion-state.d.ts +1 -0
  6. package/dist/companion-state.d.ts.map +1 -1
  7. package/dist/companion.d.ts +1 -0
  8. package/dist/companion.d.ts.map +1 -1
  9. package/dist/companion.js +18 -7
  10. package/dist/companion.js.map +1 -1
  11. package/dist/entities.d.ts +1 -1
  12. package/dist/execution.d.ts +6 -0
  13. package/dist/execution.d.ts.map +1 -1
  14. package/dist/pipeline-purpose-vocabulary.d.ts +8 -3
  15. package/dist/pipeline-purpose-vocabulary.d.ts.map +1 -1
  16. package/dist/pipeline-purpose-vocabulary.js +14 -2
  17. package/dist/pipeline-purpose-vocabulary.js.map +1 -1
  18. package/dist/pipeline-purpose.d.ts +20 -12
  19. package/dist/pipeline-purpose.d.ts.map +1 -1
  20. package/dist/pipeline-purpose.js +79 -23
  21. package/dist/pipeline-purpose.js.map +1 -1
  22. package/dist/public-decisions.d.ts +88 -13
  23. package/dist/public-decisions.d.ts.map +1 -1
  24. package/dist/public-decisions.js +36 -5
  25. package/dist/public-decisions.js.map +1 -1
  26. package/dist/requests.d.ts +2 -2
  27. package/dist/routes/agent-runs.d.ts +6 -0
  28. package/dist/routes/agent-runs.d.ts.map +1 -1
  29. package/dist/routes/bug-hunt.d.ts +6 -0
  30. package/dist/routes/bug-hunt.d.ts.map +1 -1
  31. package/dist/routes/execution.d.ts +27 -0
  32. package/dist/routes/execution.d.ts.map +1 -1
  33. package/dist/routes/human-review.d.ts +3 -0
  34. package/dist/routes/human-review.d.ts.map +1 -1
  35. package/dist/routes/human-test.d.ts +15 -0
  36. package/dist/routes/human-test.d.ts.map +1 -1
  37. package/dist/routes/pipelines.d.ts +8 -8
  38. package/dist/routes/public-decisions.d.ts +164 -0
  39. package/dist/routes/public-decisions.d.ts.map +1 -1
  40. package/dist/routes/visual-confirm.d.ts +9 -0
  41. package/dist/routes/visual-confirm.d.ts.map +1 -1
  42. package/dist/routes/workspaces.d.ts +10 -4
  43. package/dist/routes/workspaces.d.ts.map +1 -1
  44. package/dist/snapshot.d.ts +5 -2
  45. package/dist/snapshot.d.ts.map +1 -1
  46. package/dist/step-decisions.d.ts +139 -0
  47. package/dist/step-decisions.d.ts.map +1 -1
  48. package/dist/step-decisions.js +117 -0
  49. package/dist/step-decisions.js.map +1 -1
  50. package/dist/visual-pipeline.d.ts +24 -1
  51. package/dist/visual-pipeline.d.ts.map +1 -1
  52. package/dist/visual-pipeline.js +44 -3
  53. package/dist/visual-pipeline.js.map +1 -1
  54. package/package.json +1 -1
@@ -23,6 +23,65 @@ export const decisionSchema = v.object({
23
23
  options: v.array(v.string()),
24
24
  chosen: v.nullable(v.string()),
25
25
  });
26
+ /**
27
+ * How urgently a review comment must be acted on, worst first. The three levels are the ones a
28
+ * reviewer already writes as prose group labels ("Must fix" / "Should fix" / "Minor"), promoted to
29
+ * a field so the ENGINE can act on them rather than a human having to read the summary to find out
30
+ * what blocks the work.
31
+ *
32
+ * `blocker` is the load-bearing member and the only one with mechanical force: while a companion's
33
+ * latest verdict carries one, the producer is reworked and the run does not advance past the step,
34
+ * whatever the overall rating says (see {@link hasBlockingReviewComments} and kernel's
35
+ * `disposeCompanionVerdict`). A rating is one number over a whole deliverable, so a review that
36
+ * found something genuinely unshippable could still average above the bar and be waved through;
37
+ * that is the gap this closes. `major` and `minor` differ only in how a reader (and a reworking
38
+ * producer) should prioritise them.
39
+ */
40
+ export const reviewCommentSeveritySchema = v.picklist(['blocker', 'major', 'minor']);
41
+ /** Rank of a {@link ReviewCommentSeverity}, for worst-first ordering and "at or above" tests. */
42
+ export const REVIEW_COMMENT_SEVERITY_RANK = {
43
+ minor: 0,
44
+ major: 1,
45
+ blocker: 2,
46
+ };
47
+ const REVIEW_COMMENT_SEVERITY_SET = new Set(reviewCommentSeveritySchema.options);
48
+ /**
49
+ * Whether a value is still a member of the vocabulary — DERIVED from the picklist, so it cannot
50
+ * drift from it the way a hand-written second list would.
51
+ *
52
+ * The vocabulary is closed but PERSISTED (a verdict's `comments`, a gate's `comments`, a step's
53
+ * `rework.comments`), and the schema's `severity` fallback does NOT cover those reads: a stored run
54
+ * row is mapped onto {@link StepReviewComment} by the facade repositories, never re-parsed, so the
55
+ * fallback runs on the MODEL REPLY and nowhere else. A member retired from the union therefore goes
56
+ * on existing in saved rows with its type claiming otherwise, and every `Record<ReviewCommentSeverity, …>`
57
+ * over it is total against the TYPE and partial against the DATA. Narrow with this at the read
58
+ * boundary and render the negative case as the unrecognised level it is.
59
+ *
60
+ * A retired member here is NAMED rather than dropped or mapped onto a current one, for the reason
61
+ * `isBinaryModality` names its own: nothing knows which surviving level was meant, and the reader
62
+ * that meets a stale value first is the panel asking a person to act on the finding. What it must
63
+ * never do is claim an urgency nobody graded. It carries no mechanical force either (a stale value
64
+ * is not a `blocker`), so retiring a member means restating the stored rows in the same change;
65
+ * this is what keeps the interim honest instead of invisible.
66
+ */
67
+ export function isReviewCommentSeverity(value) {
68
+ return REVIEW_COMMENT_SEVERITY_SET.has(value);
69
+ }
70
+ /** Where an ungraded comment (and an unrecognised level) sorts: below every graded one. */
71
+ export const UNGRADED_REVIEW_COMMENT_RANK = -1;
72
+ /**
73
+ * The rank a comment's severity sorts and compares at: {@link REVIEW_COMMENT_SEVERITY_RANK} while
74
+ * the vocabulary holds it, and {@link UNGRADED_REVIEW_COMMENT_RANK} when the comment carries no
75
+ * severity (a person's) or one this build no longer knows ({@link isReviewCommentSeverity}).
76
+ *
77
+ * The ONE place that lookup happens, so an unrecognised value cannot reach the `Record` raw and
78
+ * come back `undefined` — which sorts by `NaN` and compares false against every floor at once.
79
+ */
80
+ export function reviewCommentSeverityRank(severity) {
81
+ return severity !== undefined && isReviewCommentSeverity(severity)
82
+ ? REVIEW_COMMENT_SEVERITY_RANK[severity]
83
+ : UNGRADED_REVIEW_COMMENT_RANK;
84
+ }
26
85
  /**
27
86
  * One GitHub-review-style comment left on a specific block or item of an agent's
28
87
  * proposal — either by a human reviewing an approval gate, or by a quality
@@ -56,9 +115,63 @@ export const stepReviewCommentSchema = v.object({
56
115
  * items rather than free prose. Absent for prose-range comments.
57
116
  */
58
117
  anchorId: v.optional(v.string()),
118
+ /**
119
+ * How urgently this point must be acted on (see {@link reviewCommentSeveritySchema}).
120
+ *
121
+ * Absent on a HUMAN's comment, which carries no such grading: a person requesting changes on an
122
+ * approval gate is already holding the run, so there is nothing for a severity to decide. Absent
123
+ * too on a comment recorded before this field existed. An absent severity is therefore read as
124
+ * "ungraded", NEVER as a blocker and never as a nit — the one reader that acts on the value
125
+ * ({@link hasBlockingReviewComments}) asks only whether a `blocker` is present.
126
+ *
127
+ * An out-of-vocabulary value from a model reads as `major`, the same "unreadable severity reads
128
+ * as its safe default" rule the judge and PR-review findings use. `major` rather than either
129
+ * extreme on purpose: a typo must not manufacture a hard stop, and it must not silently retire
130
+ * one either, so it lands where the point still costs a rework round without holding the run.
131
+ *
132
+ * That fallback covers the MODEL REPLY, which is the only input this schema parses. A stored row
133
+ * is mapped onto the type by the facade repositories rather than re-parsed, so a value this build
134
+ * has retired reaches a reader with the fallback never having run: narrow those reads with
135
+ * {@link isReviewCommentSeverity} / {@link reviewCommentSeverityRank}.
136
+ */
137
+ severity: v.optional(v.fallback(reviewCommentSeveritySchema, 'major')),
59
138
  /** The reviewer's note on this block / item. */
60
139
  body: v.string(),
61
140
  });
141
+ /**
142
+ * The comments that MUST be fixed before the work moves on, worst-first-ordered input aside.
143
+ *
144
+ * The pure rule, in contracts rather than in the engine, because both sides have to agree about
145
+ * it: the engine decides whether the run advances, and the SPA states on the parked step WHY it
146
+ * stopped and which points to look at. Restated on each side, the panel would eventually count
147
+ * findings the engine did not.
148
+ */
149
+ export function blockingReviewComments(comments) {
150
+ return (comments ?? []).filter((comment) => comment.severity === 'blocker');
151
+ }
152
+ /** Whether any of `comments` is a `blocker` (see {@link blockingReviewComments}). */
153
+ export function hasBlockingReviewComments(comments) {
154
+ return (comments ?? []).some((comment) => comment.severity === 'blocker');
155
+ }
156
+ /**
157
+ * Whether a review raised anything it did NOT call a nit.
158
+ *
159
+ * What separates a batch worth sending back to the producer from one that only had polish to offer:
160
+ * a `minor` is stated to the reviewer as "never worth holding anything for", so a rule that spent a
161
+ * producer re-run plus a re-grading call on one would make that instruction false (kernel's
162
+ * `disposeCompanionVerdict` is the reader).
163
+ *
164
+ * Everything that is not the nit level counts, which puts an UNGRADED comment and a level this build
165
+ * has retired on the acting side. Both are cases where the urgency is unknown rather than known to
166
+ * be low, and the cheap error is one wasted round against a point silently dropped.
167
+ */
168
+ export function hasReviewCommentsBeyondNits(comments) {
169
+ return (comments ?? []).some((comment) => comment.severity !== 'minor');
170
+ }
171
+ /** `comments` ordered worst severity first; an ungraded comment sorts last. Stable within a level. */
172
+ export function bySeverityWorstFirst(comments) {
173
+ return [...comments].sort((a, b) => reviewCommentSeverityRank(b.severity) - reviewCommentSeverityRank(a.severity));
174
+ }
62
175
  /**
63
176
  * The standardized, stored verdict a quality companion produced for an output it
64
177
  * graded — shared by every companion site (the pipeline companion step and the
@@ -86,6 +199,10 @@ export const companionVerdictSchema = v.object({
86
199
  * mirror-image reason — so it cannot regress on a point raised two rounds ago and forgotten.
87
200
  *
88
201
  * Absent on a round that anchored nothing, and on every verdict written before this existed.
202
+ *
203
+ * Their SEVERITIES are what makes the verdict self-describing: `passed: false` on a round whose
204
+ * rating cleared `threshold` is only readable next to the `blocker` that held it (see
205
+ * {@link reviewCommentSeveritySchema}).
89
206
  */
90
207
  comments: v.optional(v.array(stepReviewCommentSchema)),
91
208
  });
@@ -1 +1 @@
1
- {"version":3,"file":"step-decisions.js","sourceRoot":"","sources":["../src/step-decisions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"step-decisions.js","sourceRoot":"","sources":["../src/step-decisions.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type RunServiceScope } from './step-conditions.js';
1
2
  import type { Block, Pipeline } from './entities.js';
2
3
  /**
3
4
  * The agent kind that drives a real browser against a running frontend and captures a
@@ -13,8 +14,30 @@ export declare const UI_TESTER_AGENT_KIND = "tester-ui";
13
14
  export declare const VISUAL_CONFIRM_AGENT_KIND = "visual-confirmation";
14
15
  /** The visual step kinds: a pipeline carrying any of these is a "visual" pipeline. */
15
16
  export declare const VISUAL_STEP_KINDS: readonly ["tester-ui", "visual-confirmation"];
16
- /** Whether a pipeline includes any visual step (`tester-ui` / `visual-confirmation`). */
17
+ /**
18
+ * Whether a pipeline includes any visual step (`tester-ui` / `visual-confirmation`) AT ALL,
19
+ * conditional ones included. The question for a caller asking whether the pipeline could ever
20
+ * drive a browser (run start resolves the frontend bindings off it). A caller deciding whether
21
+ * a pipeline may run HERE wants {@link pipelineRunsVisualStep} instead.
22
+ */
17
23
  export declare function pipelineHasVisualStep(pipeline: Pick<Pipeline, 'agentKinds'>): boolean;
24
+ /**
25
+ * Whether a run of `scope` would actually REACH a visual step: the steps that are enabled and
26
+ * whose run condition (`stepOptions[i].condition`) the scope admits. This is the question the
27
+ * frame gate below has to ask, and it is NOT the same as "does the pipeline list one".
28
+ *
29
+ * Since the build ladder carries the CONDITIONAL tester pair, every build preset lists a
30
+ * `tester-ui` scoped to `serviceScope: 'frontend'`. On a backend service that step is skipped
31
+ * before it dispatches, so the pipeline demands no UI and the run starts fine — which is exactly
32
+ * what run admission concludes, filtering the chain through the same two rules before it reaches
33
+ * the frame gate. Asking the coarse question there instead hides every build rung from every
34
+ * picker on every non-frontend service.
35
+ *
36
+ * An UNRESOLVABLE scope (neither half set: a block under no service frame) admits every
37
+ * condition, the fail-safe direction `stepConditionSatisfied` takes throughout: the visual step
38
+ * would run, so the frame gate gets to refuse it rather than a condition quietly excusing it.
39
+ */
40
+ export declare function pipelineRunsVisualStep(pipeline: Pick<Pipeline, 'agentKinds' | 'enabled' | 'stepOptions'>, scope: RunServiceScope): boolean;
18
41
  /**
19
42
  * Whether a visual pipeline may run on a task under `frame`. A visual step needs a UI to
20
43
  * exercise, which exists when the enclosing frame is either:
@@ -1 +1 @@
1
- {"version":3,"file":"visual-pipeline.d.ts","sourceRoot":"","sources":["../src/visual-pipeline.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAepD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,wBAAwB,CAAA;AAE9D,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,+CAA6D,CAAA;AAE3F,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,GAAG,OAAO,CAIrF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,SAAS,GAAG,IAAI,EACpD,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,gBAAgB,CAAC,EAAE,GAClE,OAAO,CAWT;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,gBAAgB,CAAC,EAAE,GAClE,MAAM,EAAE,CAcV"}
1
+ {"version":3,"file":"visual-pipeline.d.ts","sourceRoot":"","sources":["../src/visual-pipeline.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,eAAe,EAA0B,MAAM,sBAAsB,CAAA;AACnF,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAoBpD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,wBAAwB,CAAA;AAE9D,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,+CAA6D,CAAA;AAY3F;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,GAAG,OAAO,CAErF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,CAAC,EAClE,KAAK,EAAE,eAAe,GACrB,OAAO,CAOT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,SAAS,GAAG,IAAI,EACpD,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,gBAAgB,CAAC,EAAE,GAClE,OAAO,CAWT;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,gBAAgB,CAAC,EAAE,GAClE,MAAM,EAAE,CAcV"}
@@ -1,15 +1,21 @@
1
1
  import { resolveFrontendServePort } from './frontend.js';
2
+ import { stepConditionSatisfied } from './step-conditions.js';
2
3
  // ---------------------------------------------------------------------------
3
4
  // Visual-pipeline gating (shared by the SPA surface + the backend run-start gate).
4
5
  //
5
6
  // A "visual" pipeline exercises a rendered UI — it either drives a real browser
6
7
  // against a running frontend (`tester-ui`) or parks for a human to review the
7
8
  // captured screenshots vs the task's reference designs (`visual-confirmation`).
8
- // Such a pipeline only makes sense where there IS a UI to exercise: a `frontend`
9
+ // Such a step only makes sense where there IS a UI to exercise: a `frontend`
9
10
  // frame (it owns the app under test), or a frame a `frontend` frame links to (the
10
11
  // linked frontend is the UI a change to that service is validated through). The SPA
11
12
  // uses these predicates to surface visual pipelines only where they can run; the
12
13
  // backend gates a run start on the SAME rule so the guarantee holds server-side.
14
+ //
15
+ // What a pipeline LISTS and what a run REACHES are two questions, and the gate wants the
16
+ // second: a visual step scoped to a frontend service (`stepOptions[i].condition`) excuses
17
+ // itself from a backend run before it dispatches, which is what lets one build preset carry
18
+ // both testers. See `pipelineRunsVisualStep`.
13
19
  // ---------------------------------------------------------------------------
14
20
  /**
15
21
  * The agent kind that drives a real browser against a running frontend and captures a
@@ -25,9 +31,44 @@ export const UI_TESTER_AGENT_KIND = 'tester-ui';
25
31
  export const VISUAL_CONFIRM_AGENT_KIND = 'visual-confirmation';
26
32
  /** The visual step kinds: a pipeline carrying any of these is a "visual" pipeline. */
27
33
  export const VISUAL_STEP_KINDS = [UI_TESTER_AGENT_KIND, VISUAL_CONFIRM_AGENT_KIND];
28
- /** Whether a pipeline includes any visual step (`tester-ui` / `visual-confirmation`). */
34
+ /**
35
+ * Whether an agent kind drives a rendered UI. Reads {@link VISUAL_STEP_KINDS} rather than
36
+ * re-spelling the disjunction, so a third visual kind added to that list is seen by both
37
+ * predicates below instead of leaving the frame gate quietly offering a UI-driving pipeline on a
38
+ * frame with no UI.
39
+ */
40
+ function isVisualStepKind(kind) {
41
+ return VISUAL_STEP_KINDS.includes(kind);
42
+ }
43
+ /**
44
+ * Whether a pipeline includes any visual step (`tester-ui` / `visual-confirmation`) AT ALL,
45
+ * conditional ones included. The question for a caller asking whether the pipeline could ever
46
+ * drive a browser (run start resolves the frontend bindings off it). A caller deciding whether
47
+ * a pipeline may run HERE wants {@link pipelineRunsVisualStep} instead.
48
+ */
29
49
  export function pipelineHasVisualStep(pipeline) {
30
- return pipeline.agentKinds.some((kind) => kind === UI_TESTER_AGENT_KIND || kind === VISUAL_CONFIRM_AGENT_KIND);
50
+ return pipeline.agentKinds.some(isVisualStepKind);
51
+ }
52
+ /**
53
+ * Whether a run of `scope` would actually REACH a visual step: the steps that are enabled and
54
+ * whose run condition (`stepOptions[i].condition`) the scope admits. This is the question the
55
+ * frame gate below has to ask, and it is NOT the same as "does the pipeline list one".
56
+ *
57
+ * Since the build ladder carries the CONDITIONAL tester pair, every build preset lists a
58
+ * `tester-ui` scoped to `serviceScope: 'frontend'`. On a backend service that step is skipped
59
+ * before it dispatches, so the pipeline demands no UI and the run starts fine — which is exactly
60
+ * what run admission concludes, filtering the chain through the same two rules before it reaches
61
+ * the frame gate. Asking the coarse question there instead hides every build rung from every
62
+ * picker on every non-frontend service.
63
+ *
64
+ * An UNRESOLVABLE scope (neither half set: a block under no service frame) admits every
65
+ * condition, the fail-safe direction `stepConditionSatisfied` takes throughout: the visual step
66
+ * would run, so the frame gate gets to refuse it rather than a condition quietly excusing it.
67
+ */
68
+ export function pipelineRunsVisualStep(pipeline, scope) {
69
+ return pipeline.agentKinds.some((kind, i) => isVisualStepKind(kind) &&
70
+ pipeline.enabled?.[i] !== false &&
71
+ stepConditionSatisfied(pipeline.stepOptions?.[i]?.condition, scope));
31
72
  }
32
73
  /**
33
74
  * Whether a visual pipeline may run on a task under `frame`. A visual step needs a UI to
@@ -1 +1 @@
1
- {"version":3,"file":"visual-pipeline.js","sourceRoot":"","sources":["../src/visual-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAGxD,8EAA8E;AAC9E,mFAAmF;AACnF,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,iFAAiF;AACjF,kFAAkF;AAClF,oFAAoF;AACpF,iFAAiF;AACjF,iFAAiF;AACjF,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,WAAW,CAAA;AAE/C;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,qBAAqB,CAAA;AAE9D,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,oBAAoB,EAAE,yBAAyB,CAAU,CAAA;AAE3F,yFAAyF;AACzF,MAAM,UAAU,qBAAqB,CAAC,QAAsC;IAC1E,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAC7B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,oBAAoB,IAAI,IAAI,KAAK,yBAAyB,CAC9E,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAoD,EACpD,MAAmE;IAEnE,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAA;IAC1C,OAAO,MAAM,CAAC,IAAI,CAChB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,KAAK,KAAK,OAAO;QACnB,CAAC,CAAC,IAAI,KAAK,UAAU;QACrB,CAAC,CAAC,CAAC,cAAc,EAAE,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,CAC5C,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,KAAK,CAAC,EAAE,CACpF,CACJ,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,yBAAyB,CACvC,cAAsB,EACtB,MAAmE;IAEnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,cAAc;YAAE,SAAQ;QAC/E,MAAM,YAAY,GAAG,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,IAAI,CACxD,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;YAC9B,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,cAAc;YAC7C,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAChC,CAAA;QACD,IAAI,CAAC,YAAY;YAAE,SAAQ;QAC3B,OAAO,CAAC,GAAG,CAAC,oBAAoB,wBAAwB,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;IACzF,CAAC;IACD,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;AAC5B,CAAC"}
1
+ {"version":3,"file":"visual-pipeline.js","sourceRoot":"","sources":["../src/visual-pipeline.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/contracts",
3
- "version": "0.308.1",
3
+ "version": "0.310.0",
4
4
  "description": "Valibot wire contract shared between the Agent Architecture Board frontend and backend.",
5
5
  "repository": {
6
6
  "type": "git",