@xemahq/kernel-contracts 0.2.1 → 0.2.3

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 (147) hide show
  1. package/dist/agent-workspace/awp-spec.json +1 -1
  2. package/dist/biome/index.d.ts +5 -0
  3. package/dist/biome/index.d.ts.map +1 -1
  4. package/dist/biome/index.js +5 -0
  5. package/dist/biome/index.js.map +1 -1
  6. package/dist/biome/lib/biome-audience.d.ts +8 -0
  7. package/dist/biome/lib/biome-audience.d.ts.map +1 -0
  8. package/dist/biome/lib/biome-audience.js +12 -0
  9. package/dist/biome/lib/biome-audience.js.map +1 -0
  10. package/dist/biome/lib/biome-availability-grant.d.ts +10 -0
  11. package/dist/biome/lib/biome-availability-grant.d.ts.map +1 -0
  12. package/dist/biome/lib/biome-availability-grant.js +12 -0
  13. package/dist/biome/lib/biome-availability-grant.js.map +1 -0
  14. package/dist/biome/lib/biome-origin.d.ts +8 -0
  15. package/dist/biome/lib/biome-origin.d.ts.map +1 -0
  16. package/dist/biome/lib/biome-origin.js +12 -0
  17. package/dist/biome/lib/biome-origin.js.map +1 -0
  18. package/dist/biome/lib/biome-target.d.ts +7 -0
  19. package/dist/biome/lib/biome-target.d.ts.map +1 -0
  20. package/dist/biome/lib/biome-target.js +11 -0
  21. package/dist/biome/lib/biome-target.js.map +1 -0
  22. package/dist/biome/lib/biome-tier.d.ts +10 -0
  23. package/dist/biome/lib/biome-tier.d.ts.map +1 -0
  24. package/dist/biome/lib/biome-tier.js +19 -0
  25. package/dist/biome/lib/biome-tier.js.map +1 -0
  26. package/dist/contribution/lib/contribution-kind.d.ts +2 -1
  27. package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
  28. package/dist/contribution/lib/contribution-kind.js +1 -0
  29. package/dist/contribution/lib/contribution-kind.js.map +1 -1
  30. package/dist/distribution/index.d.ts +5 -0
  31. package/dist/distribution/index.d.ts.map +1 -0
  32. package/dist/distribution/index.js +21 -0
  33. package/dist/distribution/index.js.map +1 -0
  34. package/dist/distribution/lib/distribution-lock.d.ts +31 -0
  35. package/dist/distribution/lib/distribution-lock.d.ts.map +1 -0
  36. package/dist/distribution/lib/distribution-lock.js +40 -0
  37. package/dist/distribution/lib/distribution-lock.js.map +1 -0
  38. package/dist/distribution/lib/distribution-selector.d.ts +12 -0
  39. package/dist/distribution/lib/distribution-selector.d.ts.map +1 -0
  40. package/dist/distribution/lib/distribution-selector.js +13 -0
  41. package/dist/distribution/lib/distribution-selector.js.map +1 -0
  42. package/dist/distribution/lib/distribution.d.ts +27 -0
  43. package/dist/distribution/lib/distribution.d.ts.map +1 -0
  44. package/dist/distribution/lib/distribution.js +35 -0
  45. package/dist/distribution/lib/distribution.js.map +1 -0
  46. package/dist/distribution/lib/image-lock.d.ts +27 -0
  47. package/dist/distribution/lib/image-lock.d.ts.map +1 -0
  48. package/dist/distribution/lib/image-lock.js +36 -0
  49. package/dist/distribution/lib/image-lock.js.map +1 -0
  50. package/dist/inquiry/lib/enums.d.ts +4 -0
  51. package/dist/inquiry/lib/enums.d.ts.map +1 -1
  52. package/dist/inquiry/lib/enums.js +6 -1
  53. package/dist/inquiry/lib/enums.js.map +1 -1
  54. package/dist/inquiry/lib/inquiry.d.ts +5 -9
  55. package/dist/inquiry/lib/inquiry.d.ts.map +1 -1
  56. package/dist/inquiry/lib/inquiry.js +4 -2
  57. package/dist/inquiry/lib/inquiry.js.map +1 -1
  58. package/dist/invocation/index.d.ts +10 -0
  59. package/dist/invocation/index.d.ts.map +1 -0
  60. package/dist/invocation/index.js +26 -0
  61. package/dist/invocation/index.js.map +1 -0
  62. package/dist/invocation/lib/execution-requirements.d.ts +24 -0
  63. package/dist/invocation/lib/execution-requirements.d.ts.map +1 -0
  64. package/dist/invocation/lib/execution-requirements.js +25 -0
  65. package/dist/invocation/lib/execution-requirements.js.map +1 -0
  66. package/dist/invocation/lib/invocation-decision.d.ts +8 -0
  67. package/dist/invocation/lib/invocation-decision.d.ts.map +1 -0
  68. package/dist/invocation/lib/invocation-decision.js +10 -0
  69. package/dist/invocation/lib/invocation-decision.js.map +1 -0
  70. package/dist/invocation/lib/invocation-mode.d.ts +8 -0
  71. package/dist/invocation/lib/invocation-mode.d.ts.map +1 -0
  72. package/dist/invocation/lib/invocation-mode.js +12 -0
  73. package/dist/invocation/lib/invocation-mode.js.map +1 -0
  74. package/dist/invocation/lib/invocation-priority.d.ts +8 -0
  75. package/dist/invocation/lib/invocation-priority.d.ts.map +1 -0
  76. package/dist/invocation/lib/invocation-priority.js +12 -0
  77. package/dist/invocation/lib/invocation-priority.js.map +1 -0
  78. package/dist/invocation/lib/invocation-record.d.ts +27 -0
  79. package/dist/invocation/lib/invocation-record.d.ts.map +1 -0
  80. package/dist/invocation/lib/invocation-record.js +29 -0
  81. package/dist/invocation/lib/invocation-record.js.map +1 -0
  82. package/dist/invocation/lib/invocation-status.d.ts +13 -0
  83. package/dist/invocation/lib/invocation-status.d.ts.map +1 -0
  84. package/dist/invocation/lib/invocation-status.js +17 -0
  85. package/dist/invocation/lib/invocation-status.js.map +1 -0
  86. package/dist/invocation/lib/invoke-request.d.ts +13 -0
  87. package/dist/invocation/lib/invoke-request.d.ts.map +1 -0
  88. package/dist/invocation/lib/invoke-request.js +15 -0
  89. package/dist/invocation/lib/invoke-request.js.map +1 -0
  90. package/dist/invocation/lib/invoke-response.d.ts +17 -0
  91. package/dist/invocation/lib/invoke-response.d.ts.map +1 -0
  92. package/dist/invocation/lib/invoke-response.js +19 -0
  93. package/dist/invocation/lib/invoke-response.js.map +1 -0
  94. package/dist/invocation/lib/isolation-level.d.ts +7 -0
  95. package/dist/invocation/lib/isolation-level.d.ts.map +1 -0
  96. package/dist/invocation/lib/isolation-level.js +11 -0
  97. package/dist/invocation/lib/isolation-level.js.map +1 -0
  98. package/dist/mail-source/index.d.ts +2 -0
  99. package/dist/mail-source/index.d.ts.map +1 -0
  100. package/dist/mail-source/index.js +18 -0
  101. package/dist/mail-source/index.js.map +1 -0
  102. package/dist/mail-source/lib/mail-event.d.ts +58 -0
  103. package/dist/mail-source/lib/mail-event.d.ts.map +1 -0
  104. package/dist/mail-source/lib/mail-event.js +41 -0
  105. package/dist/mail-source/lib/mail-event.js.map +1 -0
  106. package/dist/policy/index.d.ts +1 -0
  107. package/dist/policy/index.d.ts.map +1 -1
  108. package/dist/policy/index.js +1 -0
  109. package/dist/policy/index.js.map +1 -1
  110. package/dist/policy/lib/egress-allowlist.d.ts +18 -0
  111. package/dist/policy/lib/egress-allowlist.d.ts.map +1 -0
  112. package/dist/policy/lib/egress-allowlist.js +130 -0
  113. package/dist/policy/lib/egress-allowlist.js.map +1 -0
  114. package/dist/policy/lib/obligations.d.ts +12 -1
  115. package/dist/policy/lib/obligations.d.ts.map +1 -1
  116. package/dist/policy/lib/obligations.js +14 -1
  117. package/dist/policy/lib/obligations.js.map +1 -1
  118. package/package.json +1 -1
  119. package/src/biome/index.ts +5 -0
  120. package/src/biome/lib/biome-audience.ts +27 -0
  121. package/src/biome/lib/biome-availability-grant.ts +42 -0
  122. package/src/biome/lib/biome-origin.ts +26 -0
  123. package/src/biome/lib/biome-target.ts +16 -0
  124. package/src/biome/lib/biome-tier.ts +44 -0
  125. package/src/contribution/lib/contribution-kind.ts +14 -0
  126. package/src/distribution/index.ts +4 -0
  127. package/src/distribution/lib/distribution-lock.ts +120 -0
  128. package/src/distribution/lib/distribution-selector.ts +35 -0
  129. package/src/distribution/lib/distribution.ts +108 -0
  130. package/src/distribution/lib/image-lock.ts +102 -0
  131. package/src/inquiry/lib/enums.ts +13 -0
  132. package/src/inquiry/lib/inquiry.ts +22 -4
  133. package/src/invocation/index.ts +9 -0
  134. package/src/invocation/lib/execution-requirements.ts +61 -0
  135. package/src/invocation/lib/invocation-decision.ts +21 -0
  136. package/src/invocation/lib/invocation-mode.ts +26 -0
  137. package/src/invocation/lib/invocation-priority.ts +16 -0
  138. package/src/invocation/lib/invocation-record.ts +59 -0
  139. package/src/invocation/lib/invocation-status.ts +32 -0
  140. package/src/invocation/lib/invoke-request.ts +34 -0
  141. package/src/invocation/lib/invoke-response.ts +39 -0
  142. package/src/invocation/lib/isolation-level.ts +20 -0
  143. package/src/mail-source/index.ts +1 -0
  144. package/src/mail-source/lib/mail-event.ts +106 -0
  145. package/src/policy/index.ts +1 -0
  146. package/src/policy/lib/egress-allowlist.ts +239 -0
  147. package/src/policy/lib/obligations.ts +61 -0
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ BiomeTierSchema,
4
+ BiomeTargetSchema,
5
+ BiomeOriginSchema,
6
+ type BiomeTier,
7
+ type BiomeTarget,
8
+ type BiomeOrigin,
9
+ } from '../../biome';
10
+
11
+ /**
12
+ * A single biome as resolved into a distribution lockfile. Version-pinned and
13
+ * (once images are built) content-addressed. `imageDigest` is populated by the
14
+ * image-manifest step (`images.lock.json`) and may be absent in the pure biome
15
+ * lockfile before images are built.
16
+ */
17
+ export interface LockedBiome {
18
+ id: string;
19
+ version: string;
20
+ tier: BiomeTier;
21
+ target: BiomeTarget;
22
+ /** Provenance posture from the manifest (gated by the distribution trustPolicy). */
23
+ origin: BiomeOrigin;
24
+ /** Whether the org may disable this biome once shipped (cannot-disable). */
25
+ mandatory: boolean;
26
+ /** Source repo + path the biome was resolved from (provenance for the build). */
27
+ repo: string;
28
+ path: string;
29
+ /**
30
+ * Deployable service names this biome ships (from the manifest `ships.apis[].name`).
31
+ * Empty for biomes with no deployable API (pure skill/agent bundles) and for
32
+ * web biomes (which bundle into the host shell, not their own container). This
33
+ * is what the deployment-roster generator reads — it runs in a standalone
34
+ * checkout without the biome sources, so the biome→service mapping must travel
35
+ * inside the lockfile, never be recomputed downstream.
36
+ */
37
+ services: readonly string[];
38
+ /** Set by the image-manifest resolver; `image@sha256:<digest>`. */
39
+ imageDigest?: string;
40
+ }
41
+
42
+ export const LockedBiomeSchema = z.object({
43
+ id: z.string().min(1),
44
+ version: z.string().min(1),
45
+ tier: BiomeTierSchema,
46
+ target: BiomeTargetSchema,
47
+ origin: BiomeOriginSchema,
48
+ mandatory: z.boolean(),
49
+ repo: z.string().min(1),
50
+ path: z.string().min(1),
51
+ services: z.array(z.string().min(1)).readonly(),
52
+ imageDigest: z.string().min(1).optional(),
53
+ }) as z.ZodType<LockedBiome>;
54
+
55
+ /**
56
+ * A platform-substrate service that is part of a distribution but is NOT a biome
57
+ * — the deployment "init system" (kernel server, host shell, opencode worker
58
+ * plane, docs). These always ship with any Xema deployment that includes the
59
+ * base layer; they are declared once on the `core` distribution and inherited
60
+ * via `extends`. Carried into the lock so the roster generator sees the full
61
+ * service set (biome services ∪ platform services) without any hardcoded floor.
62
+ */
63
+ export interface LockedPlatformService {
64
+ name: string;
65
+ /** Wave/boot tier — drives ordering in the generated bring-up plan. */
66
+ tier: BiomeTier | 'edge';
67
+ }
68
+
69
+ export const LockedPlatformServiceSchema = z.object({
70
+ name: z.string().min(1),
71
+ tier: z.union([BiomeTierSchema, z.literal('edge')]),
72
+ }) as z.ZodType<LockedPlatformService>;
73
+
74
+ /**
75
+ * `DistributionLock` — the deterministic, resolved output of a `Distribution`.
76
+ * THE single artifact every consumer reads (deploy generator, test-suite
77
+ * mapping, biome-host boot filter, appliance build). No consumer re-applies the
78
+ * distribution rules — the lockfile is the hard boundary.
79
+ *
80
+ * `resolvedAt` / `inputHash` are stamped by the resolver tooling, not the
81
+ * kernel (kernel contracts hold no clock); both optional so the type is usable
82
+ * mid-resolution.
83
+ */
84
+ export interface DistributionLock {
85
+ schemaVersion: 1;
86
+ distributionId: string;
87
+ biomes: readonly LockedBiome[];
88
+ /** Non-biome substrate services that ship with this distribution (the deploy "init system"). */
89
+ platformServices: readonly LockedPlatformService[];
90
+ /** ISO-8601 timestamp the resolver stamped (tooling-supplied). */
91
+ resolvedAt?: string;
92
+ /** Hash of the resolver inputs (manifest + index) for drift detection. */
93
+ inputHash?: string;
94
+ }
95
+
96
+ export const DistributionLockSchema = z.object({
97
+ schemaVersion: z.literal(1),
98
+ distributionId: z.string().min(1),
99
+ biomes: z.array(LockedBiomeSchema).readonly(),
100
+ platformServices: z.array(LockedPlatformServiceSchema).readonly(),
101
+ resolvedAt: z.string().min(1).optional(),
102
+ inputHash: z.string().min(1).optional(),
103
+ }) as z.ZodType<DistributionLock>;
104
+
105
+ /**
106
+ * Parse + validate a raw `distribution.lock.json`. Fail-fast on any shape error
107
+ * — the lockfile is the hard boundary every consumer reads, so a malformed lock
108
+ * must never be silently tolerated.
109
+ */
110
+ export function parseDistributionLock(raw: unknown): DistributionLock {
111
+ const result = DistributionLockSchema.safeParse(raw);
112
+ if (!result.success) {
113
+ throw new Error(
114
+ `Invalid distribution.lock.json: ${result.error.issues
115
+ .map((i) => `[${i.path.join('.')}] ${i.message}`)
116
+ .join('; ')}`,
117
+ );
118
+ }
119
+ return result.data;
120
+ }
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ BiomeTierSchema,
4
+ BiomeOriginSchema,
5
+ type BiomeTier,
6
+ type BiomeOrigin,
7
+ } from '../../biome';
8
+
9
+ /**
10
+ * A `DistributionSelector` declares which biomes an `include[]` entry pulls
11
+ * into a distribution. Exactly one of the three forms:
12
+ *
13
+ * - `{ tier }` — every biome of that dependency tier (maps to the manifest
14
+ * `xema.scope` field). NEVER pulls `audience: operator`
15
+ * biomes — those require explicit `{ biome }` naming.
16
+ * - `{ origin }` — every biome of that provenance posture (first/third-party).
17
+ * - `{ biome }` — one biome by id, optional semver `version` range. This is
18
+ * the ONLY form that can pull an `audience: operator` biome
19
+ * (the "force it in" mechanism for our internal distribution).
20
+ *
21
+ * Selector matching is resolved against the global biome index; the resolver,
22
+ * not any consumer, applies these rules (the lockfile is the hard boundary).
23
+ */
24
+ export type DistributionSelector =
25
+ | { tier: BiomeTier }
26
+ | { origin: BiomeOrigin }
27
+ | { biome: string; version?: string };
28
+
29
+ export const DistributionSelectorSchema = z.union([
30
+ z.object({ tier: BiomeTierSchema }).strict(),
31
+ z.object({ origin: BiomeOriginSchema }).strict(),
32
+ z
33
+ .object({ biome: z.string().min(1), version: z.string().min(1).optional() })
34
+ .strict(),
35
+ ]) as z.ZodType<DistributionSelector>;
@@ -0,0 +1,108 @@
1
+ import { z } from 'zod';
2
+ import { BiomeOriginSchema, type BiomeOrigin } from '../../biome';
3
+ import {
4
+ DistributionSelectorSchema,
5
+ type DistributionSelector,
6
+ } from './distribution-selector';
7
+
8
+ /**
9
+ * Schema-versioning seed for `xema-distribution.json`. Bumped via a coordinated
10
+ * PR on an incompatible shape change; consumers refuse versions they do not
11
+ * understand (fail-fast — no best-effort parse).
12
+ */
13
+ export type DistributionSchemaVersion = 1;
14
+
15
+ /**
16
+ * Trust policy of a distribution — gates which provenance postures may ship.
17
+ * E.g. a locked appliance distribution allows only `first-party`. Gates on
18
+ * `BiomeOrigin` (the posture carried on the manifest at build time), NOT on
19
+ * the richer install-time `BiomeTrustTier`.
20
+ */
21
+ export interface DistributionTrustPolicy {
22
+ allow: readonly BiomeOrigin[];
23
+ }
24
+
25
+ export const DistributionTrustPolicySchema = z.object({
26
+ allow: z.array(BiomeOriginSchema).readonly(),
27
+ }) as z.ZodType<DistributionTrustPolicy>;
28
+
29
+ /**
30
+ * A platform-substrate service a distribution ships that is NOT a biome — the
31
+ * deployment "init system" (kernel server, host shell, opencode worker plane,
32
+ * docs). Declared once on the floor distribution (`core`) and inherited via
33
+ * `extends`. These never go through biome selectors (they are not biomes); they
34
+ * are carried verbatim into the lock so the deployment-roster generator sees the
35
+ * full service set without any hardcoded floor in the generator.
36
+ */
37
+ export interface DistributionPlatformService {
38
+ name: string;
39
+ /** Wave/boot tier — drives ordering in the generated bring-up plan. */
40
+ tier: 'kernel' | 'system' | 'base' | 'platform' | 'edge';
41
+ }
42
+
43
+ export const DistributionPlatformServiceSchema = z.object({
44
+ name: z.string().min(1),
45
+ tier: z.enum(['kernel', 'system', 'base', 'platform', 'edge']),
46
+ }) as z.ZodType<DistributionPlatformService>;
47
+
48
+ /**
49
+ * `Distribution` — the top-level shape of `xema-distribution.json`: the
50
+ * PACKAGING primitive that declares which biomes a given Xema build ships.
51
+ *
52
+ * This is orthogonal to a biome's dependency tier (`BiomeTier`) and to where it
53
+ * is deployed (the deployment plane). It contains PACKAGING fields only — no
54
+ * infra/deploy/values config (that lives in the deployment overlay).
55
+ *
56
+ * Composition: `extends` names a parent distribution; the resolver composes
57
+ * parent → child, with the child's `include`/`exclude` overriding. The resolver
58
+ * enforces (fail-fast): kernel-tier biomes are always included and cannot be
59
+ * excluded; an included biome whose `requires` are absent is an error; selectors
60
+ * never pull `audience: operator` biomes. The deterministic resolved output is a
61
+ * `DistributionLock` — the single artifact every consumer reads.
62
+ */
63
+ export interface Distribution {
64
+ schemaVersion: DistributionSchemaVersion;
65
+ id: string;
66
+ displayName: string;
67
+ description?: string;
68
+ /** Parent distribution id this one composes onto. */
69
+ extends?: string;
70
+ include: readonly DistributionSelector[];
71
+ /** Biome ids to remove after composition. Cannot remove a kernel-tier biome. */
72
+ exclude?: readonly string[];
73
+ trustPolicy?: DistributionTrustPolicy;
74
+ /**
75
+ * Non-biome substrate services this distribution ships (the deploy "init
76
+ * system"). Composed parent→child like `include` (concatenated, de-duped by
77
+ * name with the child winning on tier). Usually declared only on `core`.
78
+ */
79
+ platformServices?: readonly DistributionPlatformService[];
80
+ }
81
+
82
+ export const DistributionSchema = z.object({
83
+ schemaVersion: z.literal(1),
84
+ id: z.string().min(1),
85
+ displayName: z.string().min(1),
86
+ description: z.string().min(1).optional(),
87
+ extends: z.string().min(1).optional(),
88
+ include: z.array(DistributionSelectorSchema).readonly(),
89
+ exclude: z.array(z.string().min(1)).readonly().optional(),
90
+ trustPolicy: DistributionTrustPolicySchema.optional(),
91
+ platformServices: z.array(DistributionPlatformServiceSchema).readonly().optional(),
92
+ }) as z.ZodType<Distribution>;
93
+
94
+ /**
95
+ * Parse + validate a raw `xema-distribution.json`. Fail-fast on any shape error
96
+ * — never best-effort. Mirrors `parseBiomeManifest`.
97
+ */
98
+ export function parseDistribution(raw: unknown): Distribution {
99
+ const result = DistributionSchema.safeParse(raw);
100
+ if (!result.success) {
101
+ throw new Error(
102
+ `Invalid xema-distribution.json: ${result.error.issues
103
+ .map((i) => `[${i.path.join('.')}] ${i.message}`)
104
+ .join('; ')}`,
105
+ );
106
+ }
107
+ return result.data;
108
+ }
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod';
2
+ import { BiomeTargetSchema, type BiomeTarget } from '../../biome';
3
+
4
+ /**
5
+ * A single deployable image as resolved by the image-manifest step
6
+ * (`images.lock.json`). Images are CONTENT-ADDRESSED: `contentHash` is a
7
+ * deterministic sha256 over the biome's source directory tree, so the SAME
8
+ * biome source produces the SAME hash (and therefore the same image tag)
9
+ * across every customer/distribution. This is the delta key — CI builds an
10
+ * image only when its `contentHash` is not already present in the registry.
11
+ *
12
+ * `imageRef` is the untagged repository reference; the build-time tag is
13
+ * `${imageRef}:${contentHash}`.
14
+ */
15
+ export interface LockedImage {
16
+ /** The biome this image is built from. */
17
+ biomeId: string;
18
+ /** Runtime surface — only `server` biomes produce a container image. */
19
+ target: BiomeTarget;
20
+ /** Untagged image repository reference, e.g. `ghcr.io/xema-dev/<biomeId>`. */
21
+ imageRef: string;
22
+ /** sha256 over the biome source tree — identical source ⇒ identical hash. */
23
+ contentHash: string;
24
+ /** Source repo segment the biome was resolved from (provenance for the build). */
25
+ repo: string;
26
+ /** Source directory path (repo-root-relative) the biome was resolved from. */
27
+ path: string;
28
+ }
29
+
30
+ export const LockedImageSchema = z.object({
31
+ biomeId: z.string().min(1),
32
+ target: BiomeTargetSchema,
33
+ imageRef: z.string().min(1),
34
+ contentHash: z.string().min(1),
35
+ repo: z.string().min(1),
36
+ path: z.string().min(1),
37
+ }) as z.ZodType<LockedImage>;
38
+
39
+ /**
40
+ * A web biome that ships in a distribution. Web biomes do NOT produce their own
41
+ * container image — they are static frontend bundles compiled INTO the single
42
+ * host-shell image (`xema-host-web`). They are listed here explicitly (never
43
+ * silently dropped) so the host-shell build knows exactly which web biomes to
44
+ * bundle for this distribution: the lockfile is the join key for the frontend
45
+ * too, not just the backend.
46
+ */
47
+ export interface LockedWebBiome {
48
+ biomeId: string;
49
+ /** Source repo segment the web biome was resolved from. */
50
+ repo: string;
51
+ /** Source directory path (repo-root-relative) the web biome was resolved from. */
52
+ path: string;
53
+ }
54
+
55
+ export const LockedWebBiomeSchema = z.object({
56
+ biomeId: z.string().min(1),
57
+ repo: z.string().min(1),
58
+ path: z.string().min(1),
59
+ }) as z.ZodType<LockedWebBiome>;
60
+
61
+ /**
62
+ * `ImageLock` — the deterministic, content-addressed image manifest for a
63
+ * distribution. Resolved from `distribution.lock.json` by the image-manifest
64
+ * tooling; the delta-build planner reads it to decide which images are NEW vs
65
+ * already-built. Like `DistributionLock`, `resolvedAt` is stamped by the
66
+ * tooling (kernel contracts hold no clock) and is optional so the type is
67
+ * usable mid-resolution.
68
+ */
69
+ export interface ImageLock {
70
+ schemaVersion: 1;
71
+ distributionId: string;
72
+ images: readonly LockedImage[];
73
+ /** Web biomes bundled into the host-shell image (no own container). */
74
+ webBiomes: readonly LockedWebBiome[];
75
+ /** ISO-8601 timestamp the resolver stamped (tooling-supplied). */
76
+ resolvedAt?: string;
77
+ }
78
+
79
+ export const ImageLockSchema = z.object({
80
+ schemaVersion: z.literal(1),
81
+ distributionId: z.string().min(1),
82
+ images: z.array(LockedImageSchema).readonly(),
83
+ webBiomes: z.array(LockedWebBiomeSchema).readonly(),
84
+ resolvedAt: z.string().min(1).optional(),
85
+ }) as z.ZodType<ImageLock>;
86
+
87
+ /**
88
+ * Parse + validate a raw `images.lock.json`. Fail-fast on any shape error —
89
+ * the image lock is the hard boundary the delta-build planner reads, so a
90
+ * malformed lock must never be silently tolerated.
91
+ */
92
+ export function parseImageLock(raw: unknown): ImageLock {
93
+ const result = ImageLockSchema.safeParse(raw);
94
+ if (!result.success) {
95
+ throw new Error(
96
+ `Invalid images.lock.json: ${result.error.issues
97
+ .map((i) => `[${i.path.join('.')}] ${i.message}`)
98
+ .join('; ')}`,
99
+ );
100
+ }
101
+ return result.data;
102
+ }
@@ -101,3 +101,16 @@ export enum InquiryTimeoutAction {
101
101
  APPROVE = 'approve',
102
102
  ESCALATE = 'escalate',
103
103
  }
104
+
105
+ /**
106
+ * Terminal action when an inquiry's `escalationChain` is exhausted (the
107
+ * final step's deadline passed with no terminal verdict). A deliberately
108
+ * NARROWER closed set than `InquiryTimeoutAction`: `ESCALATE` is not
109
+ * representable here — escalating past the last chain step would be an
110
+ * infinite loop. Defaults to `REJECT` so a misconfigured chain never
111
+ * silently approves.
112
+ */
113
+ export enum EscalationExhaustionAction {
114
+ REJECT = 'reject',
115
+ APPROVE = 'approve',
116
+ }
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  import {
4
+ EscalationExhaustionAction,
4
5
  InquiryKind,
5
6
  InquiryRecipientStatus,
6
7
  InquiryStatus,
@@ -51,6 +52,17 @@ export const InquirySchema = z.object({
51
52
  workflowRunId: z.string().min(1),
52
53
  jobRunId: z.string().min(1),
53
54
  kind: z.nativeEnum(InquiryKind),
55
+ /**
56
+ * Optional domain approval-card discriminator. Distinct from the
57
+ * structural `kind` (which selects policy + gate semantics): an
58
+ * `inquiryKind` selects a biome-contributed approval-card variant
59
+ * (custom fields + verdict shape) on the FE and a domain descriptor on
60
+ * the engine. `null`/absent = the generic card for the structural
61
+ * `kind`, which is a valid state — never a silent error. When present,
62
+ * the value MUST match a `ContributionKind.InquiryKind` descriptor
63
+ * registered with the engine (validated fail-fast at create time).
64
+ */
65
+ inquiryKind: z.string().min(1).nullable(),
54
66
  title: z.string(),
55
67
  prompt: z.record(z.string(), z.unknown()),
56
68
  policy: ReplyPolicySchema,
@@ -66,10 +78,10 @@ export const InquirySchema = z.object({
66
78
  /**
67
79
  * Action when the escalation chain is exhausted (i.e. the final
68
80
  * step's deadline passes with no terminal verdict). Closed set:
69
- * `REJECT` (default) or `APPROVE`. `ESCALATE` is rejected here at
70
- * the schema level would be a runtime infinite loop.
81
+ * `REJECT` (default) or `APPROVE`. `ESCALATE` is not representable
82
+ * escalating past the last chain step would be a runtime infinite loop.
71
83
  */
72
- onEscalationExhaustion: z.enum(['reject', 'approve']),
84
+ onEscalationExhaustion: z.nativeEnum(EscalationExhaustionAction),
73
85
  /**
74
86
  * Zero-based pointer into `escalationChain`. Bumped each time the
75
87
  * engine advances. `0` means "still on the original recipients";
@@ -120,6 +132,12 @@ export const CreateInquiryInputSchema = z
120
132
  workflowRunId: z.string().min(1),
121
133
  jobRunId: z.string().min(1),
122
134
  kind: z.nativeEnum(InquiryKind),
135
+ /**
136
+ * Optional domain approval-card discriminator (see `InquirySchema`).
137
+ * Omitted = generic card for the structural `kind`. When present, the
138
+ * engine refuses an unregistered value fail-fast.
139
+ */
140
+ inquiryKind: z.string().min(1).optional(),
123
141
  title: z.string(),
124
142
  prompt: z.record(z.string(), z.unknown()),
125
143
  policy: ReplyPolicySchema,
@@ -127,7 +145,7 @@ export const CreateInquiryInputSchema = z
127
145
  expiresAtIso: z.string(),
128
146
  onTimeout: z.nativeEnum(InquiryTimeoutAction),
129
147
  escalationChain: z.array(EscalationStepSchema).optional(),
130
- onEscalationExhaustion: z.enum(['reject', 'approve']).optional(),
148
+ onEscalationExhaustion: z.nativeEnum(EscalationExhaustionAction).optional(),
131
149
  })
132
150
  .superRefine((value, ctx) => {
133
151
  // ESCALATE without a chain is meaningless — would have to fall back
@@ -0,0 +1,9 @@
1
+ export * from './lib/invocation-mode';
2
+ export * from './lib/isolation-level';
3
+ export * from './lib/invocation-status';
4
+ export * from './lib/invocation-priority';
5
+ export * from './lib/execution-requirements';
6
+ export * from './lib/invocation-decision';
7
+ export * from './lib/invoke-request';
8
+ export * from './lib/invoke-response';
9
+ export * from './lib/invocation-record';
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ import { InvocationMode, InvocationModeSchema } from './invocation-mode';
3
+ import { IsolationLevel, IsolationLevelSchema } from './isolation-level';
4
+ import {
5
+ InvocationPriority,
6
+ InvocationPrioritySchema,
7
+ } from './invocation-priority';
8
+
9
+ /**
10
+ * Per-invocation hard limits. None are required at the contract level; an
11
+ * absent field means "no caller-declared limit on this axis" (the platform may
12
+ * still apply its own). These map directly onto the existing `composition.limits`
13
+ * consumed by `CompositionBudgetEnforcer` — no new enforcement engine.
14
+ */
15
+ export interface InvocationLimits {
16
+ readonly maxDurationMs?: number;
17
+ readonly maxTokens?: number;
18
+ readonly maxToolCalls?: number;
19
+ readonly maxRetries?: number;
20
+ readonly maxCostUsd?: number;
21
+ }
22
+
23
+ export const InvocationLimitsSchema = z.object({
24
+ maxDurationMs: z.number().int().nonnegative().optional(),
25
+ maxTokens: z.number().int().nonnegative().optional(),
26
+ maxToolCalls: z.number().int().nonnegative().optional(),
27
+ maxRetries: z.number().int().nonnegative().optional(),
28
+ maxCostUsd: z.number().nonnegative().optional(),
29
+ }) as z.ZodType<InvocationLimits>;
30
+
31
+ /**
32
+ * What a biome DECLARES about how its invocation should run; the platform
33
+ * ENFORCES. `mode` is a hint the platform may override by policy; `isolation`
34
+ * defaults to `Strict` (fresh thread per invocation).
35
+ */
36
+ export interface ExecutionRequirements {
37
+ /** Hint; the platform may override the execution mode by policy. */
38
+ readonly mode?: InvocationMode;
39
+ /** Thread-context isolation; default `Strict`. */
40
+ readonly isolation: IsolationLevel;
41
+ readonly latencyTargetMs?: number;
42
+ readonly priority?: InvocationPriority;
43
+ /** Strategy / modelClass hint for model resolution. */
44
+ readonly modelCapability?: string;
45
+ /** Deliverable-spec / JSON-schema ref the root output is validated against. */
46
+ readonly outputSchemaRef?: string;
47
+ readonly idempotencyKey?: string;
48
+ /** Maps to the existing `composition.limits` budget axes. */
49
+ readonly limits?: InvocationLimits;
50
+ }
51
+
52
+ export const ExecutionRequirementsSchema = z.object({
53
+ mode: InvocationModeSchema.optional(),
54
+ isolation: IsolationLevelSchema,
55
+ latencyTargetMs: z.number().int().nonnegative().optional(),
56
+ priority: InvocationPrioritySchema.optional(),
57
+ modelCapability: z.string().min(1).optional(),
58
+ outputSchemaRef: z.string().min(1).optional(),
59
+ idempotencyKey: z.string().min(1).optional(),
60
+ limits: InvocationLimitsSchema.optional(),
61
+ }) as z.ZodType<ExecutionRequirements>;
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * One structured decision produced by a composition invocation. An invocation
5
+ * MAY emit multiple decisions (e.g. a classify step and a route step).
6
+ *
7
+ * `kind` is free-form at the contract layer because the set of decision kinds
8
+ * is open and biome-owned (e.g. `'classification'`, `'route'`); the runtime
9
+ * does not interpret it. `payload` carries the decision body verbatim.
10
+ */
11
+ export interface InvocationDecision {
12
+ readonly kind: string;
13
+ readonly payload: Record<string, unknown>;
14
+ readonly confidence?: number;
15
+ }
16
+
17
+ export const InvocationDecisionSchema = z.object({
18
+ kind: z.string().min(1),
19
+ payload: z.record(z.string(), z.unknown()),
20
+ confidence: z.number().optional(),
21
+ }) as z.ZodType<InvocationDecision>;
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * How the Composition Invocation Runtime executes an invocation.
5
+ *
6
+ * The platform owns mode selection (deterministic `selectMode`): a biome MAY
7
+ * hint a mode via `ExecutionRequirements.mode`, but policy may override it.
8
+ *
9
+ * - `Sync` — the caller holds the request open until the composition produces
10
+ * its root structured output. The smallest correct slice (Phase 1).
11
+ * - `Async` — the runtime returns immediately with a `Queued`/`Running`
12
+ * invocation; the caller polls the Invocation record for the terminal state.
13
+ * - `Event` — the runtime returns without an `output`; the result is delivered
14
+ * later as a completion event the consumer subscribes to (the default at
15
+ * high throughput).
16
+ *
17
+ * Closed set — adding a mode is a coordinated kernel change so producer and
18
+ * consumer stay in lockstep.
19
+ */
20
+ export enum InvocationMode {
21
+ Sync = 'sync',
22
+ Async = 'async',
23
+ Event = 'event',
24
+ }
25
+
26
+ export const InvocationModeSchema = z.nativeEnum(InvocationMode);
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Scheduling priority a biome may hint on an invocation. The platform owns the
5
+ * actual queue ordering; this is an advisory dimension carried on
6
+ * `ExecutionRequirements`.
7
+ *
8
+ * Closed set — adding a priority is a coordinated kernel change.
9
+ */
10
+ export enum InvocationPriority {
11
+ Low = 'low',
12
+ Normal = 'normal',
13
+ High = 'high',
14
+ }
15
+
16
+ export const InvocationPrioritySchema = z.nativeEnum(InvocationPriority);
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { InvocationMode, InvocationModeSchema } from './invocation-mode';
3
+ import { InvocationStatus, InvocationStatusSchema } from './invocation-status';
4
+ import {
5
+ InvocationDecision,
6
+ InvocationDecisionSchema,
7
+ } from './invocation-decision';
8
+
9
+ /**
10
+ * The durable Invocation unit — the projection of one row the runtime persists
11
+ * per invocation. This is the source of truth for an invocation's lifecycle
12
+ * (never the underlying session).
13
+ *
14
+ * `inputRef`/`outputRef` are blob/JSON references (inline JSON in Phase 1,
15
+ * artifact-store refs once payloads grow). Timestamps are ISO-8601 strings.
16
+ */
17
+ export interface InvocationRecord {
18
+ readonly id: string;
19
+ readonly compositionRef: string;
20
+ readonly orgId: string;
21
+ readonly projectId: string | null;
22
+ readonly threadKey: string;
23
+ readonly correlationId: string;
24
+ readonly idempotencyKey: string | null;
25
+ readonly mode: InvocationMode;
26
+ readonly status: InvocationStatus;
27
+ readonly inputRef: string;
28
+ readonly outputRef: string | null;
29
+ readonly decisions: readonly InvocationDecision[];
30
+ readonly costUsd: number | null;
31
+ readonly durationMs: number | null;
32
+ readonly modelRef: string | null;
33
+ readonly auditId: string | null;
34
+ readonly sessionId: string | null;
35
+ readonly createdAt: string;
36
+ readonly lastActiveAt: string;
37
+ }
38
+
39
+ export const InvocationRecordSchema = z.object({
40
+ id: z.string().min(1),
41
+ compositionRef: z.string().min(1),
42
+ orgId: z.string().min(1),
43
+ projectId: z.string().min(1).nullable(),
44
+ threadKey: z.string().min(1),
45
+ correlationId: z.string().min(1),
46
+ idempotencyKey: z.string().min(1).nullable(),
47
+ mode: InvocationModeSchema,
48
+ status: InvocationStatusSchema,
49
+ inputRef: z.string().min(1),
50
+ outputRef: z.string().min(1).nullable(),
51
+ decisions: z.array(InvocationDecisionSchema).readonly(),
52
+ costUsd: z.number().nullable(),
53
+ durationMs: z.number().nullable(),
54
+ modelRef: z.string().min(1).nullable(),
55
+ auditId: z.string().min(1).nullable(),
56
+ sessionId: z.string().min(1).nullable(),
57
+ createdAt: z.string().min(1),
58
+ lastActiveAt: z.string().min(1),
59
+ }) as z.ZodType<InvocationRecord>;
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * The runtime's durable invocation lifecycle.
5
+ *
6
+ * DISTINCT from the capability gateway's allow/deny `InvocationStatus`: the
7
+ * funnel decides whether a call is allowed; THIS status tracks the execution
8
+ * lifecycle of an allowed invocation. Do not conflate the two.
9
+ *
10
+ * - `Pending` — record created, not yet scheduled.
11
+ * - `Queued` — accepted onto an async/event queue.
12
+ * - `Running` — the composition is executing.
13
+ * - `Succeeded` — terminal: produced a schema-valid output.
14
+ * - `Failed` — terminal: execution or output-schema validation failed.
15
+ * - `Denied` — terminal: refused (e.g. by policy/limits) before/at execution.
16
+ * - `TimedOut` — terminal: exceeded a hard duration limit.
17
+ * - `Cancelled` — terminal: cancelled by a caller/operator.
18
+ *
19
+ * Closed set — adding a state is a coordinated kernel change.
20
+ */
21
+ export enum InvocationStatus {
22
+ Pending = 'pending',
23
+ Queued = 'queued',
24
+ Running = 'running',
25
+ Succeeded = 'succeeded',
26
+ Failed = 'failed',
27
+ Denied = 'denied',
28
+ TimedOut = 'timed-out',
29
+ Cancelled = 'cancelled',
30
+ }
31
+
32
+ export const InvocationStatusSchema = z.nativeEnum(InvocationStatus);