@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.
- package/dist/app-definition/lib/automation.d.ts.map +1 -1
- package/dist/app-definition/lib/automation.js.map +1 -1
- package/dist/biome/lib/biome-manifest.d.ts +9 -0
- package/dist/biome/lib/biome-manifest.d.ts.map +1 -1
- package/dist/biome/lib/biome-manifest.js +2 -0
- package/dist/biome/lib/biome-manifest.js.map +1 -1
- package/dist/capability/index.d.ts +2 -1
- package/dist/capability/index.d.ts.map +1 -1
- package/dist/capability/index.js +2 -1
- package/dist/capability/index.js.map +1 -1
- package/dist/capability/lib/authority-effect.d.ts +8 -0
- package/dist/capability/lib/authority-effect.d.ts.map +1 -0
- package/dist/capability/lib/authority-effect.js +17 -0
- package/dist/capability/lib/authority-effect.js.map +1 -0
- package/dist/capability/lib/capability-contribution.d.ts +4 -0
- package/dist/capability/lib/capability-contribution.d.ts.map +1 -1
- package/dist/capability/lib/capability-contribution.js +4 -0
- package/dist/capability/lib/capability-contribution.js.map +1 -1
- package/dist/capability/lib/capability-domain.d.ts +8 -0
- package/dist/capability/lib/capability-domain.d.ts.map +1 -0
- package/dist/capability/lib/capability-domain.js +24 -0
- package/dist/capability/lib/capability-domain.js.map +1 -0
- package/dist/capability/lib/meta-tool.d.ts +51 -14
- package/dist/capability/lib/meta-tool.d.ts.map +1 -1
- package/dist/capability/lib/meta-tool.js +66 -12
- package/dist/capability/lib/meta-tool.js.map +1 -1
- package/dist/capability-projection/index.d.ts +4 -0
- package/dist/capability-projection/index.d.ts.map +1 -0
- package/dist/capability-projection/index.js +20 -0
- package/dist/capability-projection/index.js.map +1 -0
- package/dist/capability-projection/lib/capability-invocation-binding.d.ts +72 -0
- package/dist/capability-projection/lib/capability-invocation-binding.d.ts.map +1 -0
- package/dist/capability-projection/lib/capability-invocation-binding.js +101 -0
- package/dist/capability-projection/lib/capability-invocation-binding.js.map +1 -0
- package/dist/capability-projection/lib/capability-sync-manifest.d.ts +8 -0
- package/dist/capability-projection/lib/capability-sync-manifest.d.ts.map +1 -0
- package/dist/capability-projection/lib/capability-sync-manifest.js +8 -0
- package/dist/capability-projection/lib/capability-sync-manifest.js.map +1 -0
- package/dist/capability-projection/lib/projected-capability.d.ts +15 -0
- package/dist/capability-projection/lib/projected-capability.d.ts.map +1 -0
- package/dist/capability-projection/lib/projected-capability.js +17 -0
- package/dist/capability-projection/lib/projected-capability.js.map +1 -0
- package/dist/connector/lib/connector-adapter-contribution.d.ts +2 -0
- package/dist/connector/lib/connector-adapter-contribution.d.ts.map +1 -1
- package/dist/connector/lib/onboarding-manifest.d.ts +2 -0
- package/dist/connector/lib/onboarding-manifest.d.ts.map +1 -1
- package/dist/connector/lib/onboarding-manifest.js +1 -0
- package/dist/connector/lib/onboarding-manifest.js.map +1 -1
- package/dist/connector/lib/provider-descriptor.d.ts +2 -0
- package/dist/connector/lib/provider-descriptor.d.ts.map +1 -1
- package/dist/contribution/lib/contribution-kind.d.ts +1 -4
- package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
- package/dist/contribution/lib/contribution-kind.js +1 -4
- package/dist/contribution/lib/contribution-kind.js.map +1 -1
- package/dist/distribution/index.d.ts +1 -0
- package/dist/distribution/index.d.ts.map +1 -1
- package/dist/distribution/index.js +1 -0
- package/dist/distribution/index.js.map +1 -1
- package/dist/distribution/lib/distribution-lock.d.ts +6 -0
- package/dist/distribution/lib/distribution-lock.d.ts.map +1 -1
- package/dist/distribution/lib/distribution-lock.js +9 -1
- package/dist/distribution/lib/distribution-lock.js.map +1 -1
- package/dist/distribution/lib/first-party-capability-domains.d.ts +15 -0
- package/dist/distribution/lib/first-party-capability-domains.d.ts.map +1 -0
- package/dist/distribution/lib/first-party-capability-domains.js +52 -0
- package/dist/distribution/lib/first-party-capability-domains.js.map +1 -0
- package/dist/object/lib/xema-object-ref.d.ts +0 -5
- package/dist/object/lib/xema-object-ref.d.ts.map +1 -1
- package/dist/object/lib/xema-object-ref.js +12 -62
- package/dist/object/lib/xema-object-ref.js.map +1 -1
- package/dist/output-surface-inspect/index.d.ts +1 -0
- package/dist/output-surface-inspect/index.d.ts.map +1 -1
- package/dist/output-surface-inspect/index.js +1 -0
- package/dist/output-surface-inspect/index.js.map +1 -1
- package/dist/output-surface-inspect/lib/buffers.d.ts +52 -0
- package/dist/output-surface-inspect/lib/buffers.d.ts.map +1 -0
- package/dist/output-surface-inspect/lib/buffers.js +132 -0
- package/dist/output-surface-inspect/lib/buffers.js.map +1 -0
- package/dist/output-surface-inspect/lib/output-surface-inspect.d.ts +6 -1
- package/dist/output-surface-inspect/lib/output-surface-inspect.d.ts.map +1 -1
- package/dist/output-surface-inspect/lib/output-surface-inspect.js +12 -1
- package/dist/output-surface-inspect/lib/output-surface-inspect.js.map +1 -1
- package/dist/output-surface-inspect/lib/step-schema.d.ts +2 -1
- package/dist/output-surface-inspect/lib/step-schema.d.ts.map +1 -1
- package/dist/output-surface-inspect/lib/step-schema.js +9 -2
- package/dist/output-surface-inspect/lib/step-schema.js.map +1 -1
- package/dist/output-surface-inspect/runtime/index.d.ts +1 -0
- package/dist/output-surface-inspect/runtime/index.d.ts.map +1 -1
- package/dist/output-surface-inspect/runtime/index.js +1 -0
- package/dist/output-surface-inspect/runtime/index.js.map +1 -1
- package/dist/search/index.d.ts +1 -0
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +1 -0
- package/dist/search/index.js.map +1 -1
- package/dist/search/lib/route-template.d.ts +31 -0
- package/dist/search/lib/route-template.d.ts.map +1 -0
- package/dist/search/lib/route-template.js +142 -0
- package/dist/search/lib/route-template.js.map +1 -0
- package/dist/search/lib/search-contribution.d.ts +2 -0
- package/dist/search/lib/search-contribution.d.ts.map +1 -1
- package/dist/search/lib/search-contribution.js +34 -1
- package/dist/search/lib/search-contribution.js.map +1 -1
- package/dist/search-source/lib/indexable-document.d.ts +22 -0
- package/dist/search-source/lib/indexable-document.d.ts.map +1 -1
- package/dist/search-source/lib/indexable-document.js +10 -1
- package/dist/search-source/lib/indexable-document.js.map +1 -1
- package/dist/search-source/lib/search-index-event.d.ts +10 -0
- package/dist/search-source/lib/search-index-event.d.ts.map +1 -1
- package/dist/search-source/lib/search-ref.d.ts.map +1 -1
- package/dist/search-source/lib/search-ref.js +11 -2
- package/dist/search-source/lib/search-ref.js.map +1 -1
- package/dist/search-source/lib/search-replay.d.ts +10 -0
- package/dist/search-source/lib/search-replay.d.ts.map +1 -1
- package/dist/service-registry/lib/service-descriptor.d.ts +1 -0
- package/dist/service-registry/lib/service-descriptor.d.ts.map +1 -1
- package/dist/space/lib/space-ref-parser.d.ts +6 -4
- package/dist/space/lib/space-ref-parser.d.ts.map +1 -1
- package/dist/space/lib/space-ref-parser.js +90 -126
- package/dist/space/lib/space-ref-parser.js.map +1 -1
- package/dist/space/lib/space-traversal.d.ts.map +1 -1
- package/dist/space/lib/space-traversal.js +9 -13
- package/dist/space/lib/space-traversal.js.map +1 -1
- package/dist/space/lib/space.d.ts +9 -0
- package/dist/space/lib/space.d.ts.map +1 -1
- package/dist/space/lib/space.js +59 -29
- package/dist/space/lib/space.js.map +1 -1
- package/dist/subject/lib/subject.d.ts +1 -0
- package/dist/subject/lib/subject.d.ts.map +1 -1
- package/dist/subject/lib/subject.js +1 -0
- package/dist/subject/lib/subject.js.map +1 -1
- package/dist/surface/index.d.ts +4 -0
- package/dist/surface/index.d.ts.map +1 -0
- package/dist/surface/index.js +20 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/surface/lib/surface-contribution.d.ts +24 -0
- package/dist/surface/lib/surface-contribution.d.ts.map +1 -0
- package/dist/surface/lib/surface-contribution.js +23 -0
- package/dist/surface/lib/surface-contribution.js.map +1 -0
- package/dist/surface/lib/surface-kind.d.ts +23 -0
- package/dist/surface/lib/surface-kind.d.ts.map +1 -0
- package/dist/surface/lib/surface-kind.js +27 -0
- package/dist/surface/lib/surface-kind.js.map +1 -0
- package/dist/surface/lib/vista-surface.d.ts +11 -0
- package/dist/surface/lib/vista-surface.d.ts.map +1 -0
- package/dist/surface/lib/vista-surface.js +13 -0
- package/dist/surface/lib/vista-surface.js.map +1 -0
- package/dist/widget/lib/chat-widget-envelope.d.ts.map +1 -1
- package/dist/widget/lib/chat-widget-envelope.js.map +1 -1
- package/dist/workflow/lib/enums.d.ts.map +1 -1
- package/dist/workflow/lib/enums.js.map +1 -1
- package/package.json +1 -1
- package/src/app-definition/lib/automation.ts +3 -2
- package/src/biome/lib/biome-manifest.ts +41 -1
- package/src/capability/index.ts +2 -1
- package/src/capability/lib/authority-effect.ts +88 -0
- package/src/capability/lib/capability-contribution.ts +46 -0
- package/src/capability/lib/capability-domain.ts +51 -0
- package/src/capability/lib/capability-grant.ts +1 -1
- package/src/capability/lib/meta-tool.ts +172 -39
- package/src/capability-projection/index.ts +3 -0
- package/src/capability-projection/lib/capability-invocation-binding.ts +223 -0
- package/src/capability-projection/lib/capability-sync-manifest.ts +52 -0
- package/src/capability-projection/lib/projected-capability.ts +63 -0
- package/src/connector/lib/onboarding-manifest.ts +16 -0
- package/src/contribution/lib/contribution-kind.ts +30 -29
- package/src/distribution/index.ts +1 -0
- package/src/distribution/lib/distribution-lock.ts +36 -0
- package/src/distribution/lib/first-party-capability-domains.ts +166 -0
- package/src/object/lib/xema-object-ref.ts +27 -82
- package/src/output-surface-inspect/index.ts +2 -0
- package/src/output-surface-inspect/lib/buffers.ts +252 -0
- package/src/output-surface-inspect/lib/output-surface-inspect.ts +65 -6
- package/src/output-surface-inspect/lib/step-schema.ts +36 -3
- package/src/output-surface-inspect/runtime/index.ts +1 -0
- package/src/search/index.ts +1 -0
- package/src/search/lib/route-template.ts +381 -0
- package/src/search/lib/search-contribution.ts +84 -11
- package/src/search-source/lib/indexable-document.ts +44 -0
- package/src/search-source/lib/search-ref.ts +22 -9
- package/src/service-registry/lib/service-descriptor.ts +16 -0
- package/src/space/lib/skill-mirror-path.ts +1 -1
- package/src/space/lib/space-ref-parser.ts +190 -165
- package/src/space/lib/space-traversal.ts +34 -17
- package/src/space/lib/space.ts +148 -35
- package/src/subject/lib/subject.ts +16 -0
- package/src/surface/index.ts +3 -0
- package/src/surface/lib/surface-contribution.ts +101 -0
- package/src/surface/lib/surface-kind.ts +65 -0
- package/src/surface/lib/vista-surface.ts +44 -0
- package/src/widget/lib/chat-widget-envelope.ts +6 -2
- package/src/workflow/lib/enums.ts +6 -7
- package/dist/capability/lib/first-party-capability-domains.d.ts +0 -30
- package/dist/capability/lib/first-party-capability-domains.d.ts.map +0 -1
- package/dist/capability/lib/first-party-capability-domains.js +0 -36
- package/dist/capability/lib/first-party-capability-domains.js.map +0 -1
- 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.
|
|
32
|
-
*
|
|
33
|
-
* presence — see the manifest reference)
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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',
|
|
@@ -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 {
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 } =
|
|
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';
|