@kernloop/workflows 0.1.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/dist/budget.d.ts +111 -0
  3. package/dist/budget.d.ts.map +1 -0
  4. package/dist/budget.js +146 -0
  5. package/dist/budget.js.map +1 -0
  6. package/dist/checkpoints.d.ts +40 -0
  7. package/dist/checkpoints.d.ts.map +1 -0
  8. package/dist/checkpoints.js +92 -0
  9. package/dist/checkpoints.js.map +1 -0
  10. package/dist/child-iterate-fixtures.d.ts +36 -0
  11. package/dist/child-iterate-fixtures.d.ts.map +1 -0
  12. package/dist/child-iterate-fixtures.js +86 -0
  13. package/dist/child-iterate-fixtures.js.map +1 -0
  14. package/dist/child-iterate.d.ts +71 -0
  15. package/dist/child-iterate.d.ts.map +1 -0
  16. package/dist/child-iterate.js +61 -0
  17. package/dist/child-iterate.js.map +1 -0
  18. package/dist/child-spend.d.ts +41 -0
  19. package/dist/child-spend.d.ts.map +1 -0
  20. package/dist/child-spend.js +76 -0
  21. package/dist/child-spend.js.map +1 -0
  22. package/dist/config.d.ts +43 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +90 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/engine-errors.d.ts +11 -0
  27. package/dist/engine-errors.d.ts.map +1 -0
  28. package/dist/engine-errors.js +23 -0
  29. package/dist/engine-errors.js.map +1 -0
  30. package/dist/engine-testkit.d.ts +44 -0
  31. package/dist/engine-testkit.d.ts.map +1 -0
  32. package/dist/engine-testkit.js +93 -0
  33. package/dist/engine-testkit.js.map +1 -0
  34. package/dist/engine-types.d.ts +78 -0
  35. package/dist/engine-types.d.ts.map +1 -0
  36. package/dist/engine-types.js +2 -0
  37. package/dist/engine-types.js.map +1 -0
  38. package/dist/engine.d.ts +12 -0
  39. package/dist/engine.d.ts.map +1 -0
  40. package/dist/engine.js +262 -0
  41. package/dist/engine.js.map +1 -0
  42. package/dist/graph.d.ts +87 -0
  43. package/dist/graph.d.ts.map +1 -0
  44. package/dist/graph.js +72 -0
  45. package/dist/graph.js.map +1 -0
  46. package/dist/index.d.ts +14 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +14 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/manifest.d.ts +19 -0
  51. package/dist/manifest.d.ts.map +1 -0
  52. package/dist/manifest.js +41 -0
  53. package/dist/manifest.js.map +1 -0
  54. package/dist/state.d.ts +1078 -0
  55. package/dist/state.d.ts.map +1 -0
  56. package/dist/state.js +151 -0
  57. package/dist/state.js.map +1 -0
  58. package/dist/steps.d.ts +77 -0
  59. package/dist/steps.d.ts.map +1 -0
  60. package/dist/steps.js +270 -0
  61. package/dist/steps.js.map +1 -0
  62. package/dist/verdict-disposition.d.ts +23 -0
  63. package/dist/verdict-disposition.d.ts.map +1 -0
  64. package/dist/verdict-disposition.js +28 -0
  65. package/dist/verdict-disposition.js.map +1 -0
  66. package/package.json +38 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-iterate.d.ts","sourceRoot":"","sources":["../src/child-iterate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGxD,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;AAE5D,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;oEACoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAKtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,EACnB,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,OAAO,GACpB,WAAW,CAMb;AAED,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,IAAI,CAKN;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAGrF"}
@@ -0,0 +1,61 @@
1
+ import { verdictDisposition } from './verdict-disposition.js';
2
+ /**
3
+ * Whether THIS gate sub-node drives child iteration. Quality always drives.
4
+ * Review drives only when `reviewDrives` is on (the review gate is at enforce)
5
+ * — the honesty guard. Parsimony drives only when `parsimonyDrives` is on (the
6
+ * overlay's `gates.parsimony.intensity` is full/ultra, #9/#415) — at lite/off it
7
+ * is advisory/disabled. Non-driving gates never trigger a re-implement; their
8
+ * findings still fold in as hints (see {@link foldHints}).
9
+ */
10
+ export function gateDrivesIteration(node, drivers) {
11
+ if (node.gate === 'quality')
12
+ return true;
13
+ if (node.gate === 'review')
14
+ return drivers.reviewDrives;
15
+ if (node.gate === 'parsimony')
16
+ return drivers.parsimonyDrives;
17
+ return false;
18
+ }
19
+ /**
20
+ * Decide the branch after a driving gate ran for a child. `pass` advances the
21
+ * sub-chain; `reiterate` re-runs implement (within Kc and budget); `escalate`
22
+ * stops the child at the bound. `withinBudget` is false when a re-entry would
23
+ * exceed the run budget (Part B) — that forces `escalate` before Kc.
24
+ *
25
+ * An `escalate` VERDICT (#192) — the gate ruling "a human must decide" — forces
26
+ * `escalate` IMMEDIATELY, regardless of Kc or budget: a human ruling is not a
27
+ * re-attempt. Routing goes through {@link verdictDisposition} so a future
28
+ * `VerdictResult` value is a compile error here, never a silent mis-branch.
29
+ */
30
+ export function childBranch(verdict, result, kc, withinBudget) {
31
+ const disposition = verdictDisposition(verdict.result);
32
+ if (disposition === 'advance')
33
+ return 'pass';
34
+ if (disposition === 'escalate')
35
+ return 'escalate';
36
+ if (result.iteration >= kc || !withinBudget)
37
+ return 'escalate';
38
+ return 'reiterate';
39
+ }
40
+ /** Fold a non-driving gate's findings into the child as hints (review → next attempt). */
41
+ export function foldHints(result, findings) {
42
+ result.findings.push(...findings);
43
+ }
44
+ /**
45
+ * Re-enter the child's implement sub-node: fold the driving gate's findings,
46
+ * bump the child iteration, reset the sub-cursor to 0 (implement). Mirrors a
47
+ * rejected vote pushing findings and re-entering plan.
48
+ */
49
+ export function reiterateChild(state, result, findings) {
50
+ if (state.cursor.phase !== 'fanout')
51
+ return;
52
+ result.findings.push(...findings);
53
+ result.iteration += 1;
54
+ state.cursor = { phase: 'fanout', childIndex: state.cursor.childIndex, sub: 0 };
55
+ }
56
+ /** Mark a child escalated at the bound: record the findings, never re-attempt. */
57
+ export function escalateChild(result, findings) {
58
+ result.findings.push(...findings);
59
+ result.escalated = true;
60
+ }
61
+ //# sourceMappingURL=child-iterate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-iterate.js","sourceRoot":"","sources":["../src/child-iterate.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAoB9D;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAc,EAAE,OAAyB;IAC3E,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,YAAY,CAAC;IACxD,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC,eAAe,CAAC;IAC9D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,OAAgB,EAChB,MAAmB,EACnB,EAAU,EACV,YAAqB;IAErB,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAC7C,IAAI,WAAW,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAClD,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,IAAI,CAAC,YAAY;QAAE,OAAO,UAAU,CAAC;IAC/D,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,MAAmB,EAAE,QAA4B;IACzE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAe,EACf,MAAmB,EACnB,QAA4B;IAE5B,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO;IAC5C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IAClC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;IACtB,KAAK,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAClF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,QAA4B;IAC7E,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IAClC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Per-child spend attribution for the fan-out (#56). Because the canonical loop
3
+ * runs children SEQUENTIALLY, the engine slices the run-global meter by the
4
+ * child boundary: snapshot an injected `meteredSpend` readout when it first
5
+ * steps into a child, and the delta to any later point is that child's spend,
6
+ * summed across all its Kc iterations. Pure of I/O — the engine owns the loop;
7
+ * this owns the arithmetic and the per-child budget verdict.
8
+ */
9
+ import { type BudgetSpend } from './budget.js';
10
+ import type { RunResult, RunState } from './state.js';
11
+ /**
12
+ * Tracks the spend baseline of the child the fan-out is currently running and
13
+ * derives each child's attributed slice from the live meter. Unmetered (no
14
+ * `meteredSpend` seam) → every readout is undefined and nothing is attributed.
15
+ */
16
+ export declare class ChildSpendTracker {
17
+ private readonly meteredSpend;
18
+ private baseline;
19
+ constructor(meteredSpend: (() => BudgetSpend) | undefined);
20
+ /**
21
+ * Start-of-loop reset: forget the baseline AND drop any pre-resume child spend
22
+ * (#212), so attribution is per-PROCESS — a resumed run re-attributes from the
23
+ * fresh meter and `childSpend` stays within the (also per-process) run cost.
24
+ */
25
+ reset(state: RunState): void;
26
+ /** Snapshot the meter as child `index`'s baseline the first time it is stepped. */
27
+ ensureBaseline(index: number): void;
28
+ /** Spend attributed to child `index` so far: meter now minus its baseline (≥ 0). */
29
+ delta(index: number): BudgetSpend | undefined;
30
+ /** Record child `index`'s attributed spend onto its result row (#56). */
31
+ attribute(state: RunState, index: number): void;
32
+ /**
33
+ * False when the CURRENT fan-out child has overspent its OWN sliced budget —
34
+ * gating re-iteration in ENFORCE mode only (`enforce` false → never halts
35
+ * per-child, mirroring the run-level discipline). Sibling-independent.
36
+ */
37
+ withinOwnBudget(enforce: boolean, state: RunState): boolean;
38
+ }
39
+ /** Per-child spend entries for a RunResult, or undefined when none was metered (#56). */
40
+ export declare function childSpends(state: RunState): RunResult['childSpend'];
41
+ //# sourceMappingURL=child-spend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-spend.d.ts","sourceRoot":"","sources":["../src/child-spend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;GAIG;AACH,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAFzC,OAAO,CAAC,QAAQ,CAAoD;gBAEvC,YAAY,EAAE,CAAC,MAAM,WAAW,CAAC,GAAG,SAAS;IAE1E;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAK5B,mFAAmF;IACnF,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMnC,oFAAoF;IACpF,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU7C,yEAAyE;IACzE,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAM/C;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO;CAO5D;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAC,CAKpE"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Per-child spend attribution for the fan-out (#56). Because the canonical loop
3
+ * runs children SEQUENTIALLY, the engine slices the run-global meter by the
4
+ * child boundary: snapshot an injected `meteredSpend` readout when it first
5
+ * steps into a child, and the delta to any later point is that child's spend,
6
+ * summed across all its Kc iterations. Pure of I/O — the engine owns the loop;
7
+ * this owns the arithmetic and the per-child budget verdict.
8
+ */
9
+ import { childOverOwnBudget } from './budget.js';
10
+ /**
11
+ * Tracks the spend baseline of the child the fan-out is currently running and
12
+ * derives each child's attributed slice from the live meter. Unmetered (no
13
+ * `meteredSpend` seam) → every readout is undefined and nothing is attributed.
14
+ */
15
+ export class ChildSpendTracker {
16
+ meteredSpend;
17
+ baseline;
18
+ constructor(meteredSpend) {
19
+ this.meteredSpend = meteredSpend;
20
+ }
21
+ /**
22
+ * Start-of-loop reset: forget the baseline AND drop any pre-resume child spend
23
+ * (#212), so attribution is per-PROCESS — a resumed run re-attributes from the
24
+ * fresh meter and `childSpend` stays within the (also per-process) run cost.
25
+ */
26
+ reset(state) {
27
+ this.baseline = undefined;
28
+ for (const r of state.childResults)
29
+ r.spend = undefined;
30
+ }
31
+ /** Snapshot the meter as child `index`'s baseline the first time it is stepped. */
32
+ ensureBaseline(index) {
33
+ const spend = this.meteredSpend?.();
34
+ if (spend === undefined || this.baseline?.index === index)
35
+ return;
36
+ this.baseline = { index, spend };
37
+ }
38
+ /** Spend attributed to child `index` so far: meter now minus its baseline (≥ 0). */
39
+ delta(index) {
40
+ const spend = this.meteredSpend?.();
41
+ if (spend === undefined || this.baseline?.index !== index)
42
+ return undefined;
43
+ const base = this.baseline.spend;
44
+ return {
45
+ tokens: Math.max(0, spend.tokens - base.tokens),
46
+ usd: Math.max(0, spend.usd - base.usd),
47
+ };
48
+ }
49
+ /** Record child `index`'s attributed spend onto its result row (#56). */
50
+ attribute(state, index) {
51
+ const d = this.delta(index);
52
+ const result = state.childResults[index];
53
+ if (d !== undefined && result !== undefined)
54
+ result.spend = d;
55
+ }
56
+ /**
57
+ * False when the CURRENT fan-out child has overspent its OWN sliced budget —
58
+ * gating re-iteration in ENFORCE mode only (`enforce` false → never halts
59
+ * per-child, mirroring the run-level discipline). Sibling-independent.
60
+ */
61
+ withinOwnBudget(enforce, state) {
62
+ if (!enforce || state.cursor.phase !== 'fanout')
63
+ return true;
64
+ const d = this.delta(state.cursor.childIndex);
65
+ const result = state.childResults[state.cursor.childIndex];
66
+ if (d === undefined || result === undefined)
67
+ return true;
68
+ return !childOverOwnBudget(result.child.budget, d);
69
+ }
70
+ }
71
+ /** Per-child spend entries for a RunResult, or undefined when none was metered (#56). */
72
+ export function childSpends(state) {
73
+ const entries = state.childResults.flatMap((r) => r.spend === undefined ? [] : [{ childId: r.child.id, spend: r.spend }]);
74
+ return entries.length > 0 ? entries : undefined;
75
+ }
76
+ //# sourceMappingURL=child-spend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-spend.js","sourceRoot":"","sources":["../src/child-spend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Engine configuration schema (spec §6, §8). EngineConfig is the loop-relevant
3
+ * subset of the cli's `OverlaySchema` (`packages/cli/src/overlay.ts`): `K`,
4
+ * `Kc`, `reviewDrivesIteration`, `gates.vote.{strategy,panel}`, and
5
+ * `nodeOverrides` share its field names and value spaces, so the composition
6
+ * root maps `Overlay` → `EngineConfig` field-for-field [CLM-0045]. Kept apart
7
+ * from the engine so neither file outgrows the 400-line budget.
8
+ */
9
+ import { z } from 'zod';
10
+ /** Engine configuration — see the module docs for the overlay mapping. */
11
+ export declare const EngineConfigSchema: z.ZodPrefault<z.ZodObject<{
12
+ K: z.ZodDefault<z.ZodNumber>;
13
+ Kc: z.ZodDefault<z.ZodNumber>;
14
+ reviewDrivesIteration: z.ZodDefault<z.ZodBoolean>;
15
+ parsimonyDrivesIteration: z.ZodDefault<z.ZodBoolean>;
16
+ budgetHeadroomFraction: z.ZodDefault<z.ZodNumber>;
17
+ gates: z.ZodPrefault<z.ZodObject<{
18
+ vote: z.ZodPrefault<z.ZodObject<{
19
+ strategy: z.ZodDefault<z.ZodEnum<{
20
+ simple_majority: "simple_majority";
21
+ supermajority: "supermajority";
22
+ unanimous: "unanimous";
23
+ }>>;
24
+ panel: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<3>, z.ZodLiteral<7>]>>;
25
+ providerDiverse: z.ZodDefault<z.ZodBoolean>;
26
+ escalateOnNoConsensus: z.ZodDefault<z.ZodBoolean>;
27
+ precisionWeighted: z.ZodDefault<z.ZodBoolean>;
28
+ correlationAware: z.ZodDefault<z.ZodBoolean>;
29
+ correlationForm: z.ZodDefault<z.ZodEnum<{
30
+ sqrt: "sqrt";
31
+ linear: "linear";
32
+ }>>;
33
+ minDistinctClasses: z.ZodOptional<z.ZodNumber>;
34
+ }, z.core.$strict>>;
35
+ }, z.core.$strict>>;
36
+ nodeOverrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
37
+ gate: z.ZodOptional<z.ZodString>;
38
+ specialists: z.ZodOptional<z.ZodArray<z.ZodString>>;
39
+ }, z.core.$strict>>>;
40
+ }, z.core.$strict>>;
41
+ export type EngineConfig = z.infer<typeof EngineConfigSchema>;
42
+ export type EngineConfigInput = z.input<typeof EngineConfigSchema>;
43
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2CxB,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAuChB,CAAC;AAChB,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Engine configuration schema (spec §6, §8). EngineConfig is the loop-relevant
3
+ * subset of the cli's `OverlaySchema` (`packages/cli/src/overlay.ts`): `K`,
4
+ * `Kc`, `reviewDrivesIteration`, `gates.vote.{strategy,panel}`, and
5
+ * `nodeOverrides` share its field names and value spaces, so the composition
6
+ * root maps `Overlay` → `EngineConfig` field-for-field [CLM-0045]. Kept apart
7
+ * from the engine so neither file outgrows the 400-line budget.
8
+ */
9
+ import { z } from 'zod';
10
+ /** Vote strategies in use (mirrors the cli overlay's VOTE_STRATEGIES). */
11
+ const VoteConfigSchema = z.strictObject({
12
+ strategy: z.enum(['simple_majority', 'supermajority', 'unanimous']).default('simple_majority'),
13
+ panel: z.union([z.literal(3), z.literal(7)]).default(3),
14
+ /** Opt-in PROVIDER-DIVERSE panel-7 voting (#369). Default FALSE (#461): a live
15
+ * experiment found 3 independent model families gave identical verdicts to a
16
+ * single strong model on every test proposal — the adversarial ROLES carry the
17
+ * signal, so a panel-7 ratification vote runs roles-on-the-run-adapter by default
18
+ * (one model, N personas) and does not require ≥2 authed adapters. Set true to
19
+ * route voters across the overlay's distinct CLI adapters for genuine model
20
+ * independence (the #405 distinct-class quorum + diversity findings then engage);
21
+ * off ⇒ no served identities, so the quorum/findings are inert. */
22
+ providerDiverse: z.boolean().default(false),
23
+ /** Opt-in ASK (#192): a deadlocked panel emits `escalate` → the loop halts as
24
+ * escalated for a human, instead of auto-rejecting. Default off (byte-identical).
25
+ * Liveness tradeoff (#364): in an UNATTENDED loop there is no "next interaction",
26
+ * so `escalate` halts indefinitely — keep off for autonomous runs. */
27
+ escalateOnNoConsensus: z.boolean().default(false),
28
+ /** Opt-in precision-weighted voting (#369 Inc3): weight each ballot by the
29
+ * voter's measured calibration. Default off (byte-identical). */
30
+ precisionWeighted: z.boolean().default(false),
31
+ /** Opt-in correlation-aware aggregation (#369 Inc4): downweight voters that share
32
+ * a served model class by `correlationDiscount(form, K)`. Default off (byte-identical);
33
+ * inert on a single-adapter panel. Mirrors the cli overlay's VoteGateSchema. */
34
+ correlationAware: z.boolean().default(false),
35
+ /** The `correlationAware` discount form (#369 Inc4): `sqrt` ⇒ 1/√K, `linear` ⇒ 1/K. */
36
+ correlationForm: z.enum(['sqrt', 'linear']).default('sqrt'),
37
+ /** Distinct-class independence quorum (#405/#369 Inc5b): require ≥ this many distinct
38
+ * served model classes on a diverse panel else the vote ESCALATES to a human instead
39
+ * of ruling. Absent ⇒ defaults to 2 for a panel-7 RATIFICATION vote (the human-ratified
40
+ * default-on), off for a panel-3 loop vote; set to 1 to disable it on a ratification
41
+ * panel. Inert on a single-adapter / endpoint-only panel (no served identities). */
42
+ minDistinctClasses: z.number().int().min(0).optional(),
43
+ });
44
+ /** One node override (mirrors the cli overlay's NodeOverrideSchema). */
45
+ const NodeOverrideSchema = z.strictObject({
46
+ gate: z.string().min(1).optional(),
47
+ specialists: z.array(z.string().min(1)).optional(),
48
+ });
49
+ /** Engine configuration — see the module docs for the overlay mapping. */
50
+ export const EngineConfigSchema = z
51
+ .strictObject({
52
+ /** Vote-iterate bound: at most K rejected re-entries into plan (spec §6). */
53
+ K: z.number().int().min(1).default(3),
54
+ /**
55
+ * Child-iterate bound [CLM-0043]: at most Kc re-runs of a child's
56
+ * implement on a quality reject before the child escalates (spec §6, §8).
57
+ * Bounds child iteration in BOTH budget modes — unlimited budget is not
58
+ * unlimited iterations; raising Kc is how an overlay allows more.
59
+ */
60
+ Kc: z.number().int().min(1).default(3),
61
+ /**
62
+ * Honesty guard (CLM-0064): the review gate is advisory and does NOT drive
63
+ * child iteration. Default off; an overlay flips it on only when the review
64
+ * gate is promoted to enforce. We never claim review enforces iteration.
65
+ */
66
+ reviewDrivesIteration: z.boolean().default(false),
67
+ /**
68
+ * Does the parsimony gate drive child re-iteration this run (#9/#415)? The
69
+ * CLI computes this from the overlay's `gates.parsimony.intensity`: true at
70
+ * `full`/`ultra` (a rejecting parsimony verdict re-runs implement within Kc),
71
+ * false at `lite`/`off` (advisory or disabled). Default false ⇒ a fresh
72
+ * EngineConfig is byte-identical (the engine never drives parsimony unless the
73
+ * mapping flips it). The INTENSITY itself lives in the cli Overlay
74
+ * (`gates.parsimony`) and reaches the executor via `kern.config`; only this
75
+ * derived drive-flag the engine needs is mirrored here [CLM-0045].
76
+ */
77
+ parsimonyDrivesIteration: z.boolean().default(false),
78
+ /**
79
+ * Pre-node budget reserve floor as a FRACTION of the limit (#342): the
80
+ * pre-node guard halts before a node when remaining < max(this × limit,
81
+ * largest-node-seen). The floor covers COLD START (the first node, before any
82
+ * spend is observed). Default 0 — observed-max still caps steady-state
83
+ * overshoot; an overlay raises this to also bound the first node.
84
+ */
85
+ budgetHeadroomFraction: z.number().min(0).max(1).default(0),
86
+ gates: z.strictObject({ vote: VoteConfigSchema.prefault({}) }).prefault({}),
87
+ nodeOverrides: z.record(z.string().min(1), NodeOverrideSchema).default({}),
88
+ })
89
+ .prefault({});
90
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Engine error classification (split from engine.ts to keep it under the LOC
3
+ * budget, #58): the "kill" path vs an executor failure. Pure helpers the loop
4
+ * calls when a node throws.
5
+ */
6
+ import { WorkflowError } from './state.js';
7
+ /** True for AbortError throws and fired signals — the "kill" path [CLM-0044]. */
8
+ export declare function isAbort(error: unknown, signal?: AbortSignal): boolean;
9
+ /** Wrap a non-engine throw in a typed WorkflowError. */
10
+ export declare function asWorkflowError(error: unknown, node: string, signal?: AbortSignal): WorkflowError;
11
+ //# sourceMappingURL=engine-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-errors.d.ts","sourceRoot":"","sources":["../src/engine-errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAErE;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,aAAa,CASjG"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Engine error classification (split from engine.ts to keep it under the LOC
3
+ * budget, #58): the "kill" path vs an executor failure. Pure helpers the loop
4
+ * calls when a node throws.
5
+ */
6
+ import { WorkflowError } from './state.js';
7
+ /** True for AbortError throws and fired signals — the "kill" path [CLM-0044]. */
8
+ export function isAbort(error, signal) {
9
+ return signal?.aborted === true || (error instanceof Error && error.name === 'AbortError');
10
+ }
11
+ /** Wrap a non-engine throw in a typed WorkflowError. */
12
+ export function asWorkflowError(error, node, signal) {
13
+ if (error instanceof WorkflowError)
14
+ return error;
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ return isAbort(error, signal)
17
+ ? new WorkflowError('aborted', `run aborted at node "${node}": ${message}`, { node })
18
+ : new WorkflowError('executor_failed', `node "${node}" failed: ${message}`, {
19
+ node,
20
+ cause: error,
21
+ });
22
+ }
23
+ //# sourceMappingURL=engine-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-errors.js","sourceRoot":"","sources":["../src/engine-errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,iFAAiF;AACjF,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,MAAoB;IAC1D,OAAO,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AAC7F,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,IAAY,EAAE,MAAoB;IAChF,IAAI,KAAK,YAAY,aAAa;QAAE,OAAO,KAAK,CAAC;IACjD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;QAC3B,CAAC,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,wBAAwB,IAAI,MAAM,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACrF,CAAC,CAAC,IAAI,aAAa,CAAC,iBAAiB,EAAE,SAAS,IAAI,aAAa,OAAO,EAAE,EAAE;YACxE,IAAI;YACJ,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;AACT,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared scripted-executor harness for the engine tests (child iteration,
3
+ * budget, per-child spend). A deterministic stand-in for the real faculties:
4
+ * every node returns a canned contract so a test drives the loop's CONTROL
5
+ * flow (re-iterate, escalate, attribute) without any model call. Not test code
6
+ * itself — it carries no `describe`/`it`; the `.test.ts` files import it.
7
+ */
8
+ import type { Brief, Finding, Outcome, TaskContract, Verdict } from '@kernloop/contracts';
9
+ import type { NodeContext, NodeExecutor } from './engine.js';
10
+ /** The root task every engine test decomposes. */
11
+ export declare const task: TaskContract;
12
+ /** A scripted compiler Brief for `taskId`. */
13
+ export declare const brief: (taskId: string) => Brief;
14
+ /** A scripted gate Verdict; a non-pass result carries one finding to fold. */
15
+ export declare const verdict: (taskId: string, gate: string, result: Verdict["result"]) => Verdict;
16
+ /** A scripted success Outcome for `taskId`. */
17
+ export declare const outcome: (taskId: string) => Outcome;
18
+ /** Render a trace as `node[:childId]` labels for order assertions. */
19
+ export declare const names: (trace: readonly {
20
+ node: string;
21
+ childId?: string;
22
+ }[]) => string[];
23
+ /**
24
+ * A scripted executor set. `qualityByChild` maps a child id to the sequence of
25
+ * quality results it returns (last entry repeats). Records every implement's
26
+ * NodeContext so tests can assert the folded child findings + childIteration.
27
+ */
28
+ export declare function scripted(qualityByChild?: Record<string, Array<Verdict['result']>>): {
29
+ executors: Record<string, NodeExecutor>;
30
+ qualityCalls: Record<string, number>;
31
+ implementCtx: {
32
+ childId: string;
33
+ iteration: number;
34
+ findings: readonly Finding[];
35
+ }[];
36
+ };
37
+ /** Wrap executors to count calls per node name. */
38
+ export declare function counted(executors: Record<string, NodeExecutor>): {
39
+ executors: {
40
+ [k: string]: (input: unknown, ctx: NodeContext) => Promise<unknown>;
41
+ };
42
+ calls: Record<string, number>;
43
+ };
44
+ //# sourceMappingURL=engine-testkit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-testkit.d.ts","sourceRoot":"","sources":["../src/engine-testkit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7D,kDAAkD;AAClD,eAAO,MAAM,IAAI,EAAE,YASlB,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,KAAG,KAKrC,CAAC;AAEH,8EAA8E;AAC9E,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,QAAQ,OAAO,CAAC,QAAQ,CAAC,KAAG,OAUhF,CAAC;AAEH,+CAA+C;AAC/C,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,KAAG,OAOvC,CAAC;AAEH,sEAAsE;AACtE,eAAO,MAAM,KAAK,GAAI,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAG,MAAM,EACF,CAAC;AAElF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,cAAc,GAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAM;;;;iBAE/C,MAAM;mBAAa,MAAM;kBAAY,SAAS,OAAO,EAAE;;EAmC7F;AAED,mDAAmD;AACnD,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;;6BAKjD,OAAO,OAAO,WAAW;;;EAOtC"}
@@ -0,0 +1,93 @@
1
+ /** The root task every engine test decomposes. */
2
+ export const task = {
3
+ id: 'task-1',
4
+ goal: 'ship the feature',
5
+ constraints: [],
6
+ budget: { tokens: 1000, usd: 1, wallClockMin: 10 },
7
+ evidence: [],
8
+ definitionOfDone: [],
9
+ authorityCeiling: 'suggest',
10
+ overlay: 'repo',
11
+ };
12
+ /** A scripted compiler Brief for `taskId`. */
13
+ export const brief = (taskId) => ({
14
+ taskId,
15
+ sections: [],
16
+ budget: { allotted: 10, used: 0 },
17
+ compilerVersion: 'scripted-1',
18
+ });
19
+ /** A scripted gate Verdict; a non-pass result carries one finding to fold. */
20
+ export const verdict = (taskId, gate, result) => ({
21
+ taskId,
22
+ gate,
23
+ result,
24
+ confidence: 1,
25
+ findings: result === 'approve' || result === 'pass'
26
+ ? []
27
+ : [{ severity: 'error', message: `${gate} wants ${taskId} fixed` }],
28
+ cost: { tokens: 0, usd: 0 },
29
+ });
30
+ /** A scripted success Outcome for `taskId`. */
31
+ export const outcome = (taskId) => ({
32
+ taskId,
33
+ status: 'success',
34
+ signals: [],
35
+ cost: { tokens: 0, usd: 0 },
36
+ traceRef: `trace-${taskId}`,
37
+ distillCandidates: [],
38
+ });
39
+ /** Render a trace as `node[:childId]` labels for order assertions. */
40
+ export const names = (trace) => trace.map((t) => (t.childId === undefined ? t.node : `${t.node}:${t.childId}`));
41
+ /**
42
+ * A scripted executor set. `qualityByChild` maps a child id to the sequence of
43
+ * quality results it returns (last entry repeats). Records every implement's
44
+ * NodeContext so tests can assert the folded child findings + childIteration.
45
+ */
46
+ export function scripted(qualityByChild = {}) {
47
+ const qualityCalls = {};
48
+ const implementCtx = [];
49
+ const executors = {
50
+ frame: () => Promise.resolve(task),
51
+ research: () => Promise.resolve(brief(task.id)),
52
+ plan: () => Promise.resolve(brief(task.id)),
53
+ vote: (_i, ctx) => Promise.resolve(verdict(ctx.taskId, 'vote', 'approve')),
54
+ decompose: () => Promise.resolve([
55
+ { ...task, id: `${task.id}.c1`, parent: task.id },
56
+ { ...task, id: `${task.id}.c2`, parent: task.id },
57
+ ]),
58
+ implement: (input, ctx) => {
59
+ const c = input;
60
+ implementCtx.push({
61
+ childId: c.id,
62
+ iteration: ctx.childIteration ?? -1,
63
+ findings: ctx.findings,
64
+ });
65
+ return Promise.resolve(outcome(c.id));
66
+ },
67
+ quality: (_i, ctx) => {
68
+ const id = ctx.child?.id ?? ctx.taskId;
69
+ const seq = qualityByChild[id] ?? ['pass'];
70
+ const n = qualityCalls[id] ?? 0;
71
+ qualityCalls[id] = n + 1;
72
+ return Promise.resolve(verdict(id, 'quality', seq[Math.min(n, seq.length - 1)] ?? 'pass'));
73
+ },
74
+ review: (_i, ctx) => Promise.resolve(verdict(ctx.child?.id ?? ctx.taskId, 'review', 'approve')),
75
+ parsimony: (_i, ctx) => Promise.resolve(verdict(ctx.child?.id ?? ctx.taskId, 'parsimony', 'pass')),
76
+ integrate: () => Promise.resolve(outcome(task.id)),
77
+ retrospect: (input) => Promise.resolve(input),
78
+ };
79
+ return { executors, qualityCalls, implementCtx };
80
+ }
81
+ /** Wrap executors to count calls per node name. */
82
+ export function counted(executors) {
83
+ const calls = {};
84
+ const wrapped = Object.fromEntries(Object.entries(executors).map(([key, fn]) => [
85
+ key,
86
+ (input, ctx) => {
87
+ calls[key] = (calls[key] ?? 0) + 1;
88
+ return fn(input, ctx);
89
+ },
90
+ ]));
91
+ return { executors: wrapped, calls };
92
+ }
93
+ //# sourceMappingURL=engine-testkit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-testkit.js","sourceRoot":"","sources":["../src/engine-testkit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The execution engine's public type surface — the contracts a caller of
3
+ * `createEngine` touches: the per-node {@link NodeContext}, the injected
4
+ * {@link NodeExecutor}, the {@link EngineDeps} bag, {@link RunOptions}, and the
5
+ * {@link Engine} interface itself. Split out of engine.ts to keep that file under
6
+ * the 400-line ceiling (#58); engine.ts re-exports every name here, so consumers
7
+ * still import from `./engine.js` (or `@kernloop/workflows`) unchanged.
8
+ */
9
+ import type { Finding, TaskContract } from '@kernloop/contracts';
10
+ import type { CheckpointStore } from './checkpoints.js';
11
+ import type { RunResult } from './state.js';
12
+ import type { BudgetGuard, BudgetSpend } from './budget.js';
13
+ import type { ChildIterateEvent } from './child-iterate.js';
14
+ import type { EngineConfig, EngineConfigInput } from './config.js';
15
+ /** Per-invocation context handed to every node executor. */
16
+ export interface NodeContext {
17
+ readonly runId: string;
18
+ readonly taskId: string;
19
+ /** Vote-iterate cycle count: 0 on first entry, +1 per rejected re-entry. */
20
+ readonly iteration: number;
21
+ readonly config: EngineConfig;
22
+ /** The node being executed (resolved gate executors see the gate node). */
23
+ readonly node: string;
24
+ /**
25
+ * Findings the executing node must address: the run-level vote-iterate
26
+ * findings on the main chain; inside the fan-out the CHILD's accumulated gate
27
+ * findings — what the re-running coder must fix [CLM-0043].
28
+ */
29
+ readonly findings: readonly Finding[];
30
+ /** The child contract, inside the fan-out sub-chain. */
31
+ readonly child?: TaskContract;
32
+ /** This child's actor-critic iteration (0 on first implement), inside the fan-out. */
33
+ readonly childIteration?: number;
34
+ readonly signal?: AbortSignal;
35
+ }
36
+ /** An injected unit of work: consumes the edge value, emits the declared contract. */
37
+ export type NodeExecutor = (input: unknown, ctx: NodeContext) => Promise<unknown>;
38
+ /** What `createEngine` needs. Executors are keyed by node name, node kind, or gate name. */
39
+ export interface EngineDeps {
40
+ readonly executors: Readonly<Record<string, NodeExecutor>>;
41
+ readonly checkpoints: CheckpointStore;
42
+ readonly config?: EngineConfigInput;
43
+ /**
44
+ * Runtime budget guard (spec §8) [CLM-0077]. Absent → no budget enforcement
45
+ * (Kc still bounds child iteration). In `enforce` mode the run escalates when
46
+ * metered spend exceeds the parent budget; `unlimited` never halts but spend
47
+ * is still tracked. Injected by the CLI from the mode + parent budget + live
48
+ * `totals`; workflows imports no kernel, so `spent()` is a plain seam.
49
+ */
50
+ readonly budget?: BudgetGuard;
51
+ /**
52
+ * Always-on metered-spend readout for PER-CHILD attribution (#56) — the raw
53
+ * run-global meter the engine snapshots at each child boundary to slice spend
54
+ * per child (see {@link ChildSpendTracker}), in BOTH budget modes. Absent → no
55
+ * per-child attribution or halt. The CLI wires it to the budget guard's
56
+ * `totals`; workflows imports no kernel, so this is a plain seam.
57
+ */
58
+ readonly meteredSpend?: () => BudgetSpend;
59
+ /**
60
+ * Audit hook fired on each child re-iteration [CLM-0043] — the CLI appends a
61
+ * `loop.child.iterate` event to the hash chain so the refine history is
62
+ * recorded (the Observer can later read iterations-to-pass as fitness).
63
+ */
64
+ readonly onChildIterate?: (event: ChildIterateEvent) => void;
65
+ }
66
+ /** Per-run options. */
67
+ export interface RunOptions {
68
+ /** Caller-chosen run id (defaults to a UUID). */
69
+ readonly runId?: string;
70
+ /** Abort signal: an aborted run halts mid-node, last checkpoint intact. */
71
+ readonly signal?: AbortSignal;
72
+ }
73
+ /** The engine: run the canonical loop, or resume a checkpointed run. */
74
+ export interface Engine {
75
+ run(task: TaskContract, options?: RunOptions): Promise<RunResult>;
76
+ resume(runId: string, options?: Pick<RunOptions, 'signal'>): Promise<RunResult>;
77
+ }
78
+ //# sourceMappingURL=engine-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-types.d.ts","sourceRoot":"","sources":["../src/engine-types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnE,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IACtC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,sFAAsF;AACtF,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAElF,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,WAAW,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC9D;AAED,uBAAuB;AACvB,MAAM,WAAW,UAAU;IACzB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,wEAAwE;AACxE,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACjF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=engine-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-types.js","sourceRoot":"","sources":["../src/engine-types.ts"],"names":[],"mappings":""}