@xemahq/kernel-contracts 0.49.3 → 0.56.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 (196) hide show
  1. package/dist/app-definition/lib/automation.d.ts.map +1 -1
  2. package/dist/app-definition/lib/automation.js.map +1 -1
  3. package/dist/biome/lib/biome-manifest.d.ts +9 -0
  4. package/dist/biome/lib/biome-manifest.d.ts.map +1 -1
  5. package/dist/biome/lib/biome-manifest.js +2 -0
  6. package/dist/biome/lib/biome-manifest.js.map +1 -1
  7. package/dist/capability/index.d.ts +2 -1
  8. package/dist/capability/index.d.ts.map +1 -1
  9. package/dist/capability/index.js +2 -1
  10. package/dist/capability/index.js.map +1 -1
  11. package/dist/capability/lib/authority-effect.d.ts +8 -0
  12. package/dist/capability/lib/authority-effect.d.ts.map +1 -0
  13. package/dist/capability/lib/authority-effect.js +17 -0
  14. package/dist/capability/lib/authority-effect.js.map +1 -0
  15. package/dist/capability/lib/capability-contribution.d.ts +4 -0
  16. package/dist/capability/lib/capability-contribution.d.ts.map +1 -1
  17. package/dist/capability/lib/capability-contribution.js +4 -0
  18. package/dist/capability/lib/capability-contribution.js.map +1 -1
  19. package/dist/capability/lib/capability-domain.d.ts +8 -0
  20. package/dist/capability/lib/capability-domain.d.ts.map +1 -0
  21. package/dist/capability/lib/capability-domain.js +24 -0
  22. package/dist/capability/lib/capability-domain.js.map +1 -0
  23. package/dist/capability/lib/meta-tool.d.ts +51 -14
  24. package/dist/capability/lib/meta-tool.d.ts.map +1 -1
  25. package/dist/capability/lib/meta-tool.js +66 -12
  26. package/dist/capability/lib/meta-tool.js.map +1 -1
  27. package/dist/capability-projection/index.d.ts +4 -0
  28. package/dist/capability-projection/index.d.ts.map +1 -0
  29. package/dist/capability-projection/index.js +20 -0
  30. package/dist/capability-projection/index.js.map +1 -0
  31. package/dist/capability-projection/lib/capability-invocation-binding.d.ts +72 -0
  32. package/dist/capability-projection/lib/capability-invocation-binding.d.ts.map +1 -0
  33. package/dist/capability-projection/lib/capability-invocation-binding.js +101 -0
  34. package/dist/capability-projection/lib/capability-invocation-binding.js.map +1 -0
  35. package/dist/capability-projection/lib/capability-sync-manifest.d.ts +8 -0
  36. package/dist/capability-projection/lib/capability-sync-manifest.d.ts.map +1 -0
  37. package/dist/capability-projection/lib/capability-sync-manifest.js +8 -0
  38. package/dist/capability-projection/lib/capability-sync-manifest.js.map +1 -0
  39. package/dist/capability-projection/lib/projected-capability.d.ts +15 -0
  40. package/dist/capability-projection/lib/projected-capability.d.ts.map +1 -0
  41. package/dist/capability-projection/lib/projected-capability.js +17 -0
  42. package/dist/capability-projection/lib/projected-capability.js.map +1 -0
  43. package/dist/connector/lib/connector-adapter-contribution.d.ts +2 -0
  44. package/dist/connector/lib/connector-adapter-contribution.d.ts.map +1 -1
  45. package/dist/connector/lib/onboarding-manifest.d.ts +2 -0
  46. package/dist/connector/lib/onboarding-manifest.d.ts.map +1 -1
  47. package/dist/connector/lib/onboarding-manifest.js +1 -0
  48. package/dist/connector/lib/onboarding-manifest.js.map +1 -1
  49. package/dist/connector/lib/provider-descriptor.d.ts +2 -0
  50. package/dist/connector/lib/provider-descriptor.d.ts.map +1 -1
  51. package/dist/contribution/lib/contribution-kind.d.ts +1 -4
  52. package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
  53. package/dist/contribution/lib/contribution-kind.js +1 -4
  54. package/dist/contribution/lib/contribution-kind.js.map +1 -1
  55. package/dist/distribution/index.d.ts +1 -0
  56. package/dist/distribution/index.d.ts.map +1 -1
  57. package/dist/distribution/index.js +1 -0
  58. package/dist/distribution/index.js.map +1 -1
  59. package/dist/distribution/lib/distribution-lock.d.ts +6 -0
  60. package/dist/distribution/lib/distribution-lock.d.ts.map +1 -1
  61. package/dist/distribution/lib/distribution-lock.js +9 -1
  62. package/dist/distribution/lib/distribution-lock.js.map +1 -1
  63. package/dist/distribution/lib/first-party-capability-domains.d.ts +15 -0
  64. package/dist/distribution/lib/first-party-capability-domains.d.ts.map +1 -0
  65. package/dist/distribution/lib/first-party-capability-domains.js +52 -0
  66. package/dist/distribution/lib/first-party-capability-domains.js.map +1 -0
  67. package/dist/object/lib/xema-object-ref.d.ts +0 -5
  68. package/dist/object/lib/xema-object-ref.d.ts.map +1 -1
  69. package/dist/object/lib/xema-object-ref.js +12 -62
  70. package/dist/object/lib/xema-object-ref.js.map +1 -1
  71. package/dist/output-surface-inspect/index.d.ts +1 -0
  72. package/dist/output-surface-inspect/index.d.ts.map +1 -1
  73. package/dist/output-surface-inspect/index.js +1 -0
  74. package/dist/output-surface-inspect/index.js.map +1 -1
  75. package/dist/output-surface-inspect/lib/buffers.d.ts +52 -0
  76. package/dist/output-surface-inspect/lib/buffers.d.ts.map +1 -0
  77. package/dist/output-surface-inspect/lib/buffers.js +132 -0
  78. package/dist/output-surface-inspect/lib/buffers.js.map +1 -0
  79. package/dist/output-surface-inspect/lib/output-surface-inspect.d.ts +6 -1
  80. package/dist/output-surface-inspect/lib/output-surface-inspect.d.ts.map +1 -1
  81. package/dist/output-surface-inspect/lib/output-surface-inspect.js +12 -1
  82. package/dist/output-surface-inspect/lib/output-surface-inspect.js.map +1 -1
  83. package/dist/output-surface-inspect/lib/step-schema.d.ts +2 -1
  84. package/dist/output-surface-inspect/lib/step-schema.d.ts.map +1 -1
  85. package/dist/output-surface-inspect/lib/step-schema.js +9 -2
  86. package/dist/output-surface-inspect/lib/step-schema.js.map +1 -1
  87. package/dist/output-surface-inspect/runtime/index.d.ts +1 -0
  88. package/dist/output-surface-inspect/runtime/index.d.ts.map +1 -1
  89. package/dist/output-surface-inspect/runtime/index.js +1 -0
  90. package/dist/output-surface-inspect/runtime/index.js.map +1 -1
  91. package/dist/search/index.d.ts +1 -0
  92. package/dist/search/index.d.ts.map +1 -1
  93. package/dist/search/index.js +1 -0
  94. package/dist/search/index.js.map +1 -1
  95. package/dist/search/lib/route-template.d.ts +31 -0
  96. package/dist/search/lib/route-template.d.ts.map +1 -0
  97. package/dist/search/lib/route-template.js +142 -0
  98. package/dist/search/lib/route-template.js.map +1 -0
  99. package/dist/search/lib/search-contribution.d.ts +2 -0
  100. package/dist/search/lib/search-contribution.d.ts.map +1 -1
  101. package/dist/search/lib/search-contribution.js +34 -1
  102. package/dist/search/lib/search-contribution.js.map +1 -1
  103. package/dist/search-source/lib/indexable-document.d.ts +22 -0
  104. package/dist/search-source/lib/indexable-document.d.ts.map +1 -1
  105. package/dist/search-source/lib/indexable-document.js +10 -1
  106. package/dist/search-source/lib/indexable-document.js.map +1 -1
  107. package/dist/search-source/lib/search-index-event.d.ts +10 -0
  108. package/dist/search-source/lib/search-index-event.d.ts.map +1 -1
  109. package/dist/search-source/lib/search-ref.d.ts.map +1 -1
  110. package/dist/search-source/lib/search-ref.js +11 -2
  111. package/dist/search-source/lib/search-ref.js.map +1 -1
  112. package/dist/search-source/lib/search-replay.d.ts +10 -0
  113. package/dist/search-source/lib/search-replay.d.ts.map +1 -1
  114. package/dist/service-registry/lib/service-descriptor.d.ts +1 -0
  115. package/dist/service-registry/lib/service-descriptor.d.ts.map +1 -1
  116. package/dist/space/lib/space-ref-parser.d.ts +6 -4
  117. package/dist/space/lib/space-ref-parser.d.ts.map +1 -1
  118. package/dist/space/lib/space-ref-parser.js +90 -126
  119. package/dist/space/lib/space-ref-parser.js.map +1 -1
  120. package/dist/space/lib/space-traversal.d.ts.map +1 -1
  121. package/dist/space/lib/space-traversal.js +9 -13
  122. package/dist/space/lib/space-traversal.js.map +1 -1
  123. package/dist/space/lib/space.d.ts +9 -0
  124. package/dist/space/lib/space.d.ts.map +1 -1
  125. package/dist/space/lib/space.js +59 -29
  126. package/dist/space/lib/space.js.map +1 -1
  127. package/dist/subject/lib/subject.d.ts +1 -0
  128. package/dist/subject/lib/subject.d.ts.map +1 -1
  129. package/dist/subject/lib/subject.js +1 -0
  130. package/dist/subject/lib/subject.js.map +1 -1
  131. package/dist/surface/index.d.ts +4 -0
  132. package/dist/surface/index.d.ts.map +1 -0
  133. package/dist/surface/index.js +20 -0
  134. package/dist/surface/index.js.map +1 -0
  135. package/dist/surface/lib/surface-contribution.d.ts +24 -0
  136. package/dist/surface/lib/surface-contribution.d.ts.map +1 -0
  137. package/dist/surface/lib/surface-contribution.js +23 -0
  138. package/dist/surface/lib/surface-contribution.js.map +1 -0
  139. package/dist/surface/lib/surface-kind.d.ts +23 -0
  140. package/dist/surface/lib/surface-kind.d.ts.map +1 -0
  141. package/dist/surface/lib/surface-kind.js +27 -0
  142. package/dist/surface/lib/surface-kind.js.map +1 -0
  143. package/dist/surface/lib/vista-surface.d.ts +11 -0
  144. package/dist/surface/lib/vista-surface.d.ts.map +1 -0
  145. package/dist/surface/lib/vista-surface.js +13 -0
  146. package/dist/surface/lib/vista-surface.js.map +1 -0
  147. package/dist/widget/lib/chat-widget-envelope.d.ts.map +1 -1
  148. package/dist/widget/lib/chat-widget-envelope.js.map +1 -1
  149. package/dist/workflow/lib/enums.d.ts.map +1 -1
  150. package/dist/workflow/lib/enums.js.map +1 -1
  151. package/package.json +1 -1
  152. package/src/app-definition/lib/automation.ts +3 -2
  153. package/src/biome/lib/biome-manifest.ts +41 -1
  154. package/src/capability/index.ts +2 -1
  155. package/src/capability/lib/authority-effect.ts +88 -0
  156. package/src/capability/lib/capability-contribution.ts +46 -0
  157. package/src/capability/lib/capability-domain.ts +51 -0
  158. package/src/capability/lib/capability-grant.ts +1 -1
  159. package/src/capability/lib/meta-tool.ts +172 -39
  160. package/src/capability-projection/index.ts +3 -0
  161. package/src/capability-projection/lib/capability-invocation-binding.ts +223 -0
  162. package/src/capability-projection/lib/capability-sync-manifest.ts +52 -0
  163. package/src/capability-projection/lib/projected-capability.ts +63 -0
  164. package/src/connector/lib/onboarding-manifest.ts +16 -0
  165. package/src/contribution/lib/contribution-kind.ts +30 -29
  166. package/src/distribution/index.ts +1 -0
  167. package/src/distribution/lib/distribution-lock.ts +36 -0
  168. package/src/distribution/lib/first-party-capability-domains.ts +166 -0
  169. package/src/object/lib/xema-object-ref.ts +27 -82
  170. package/src/output-surface-inspect/index.ts +2 -0
  171. package/src/output-surface-inspect/lib/buffers.ts +252 -0
  172. package/src/output-surface-inspect/lib/output-surface-inspect.ts +65 -6
  173. package/src/output-surface-inspect/lib/step-schema.ts +36 -3
  174. package/src/output-surface-inspect/runtime/index.ts +1 -0
  175. package/src/search/index.ts +1 -0
  176. package/src/search/lib/route-template.ts +381 -0
  177. package/src/search/lib/search-contribution.ts +84 -11
  178. package/src/search-source/lib/indexable-document.ts +44 -0
  179. package/src/search-source/lib/search-ref.ts +22 -9
  180. package/src/service-registry/lib/service-descriptor.ts +16 -0
  181. package/src/space/lib/skill-mirror-path.ts +1 -1
  182. package/src/space/lib/space-ref-parser.ts +190 -165
  183. package/src/space/lib/space-traversal.ts +34 -17
  184. package/src/space/lib/space.ts +148 -35
  185. package/src/subject/lib/subject.ts +16 -0
  186. package/src/surface/index.ts +3 -0
  187. package/src/surface/lib/surface-contribution.ts +101 -0
  188. package/src/surface/lib/surface-kind.ts +65 -0
  189. package/src/surface/lib/vista-surface.ts +44 -0
  190. package/src/widget/lib/chat-widget-envelope.ts +6 -2
  191. package/src/workflow/lib/enums.ts +6 -7
  192. package/dist/capability/lib/first-party-capability-domains.d.ts +0 -30
  193. package/dist/capability/lib/first-party-capability-domains.d.ts.map +0 -1
  194. package/dist/capability/lib/first-party-capability-domains.js +0 -36
  195. package/dist/capability/lib/first-party-capability-domains.js.map +0 -1
  196. package/src/capability/lib/first-party-capability-domains.ts +0 -77
@@ -28,12 +28,14 @@ import { z } from 'zod';
28
28
  * adapter projecting the `MountSourceKindRegistry`; NO biome-host
29
29
  * parser.
30
30
  * 3. RESERVED — declared for protocol/catalog completeness but with NO
31
- * contribution-protocol ingestion path today. Where the underlying feature
32
- * exists it ships via the convention CONTENT-DIRECTORY seeder (on-disk
33
- * presence — see the manifest reference), NOT the enum-dispatched
34
- * contribution protocol; a few members have no delivery path at all yet.
35
- * Do NOT assume a biome-host parser exists for a RESERVED member, and do
36
- * not ship a biome contribution of that kind expecting it to be ingested.
31
+ * contribution-protocol ingestion path today. Every remaining RESERVED
32
+ * member has a REAL delivery path through the convention CONTENT-DIRECTORY
33
+ * seeder (on-disk presence — see the manifest reference); the four members
34
+ * that had NO delivery path at all (`GateAction`, `ChartRuntime`,
35
+ * `WorkspaceSpecOverlay`, `SystemOverlayContribution`) were DELETED rather
36
+ * than left as decoration. Do NOT assume a biome-host parser exists for a
37
+ * RESERVED member, and do not ship a biome contribution of that kind
38
+ * expecting it to be ingested.
37
39
  *
38
40
  * This doc-comment is kept HONEST by review: a member with no biome-host parser
39
41
  * MUST be annotated with its true category (2 or 3). An earlier revision falsely
@@ -66,19 +68,6 @@ export enum ContributionKind {
66
68
  */
67
69
  McpTool = 'mcp-tool',
68
70
  WorkflowStep = 'workflow-step',
69
- /**
70
- * RESERVED (category 3). No ingestion path today — no parser, no
71
- * content-directory seeder, no domain-service handler. The only in-tree use
72
- * is a deliberate PLACEHOLDER reuse of this value in the kernel
73
- * `inquiry-kind-adapter` (its own comment flags it as a placeholder, not an
74
- * equivalence). A biome contribution of this kind is NOT ingested.
75
- */
76
- GateAction = 'gate-action',
77
- /**
78
- * RESERVED (category 3). No ingestion path today; referenced only as a worked
79
- * "a third party adds a new kind" example in the contribution test-suite.
80
- */
81
- ChartRuntime = 'chart-runtime',
82
71
  AgentSkill = 'agent-skill',
83
72
  AgentKernel = 'agent-kernel',
84
73
  /**
@@ -91,6 +80,28 @@ export enum ContributionKind {
91
80
  */
92
81
  ModelResolutionDimension = 'model-resolution-dimension',
93
82
  WidgetKind = 'widget-kind',
83
+ /**
84
+ * Category 1 (biome-host `KindParser` — parser pending). A VISTA SURFACE
85
+ * KIND contributed by a biome: the render kind a Vista preview tab can hold,
86
+ * declared as a bare biome-local slug plus a version, display name, summary,
87
+ * and an opaque JSON `payloadSchema`. The catalogue namespaces the slug to
88
+ * `<biomeId>:<kind>` at ingest so a biome can never shadow a first-party
89
+ * `SurfaceKind`.
90
+ *
91
+ * The kernel owns this member AND the manifest schema
92
+ * (`SurfaceContributionManifestSchema`, exported from
93
+ * `@xemahq/kernel-contracts/surface`) — the 2-file rule. The per-kind
94
+ * ingestion parser belongs to the consuming service (`biome-host-api`), not
95
+ * the kernel, and is NOT shipped yet: a later Vista unit adds
96
+ * `surface-kind.parser.ts` alongside the catalogue endpoint. Until then this
97
+ * member is declared but not ingested — do not ship a biome contribution of
98
+ * this kind expecting it to be picked up.
99
+ *
100
+ * Structurally identical to {@link ContributionKind.WidgetKind} on purpose:
101
+ * the in-chat widget plane and the Vista render plane share ONE contribution
102
+ * shape, one frontend registry core, and one three-layer renderer fallback.
103
+ */
104
+ SurfaceKind = 'surface-kind',
94
105
 
95
106
  // -- Registry gaps closed --------------------------------------------------
96
107
  /**
@@ -135,16 +146,6 @@ export enum ContributionKind {
135
146
  * `xema.provisioning[]`, parity-checked at boot), not the contribution path.
136
147
  */
137
148
  ProvisioningScaffold = 'provisioning-scaffold',
138
- /**
139
- * RESERVED (category 3). No ingestion path today — no parser, no
140
- * content-directory seeder, no domain-service handler.
141
- */
142
- WorkspaceSpecOverlay = 'workspace-spec-overlay',
143
- /**
144
- * RESERVED (category 3). No ingestion path today — no parser, no
145
- * content-directory seeder, no domain-service handler.
146
- */
147
- SystemOverlayContribution = 'system-overlay-contribution',
148
149
  /** A concrete connector provider contributed by a biome inside an
149
150
  * adapter kind. */
150
151
  ConnectorAdapter = 'connector-adapter',
@@ -3,3 +3,4 @@ export * from './lib/distribution-selector';
3
3
  export * from './lib/distribution';
4
4
  export * from './lib/distribution-lock';
5
5
  export * from './lib/image-lock';
6
+ export * from './lib/first-party-capability-domains';
@@ -9,6 +9,7 @@ import {
9
9
  type BiomeOrigin,
10
10
  type BiomeAudience,
11
11
  } from '../../biome';
12
+ import { CapabilityDomainSchema } from '../../capability';
12
13
  import {
13
14
  PlatformServiceTierSchema,
14
15
  type PlatformServiceTier,
@@ -164,10 +165,44 @@ export const LockedPlatformServiceSchema = z.object({
164
165
  * kernel (kernel contracts hold no clock); both optional so the type is usable
165
166
  * mid-resolution.
166
167
  */
168
+ /**
169
+ * One platform-reserved capability domain and the biome that owns it.
170
+ *
171
+ * Resolved from the FULL biome index the release pipeline discovered — NOT
172
+ * from `DistributionLock.biomes`. A reservation is a statement about who
173
+ * AUTHORED the domain, so it holds in every edition whether or not that
174
+ * edition preloads the owning biome. See `first-party-capability-domains.ts`
175
+ * for why per-edition ownership would be a fail-open hole.
176
+ */
177
+ export interface CapabilityDomainOwner {
178
+ /** The `<domain>` segment of a `CapabilityRef`. */
179
+ domain: string;
180
+ /** `xema.id` of the biome whose manifest claims it. */
181
+ biomeId: string;
182
+ }
183
+
184
+ export const CapabilityDomainOwnerSchema = z
185
+ .object({
186
+ domain: CapabilityDomainSchema,
187
+ biomeId: z.string().min(1),
188
+ })
189
+ .strict() as z.ZodType<CapabilityDomainOwner>;
190
+
167
191
  export interface DistributionLock {
168
192
  schemaVersion: 1;
169
193
  distributionId: string;
170
194
  biomes: readonly LockedBiome[];
195
+ /**
196
+ * Platform-reserved capability domains — the input `capability-registry-api`
197
+ * derives its org-provider-binding shadow refusal from, and the router its
198
+ * third-party authority-effect rule.
199
+ *
200
+ * REQUIRED, deliberately. An absent or empty table means "no domain is
201
+ * first-party", which is fail-OPEN: every platform ref would become
202
+ * org-bindable. A lock that predates this field must therefore fail to parse
203
+ * rather than silently disarm the guard.
204
+ */
205
+ capabilityDomainOwners: readonly CapabilityDomainOwner[];
171
206
  /** Non-biome substrate services that ship with this distribution (the deploy "init system"). */
172
207
  platformServices: readonly LockedPlatformService[];
173
208
  /** ISO-8601 timestamp the resolver stamped (tooling-supplied). */
@@ -180,6 +215,7 @@ export const DistributionLockSchema = z.object({
180
215
  schemaVersion: z.literal(1),
181
216
  distributionId: z.string().min(1),
182
217
  biomes: z.array(LockedBiomeSchema).readonly(),
218
+ capabilityDomainOwners: z.array(CapabilityDomainOwnerSchema).readonly(),
183
219
  platformServices: z.array(LockedPlatformServiceSchema).readonly(),
184
220
  resolvedAt: z.string().min(1).optional(),
185
221
  inputHash: z.string().min(1).optional(),
@@ -0,0 +1,166 @@
1
+ import {
2
+ capabilityDomainOf,
3
+ isCapabilityDomain,
4
+ type CapabilityDomain,
5
+ } from '../../capability';
6
+ import type { DistributionLock } from './distribution-lock';
7
+
8
+ /**
9
+ * ═══════════════════════════════════════════════════════════════════════════
10
+ * First-party capability domains — DERIVED from the distribution lock
11
+ * ═══════════════════════════════════════════════════════════════════════════
12
+ *
13
+ * A capability ref is `<domain>:<resource>.<verb>@<major>`. A DOMAIN is
14
+ * "first-party" when an in-the-box platform biome owns every ref under it.
15
+ *
16
+ * WHY THIS MATTERS (the security job):
17
+ *
18
+ * `capability-registry-api` REFUSES an org `CapabilityProviderBinding` whose
19
+ * ref domain is first-party. Without that refusal an org admin could bind
20
+ * `kb:page.read@1` — or `web:page.fetch@1` — to a URL they control and
21
+ * silently shadow the platform data plane for every user, agent and app in
22
+ * the org. The router is data-driven off the invocation binding, so a
23
+ * shadowing binding is a live redirect, not a theoretical one.
24
+ *
25
+ * WHY THE DISTRIBUTION LOCK IS THE RIGHT SOURCE, AND THE OTHER TWO WERE NOT:
26
+ *
27
+ * • NOT `exposesCapabilities` on a live manifest — that is SELF-DECLARED at
28
+ * install time, so a third-party biome an org admin installs could mint
29
+ * itself first-party and then bind the domain it just claimed. Circular.
30
+ * • NOT capability-record provenance in the registry DB — that is written by
31
+ * an eventually-consistent contribution-sync retry, so a derivation from it
32
+ * would report "not first-party" for a genuinely first-party domain during
33
+ * the exact window (boot, resync) the refusal exists to cover. FAIL-OPEN.
34
+ * • The distribution lock is neither. It is RESOLVED by the release pipeline
35
+ * from a pinned checkout, COMMITTED, drift-checked in CI (`dist-check`),
36
+ * vendored into the deployment as an immutable ConfigMap, and read once at
37
+ * boot. Nothing an org admin can do at runtime — installing a biome from
38
+ * npm/OCI/git, editing a manifest, publishing a contribution — changes it.
39
+ * Membership is a PLATFORM-AUTHOR decision, exactly as it should be.
40
+ *
41
+ * ── OWNERSHIP IS RESERVATION-WIDE, NOT PER-EDITION ─────────────────────────
42
+ *
43
+ * `DistributionLock.capabilityDomainOwners` is resolved from the FULL biome
44
+ * index — every first-party biome the release pipeline discovered in its own
45
+ * checkout — NOT from the subset of biomes this edition happens to ship.
46
+ *
47
+ * That distinction is load-bearing. If ownership were per-edition, a trimmed
48
+ * edition that does not ship `knowledge-base` would leave `kb` unowned, so an
49
+ * org admin could install `knowledge-base` from the store and THEN bind
50
+ * `kb:page.read@1` to a URL they control — shadowing it for the whole org.
51
+ * Being authored by the platform is what makes a domain un-shadowable;
52
+ * whether a given edition preloads the owning biome is a packaging choice and
53
+ * must not change the security answer. The domains are RESERVED by the
54
+ * platform in every edition, shipped or not.
55
+ *
56
+ * This is also why the field is on the LOCK and not on `LockedBiome`: a
57
+ * reservation exists whether or not the owning biome appears in `biomes[]`.
58
+ *
59
+ * COLLISIONS FAIL FAST. Two biomes claiming the same domain is a packaging
60
+ * error that would make ownership ambiguous, so this class throws rather than
61
+ * picking a winner. The distribution resolver runs the same check before it
62
+ * writes, so the failure surfaces at build time; the boot-time throw is the
63
+ * backstop.
64
+ *
65
+ * UNDECIDABLE INPUT IS FIRST-PARTY, NOT THIRD-PARTY. `has()` on a string that
66
+ * is not a syntactically valid domain returns `true` (⇒ refuse the binding).
67
+ * A guard must never treat "I could not parse this" as "safe to shadow".
68
+ */
69
+ export class FirstPartyCapabilityDomains {
70
+ private constructor(
71
+ private readonly owners: ReadonlyMap<CapabilityDomain, string>,
72
+ ) {}
73
+
74
+ /**
75
+ * Build the ownership index from a parsed distribution lock's
76
+ * `capabilityDomainOwners` reservation table.
77
+ *
78
+ * @throws when two biomes claim the same domain, or when a claimed value is
79
+ * not a syntactically valid capability domain. Both are packaging errors;
80
+ * neither may be tolerated silently in a security guard's input.
81
+ */
82
+ static fromDistributionLock(
83
+ lock: DistributionLock,
84
+ ): FirstPartyCapabilityDomains {
85
+ return FirstPartyCapabilityDomains.fromOwnerEntries(
86
+ lock.capabilityDomainOwners.map(
87
+ (owner) => [owner.domain, owner.biomeId] as const,
88
+ ),
89
+ `Distribution "${lock.distributionId}"`,
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Build the ownership index directly from `(domain → owning biome id)` pairs.
95
+ * For consumers that receive the ownership projection over the wire instead
96
+ * of reading the lock file themselves. Same validation, same collision rule.
97
+ */
98
+ static fromOwnerEntries(
99
+ entries: Iterable<readonly [string, string]>,
100
+ sourceLabel: string,
101
+ ): FirstPartyCapabilityDomains {
102
+ const owners = new Map<CapabilityDomain, string>();
103
+ for (const [claimed, biomeId] of entries) {
104
+ if (!isCapabilityDomain(claimed)) {
105
+ throw new Error(
106
+ `${sourceLabel}: "${claimed}" is not a valid capability domain (^[a-z][a-z0-9-]*$).`,
107
+ );
108
+ }
109
+ const incumbent = owners.get(claimed);
110
+ if (incumbent !== undefined && incumbent !== biomeId) {
111
+ throw new Error(
112
+ `${sourceLabel}: capability domain "${claimed}" is claimed by BOTH "${incumbent}" and "${biomeId}". ` +
113
+ 'A domain has exactly one owner — ownership decides whether an org may bind refs in it, ' +
114
+ 'so an ambiguous claim is refused rather than resolved to a winner.',
115
+ );
116
+ }
117
+ owners.set(claimed, biomeId);
118
+ }
119
+ return new FirstPartyCapabilityDomains(owners);
120
+ }
121
+
122
+ /**
123
+ * `true` iff `domain` is owned by a biome in the active distribution — or if
124
+ * `domain` is not a parseable domain at all (undecidable ⇒ protected).
125
+ */
126
+ has(domain: string): boolean {
127
+ return !isCapabilityDomain(domain) || this.owners.has(domain);
128
+ }
129
+
130
+ /**
131
+ * `true` iff the DOMAIN OF `ref` is first-party. A ref whose domain cannot be
132
+ * extracted is undecidable and therefore protected.
133
+ */
134
+ hasRefDomain(ref: string): boolean {
135
+ const domain = capabilityDomainOf(ref);
136
+ return domain === null || this.owners.has(domain);
137
+ }
138
+
139
+ /** The biome that owns `domain`, or `undefined` when nobody claims it. */
140
+ ownerOf(domain: string): string | undefined {
141
+ return this.owners.get(domain);
142
+ }
143
+
144
+ /** Claimed domains, sorted — for boot logs and diagnostics. */
145
+ get domains(): readonly CapabilityDomain[] {
146
+ return [...this.owners.keys()].sort();
147
+ }
148
+
149
+ get size(): number {
150
+ return this.owners.size;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Convenience wrapper — `(domain → owning biome id)` for the whole lock, with
156
+ * the collision + grammar checks applied. The distribution resolver calls this
157
+ * at build time so a bad claim fails the release, not the deployment.
158
+ */
159
+ export function resolveFirstPartyCapabilityDomains(
160
+ lock: DistributionLock,
161
+ ): ReadonlyMap<CapabilityDomain, string> {
162
+ const resolved = FirstPartyCapabilityDomains.fromDistributionLock(lock);
163
+ return new Map(
164
+ resolved.domains.map((domain) => [domain, resolved.ownerOf(domain)!]),
165
+ );
166
+ }
@@ -1,5 +1,10 @@
1
1
  import { z } from 'zod';
2
- import { SpaceRef, SpaceKind, spaceRefToPath } from '../../space';
2
+ import {
3
+ parseSpaceRefScopeSegments,
4
+ SpaceRef,
5
+ SpaceRefParseError,
6
+ spaceRefToPath,
7
+ } from '../../space';
3
8
  import { XemaObjectKind, XemaObjectKindSchema } from './xema-object-kind';
4
9
 
5
10
  /**
@@ -69,9 +74,26 @@ const SLUG_SEGMENT = '[a-zA-Z0-9][a-zA-Z0-9_.-]*';
69
74
  const SLUG_PATTERN = new RegExp(`^${SLUG_SEGMENT}(?:/${SLUG_SEGMENT})*$`);
70
75
  const VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.+-]*$/;
71
76
 
72
- export interface ScopeParse {
73
- scope: SpaceRef;
74
- consumed: number;
77
+ /**
78
+ * Consume the leading scope segments of a `xema://` object ref, re-raising any
79
+ * scope-grammar failure as a {@link XemaObjectRefParseError} so an object-ref
80
+ * caller only ever has to catch ONE error type. The scope grammar itself lives
81
+ * in `@xemahq/kernel-contracts/space` (`parseSpaceRefScopeSegments`) — it is a
82
+ * SPACE grammar, and object refs, search refs and Space refs all read it from
83
+ * there rather than restating it.
84
+ */
85
+ function parseScope(
86
+ segments: string[],
87
+ ref: string,
88
+ ): { scope: SpaceRef; consumed: number } {
89
+ try {
90
+ return parseSpaceRefScopeSegments(segments, ref);
91
+ } catch (err) {
92
+ if (err instanceof SpaceRefParseError) {
93
+ throw new XemaObjectRefParseError(ref, err.message);
94
+ }
95
+ throw err;
96
+ }
75
97
  }
76
98
 
77
99
  function requireSegment(
@@ -87,83 +109,6 @@ function requireSegment(
87
109
  return value;
88
110
  }
89
111
 
90
- /**
91
- * Parse the leading scope segments of a `xema://` ref path into a
92
- * {@link SpaceRef} plus the number of segments consumed. Shared by
93
- * {@link parseXemaObjectRef} and the search-source `parseSearchRef` helper so
94
- * the `xema://<scope-path>/...` scope grammar (plural `orgs`/`projects`, the
95
- * same one {@link spaceRefToPath} formats) lives in exactly ONE place. The
96
- * caller owns whatever follows the scope (a `<kind>/<slug>` for objects, a
97
- * `<docType>/<sourceId>` for search refs).
98
- */
99
- export function parseObjectRefScopeSegments(
100
- segments: string[],
101
- ref: string,
102
- ): ScopeParse {
103
- const head = requireSegment(segments, 0, ref, 'scope');
104
- switch (head) {
105
- case 'system':
106
- return { scope: { tier: SpaceKind.System }, consumed: 1 };
107
- case 'biomes': {
108
- const biomeId = requireSegment(segments, 1, ref, 'biomeId');
109
- return {
110
- scope: { tier: SpaceKind.Biome, biomeId },
111
- consumed: 2,
112
- };
113
- }
114
- case 'users': {
115
- const userId = requireSegment(segments, 1, ref, 'userId');
116
- return {
117
- scope: { tier: SpaceKind.User, userId },
118
- consumed: 2,
119
- };
120
- }
121
- case 'sessions': {
122
- const sessionId = requireSegment(segments, 1, ref, 'sessionId');
123
- return {
124
- scope: { tier: SpaceKind.Session, sessionId },
125
- consumed: 2,
126
- };
127
- }
128
- case 'orgs': {
129
- const orgId = requireSegment(segments, 1, ref, 'orgId');
130
- if (segments.length >= 4 && segments[2] === 'projects') {
131
- const projectId = requireSegment(segments, 3, ref, 'projectId');
132
- // …/projects/<p>/apps/<a> — the App tier (most specific under a project).
133
- if (segments.length >= 6 && segments[4] === 'apps') {
134
- const appId = requireSegment(segments, 5, ref, 'appId');
135
- return {
136
- scope: {
137
- tier: SpaceKind.App,
138
- orgId,
139
- projectId,
140
- appId,
141
- },
142
- consumed: 6,
143
- };
144
- }
145
- return {
146
- scope: {
147
- tier: SpaceKind.Project,
148
- orgId,
149
- projectId,
150
- },
151
- consumed: 4,
152
- };
153
- }
154
- return {
155
- scope: { tier: SpaceKind.Org, orgId },
156
- consumed: 2,
157
- };
158
- }
159
- default:
160
- throw new XemaObjectRefParseError(
161
- ref,
162
- `unknown scope head "${head}" (expected one of: system, biomes, orgs, users, sessions)`,
163
- );
164
- }
165
- }
166
-
167
112
  /**
168
113
  * Parse a `XemaObjectRef` into structured parts. Fail-fast on every
169
114
  * malformed input (no silent coercion). When the `@<version>` suffix
@@ -210,7 +155,7 @@ export function parseXemaObjectRef(ref: string): XemaObjectRefParts {
210
155
  );
211
156
  }
212
157
 
213
- const { scope, consumed } = parseObjectRefScopeSegments(segments, ref);
158
+ const { scope, consumed } = parseScope(segments, ref);
214
159
  const remaining = segments.slice(consumed);
215
160
  if (remaining.length < 2) {
216
161
  throw new XemaObjectRefParseError(
@@ -1,4 +1,6 @@
1
1
  // Pure, zod-free contract — safe to bundle into the browser-side bridge.
2
2
  export * from './lib/output-surface-inspect';
3
+ // Shared bounded console/network ring buffers (also zod-free).
4
+ export * from './lib/buffers';
3
5
  // Runtime zod validator — pulled in by agent-session-api's broker only.
4
6
  export * from './lib/step-schema';