@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
@@ -3,23 +3,42 @@ import { z } from 'zod';
3
3
  import { CapabilityRefSchema, type CapabilityRef } from './capability-ref';
4
4
 
5
5
  /**
6
- * The agent-facing MCP surface collapses to exactly three
7
- * meta-tools. The set is CLOSED — no free-form strings — so we ship the
8
- * canonical names as a kernel enum that the router controller, the
9
- * mcp-gateway federation bridge, and any downstream client can all agree on.
6
+ * The agent-facing MCP surface collapses to a fixed, small set of meta-tools.
7
+ * The set is CLOSED — no free-form strings — so we ship the canonical names as
8
+ * a kernel enum that the router controller, the mcp-gateway federation bridge,
9
+ * and any downstream client can all agree on.
10
10
  *
11
11
  * The string values are the wire names emitted to OpenCode via the
12
12
  * mcp-gateway federation bridge (`resolveAsMetaTools()`). They are also the
13
13
  * suffix segments of the router HTTP routes:
14
14
  *
15
- * POST /xema/capabilities/list → MetaToolName.List
16
- * POST /xema/capabilities/describe → MetaToolName.Describe
17
- * POST /xema/capabilities/invoke → MetaToolName.Invoke
15
+ * POST /xema/capabilities/search → MetaToolName.Search
16
+ * POST /xema/capabilities/describe → MetaToolName.Describe
17
+ * POST /xema/capabilities/invoke → MetaToolName.Invoke
18
+ * POST /xema/capabilities/plan → MetaToolName.Plan
19
+ * POST /xema/capabilities/preflight → MetaToolName.Preflight
20
+ * POST /xema/capabilities/explain → MetaToolName.Explain
21
+ *
22
+ * `search` REPLACED the former `list` (gap X11). `list` returned the WHOLE
23
+ * catalogue and probed the PDP once per record, sequentially — O(catalogue)
24
+ * round-trips that collapse the moment capability projection takes the
25
+ * registry past a few hundred rows. `search` is anchor → traverse → rank over
26
+ * the derived capability graph and is O(neighbourhood). There is deliberately
27
+ * NO `list` alias: an unbounded catalogue read is not a supported operation.
28
+ *
29
+ * `plan` / `preflight` / `explain` are the graph's other three answers. They
30
+ * are thin, subject-scoped projections of `capability-registry-api`'s
31
+ * `/capability-graph/*` surface, which OWNS their wire shape — the kernel
32
+ * therefore names them and stops there rather than forking a second copy of
33
+ * those DTOs (see `docs`/`.claude/plans/canopy-universal-agent.md` §5.4).
18
34
  */
19
35
  export enum MetaToolName {
20
- List = 'xema.capabilities.list',
36
+ Search = 'xema.capabilities.search',
21
37
  Describe = 'xema.capabilities.describe',
22
38
  Invoke = 'xema.capabilities.invoke',
39
+ Plan = 'xema.capabilities.plan',
40
+ Preflight = 'xema.capabilities.preflight',
41
+ Explain = 'xema.capabilities.explain',
23
42
  }
24
43
 
25
44
  export const MetaToolNameSchema = z.nativeEnum(MetaToolName);
@@ -38,16 +57,19 @@ export const META_TOOL_AUDIT_TOPIC = 'capability.invoked.v1';
38
57
  * truth.
39
58
  */
40
59
  export const META_TOOL_ROUTE_SUFFIX: Readonly<Record<MetaToolName, string>> = {
41
- [MetaToolName.List]: 'list',
60
+ [MetaToolName.Search]: 'search',
42
61
  [MetaToolName.Describe]: 'describe',
43
62
  [MetaToolName.Invoke]: 'invoke',
63
+ [MetaToolName.Plan]: 'plan',
64
+ [MetaToolName.Preflight]: 'preflight',
65
+ [MetaToolName.Explain]: 'explain',
44
66
  };
45
67
 
46
68
  // ─── Shared sub-types ───────────────────────────────────────────────────────
47
69
 
48
70
  /**
49
71
  * Reference to the biome that ships a capability — surfaced on every
50
- * `CapabilityListEntry` + `CapabilityDescribeEntry` so the agent can show
72
+ * `CapabilitySearchEntry` + `CapabilityDescribeEntry` so the agent can show
51
73
  * provenance ("this tool came from biome X@1.4.0") to the human in the loop.
52
74
  */
53
75
  export interface MetaToolBiomeRef {
@@ -60,66 +82,177 @@ export const MetaToolBiomeRefSchema: z.ZodType<MetaToolBiomeRef> = z.object({
60
82
  version: z.string().min(1),
61
83
  });
62
84
 
63
- // ─── list ───────────────────────────────────────────────────────────────────
85
+ /**
86
+ * Policy decision kinds surfaced by meta-tool projections. Mirrors
87
+ * `@xemahq/policy-contracts/PolicyDecisionKind` value-for-value; we redefine
88
+ * it here so capability-contracts stays a leaf with no upward kernel deps.
89
+ */
90
+ export enum MetaToolPolicyDecision {
91
+ Allow = 'allow',
92
+ Deny = 'deny',
93
+ NeedsApproval = 'needs_approval',
94
+ }
95
+
96
+ export const MetaToolPolicyDecisionSchema = z.nativeEnum(MetaToolPolicyDecision);
97
+
98
+ // ─── search ─────────────────────────────────────────────────────────────────
99
+
100
+ /** Default / hard upper bound on one `search` page. */
101
+ export const META_TOOL_SEARCH_DEFAULT_LIMIT = 20;
102
+ export const META_TOOL_SEARCH_MAX_LIMIT = 50;
103
+
104
+ /**
105
+ * How a capability reaches the anchor resource type of a search.
106
+ *
107
+ * Value-identical to `capability-registry-api`'s `NeighbourhoodRelation`
108
+ * (the graph owns the edges; this is the kernel vocabulary every consumer
109
+ * agrees on). {@link parseCapabilityRelation} fail-fasts if the two ever
110
+ * drift, so the mirror can never rot silently.
111
+ */
112
+ export enum CapabilityRelation {
113
+ /** The capability's ref grammar declares the anchor as its subject. */
114
+ ActsOn = 'acts_on',
115
+ /** The capability takes a reference to the anchor type as input. */
116
+ Consumes = 'consumes',
117
+ }
118
+
119
+ /** Fail-fast parse of a graph-sourced relation string. Never coerces. */
120
+ export function parseCapabilityRelation(raw: string): CapabilityRelation {
121
+ const values = Object.values(CapabilityRelation) as string[];
122
+ if (!values.includes(raw)) {
123
+ throw new Error(
124
+ `Unknown capability relation "${raw}". Expected one of ${values.join(', ')}.`,
125
+ );
126
+ }
127
+ return raw as CapabilityRelation;
128
+ }
129
+
130
+ /**
131
+ * Whether invoking a capability changes state.
132
+ *
133
+ * STRUCTURALLY derived from the capability's persisted
134
+ * `CapabilityInvocationBinding` — never guessed from prose (`sideEffects` is
135
+ * free text) and never guessed from the ref verb (an open vocabulary). An
136
+ * `http` binding states its method, which is decisive; every other binding
137
+ * kind states nothing about mutation, so it is reported as
138
+ * {@link CapabilityMutationClass.Undetermined} rather than assumed either way.
139
+ */
140
+ export enum CapabilityMutationClass {
141
+ Read = 'read',
142
+ Mutating = 'mutating',
143
+ Undetermined = 'undetermined',
144
+ }
145
+
146
+ /** Where a search's anchor resource type came from. */
147
+ export enum CapabilitySearchAnchorSource {
148
+ /** The caller named `resourceType` explicitly. */
149
+ Explicit = 'explicit',
150
+ /** A token of `query` matched a resource type known to the graph. */
151
+ DerivedFromQuery = 'derived-from-query',
152
+ /** No anchor — the search fell back to the bounded catalogue scan. */
153
+ None = 'none',
154
+ }
64
155
 
65
156
  /**
66
- * Request envelope for `xema.capabilities.list`. The router carries an opaque
67
- * `executionContext` payload (validated server-side against
157
+ * Request envelope for `xema.capabilities.search`. The router carries an
158
+ * opaque `executionContext` payload (validated server-side against
68
159
  * `@xemahq/execution-context-contracts`) so the meta-tool surface and the
69
- * grant-cache key on the same shape.
160
+ * grant-cache key share the same shape.
70
161
  */
71
- export interface CapabilityListRequest {
162
+ export interface CapabilitySearchRequest {
72
163
  readonly executionContext: Record<string, unknown>;
73
- readonly includeDenied?: boolean | undefined;
164
+ readonly query?: string | undefined;
165
+ readonly domain?: string | undefined;
166
+ readonly resourceType?: string | undefined;
167
+ readonly mutating?: boolean | undefined;
168
+ readonly limit?: number | undefined;
169
+ readonly cursor?: string | undefined;
74
170
  }
75
171
 
76
- export const CapabilityListRequestSchema: z.ZodType<CapabilityListRequest> =
172
+ export const CapabilitySearchRequestSchema: z.ZodType<CapabilitySearchRequest> =
77
173
  z.object({
78
174
  executionContext: z.record(z.string(), z.unknown()),
79
- includeDenied: z.boolean().optional(),
175
+ query: z.string().optional(),
176
+ domain: z.string().optional(),
177
+ resourceType: z.string().optional(),
178
+ mutating: z.boolean().optional(),
179
+ limit: z
180
+ .number()
181
+ .int()
182
+ .min(1)
183
+ .max(META_TOOL_SEARCH_MAX_LIMIT)
184
+ .optional(),
185
+ cursor: z.string().optional(),
80
186
  });
81
187
 
82
188
  /**
83
- * Policy decision kinds surfaced on each `CapabilityListEntry`. Mirrors
84
- * `@xemahq/policy-contracts/PolicyDecisionKind` value-for-value; we redefine
85
- * it here so capability-contracts stays a leaf with no upward kernel deps.
189
+ * One search hit. Every entry returned is one the subject is AUTHORIZED to
190
+ * invoke — there is no `policyDecision` field and no `includeDenied` switch,
191
+ * because offering an action the caller cannot perform is worse than offering
192
+ * none (the agent will attempt it).
86
193
  */
87
- export enum MetaToolPolicyDecision {
88
- Allow = 'allow',
89
- Deny = 'deny',
90
- NeedsApproval = 'needs_approval',
91
- }
92
-
93
- export const MetaToolPolicyDecisionSchema = z.nativeEnum(MetaToolPolicyDecision);
94
-
95
- export interface CapabilityListEntry {
194
+ export interface CapabilitySearchEntry {
96
195
  readonly ref: CapabilityRef;
97
196
  readonly biome: MetaToolBiomeRef;
98
197
  readonly title: string;
99
198
  readonly summary: string;
100
199
  readonly riskTier: string;
101
- readonly policyDecision: MetaToolPolicyDecision;
102
- readonly denialReason?: string | undefined;
200
+ readonly requiresApproval: boolean;
201
+ readonly mutation: CapabilityMutationClass;
202
+ /** Present only on an anchored search. */
203
+ readonly relation?: CapabilityRelation | undefined;
204
+ /** `child_of` hops from the anchor. Present only on an anchored search. */
205
+ readonly distance?: number | undefined;
206
+ /** Resource type this capability touches. Present only on an anchored search. */
207
+ readonly resourceType?: string | undefined;
103
208
  }
104
209
 
105
- export const CapabilityListEntrySchema: z.ZodType<CapabilityListEntry> =
210
+ export const CapabilitySearchEntrySchema: z.ZodType<CapabilitySearchEntry> =
106
211
  z.object({
107
212
  ref: CapabilityRefSchema,
108
213
  biome: MetaToolBiomeRefSchema,
109
214
  title: z.string().min(1),
110
215
  summary: z.string(),
111
216
  riskTier: z.string().min(1),
112
- policyDecision: MetaToolPolicyDecisionSchema,
113
- denialReason: z.string().optional(),
217
+ requiresApproval: z.boolean(),
218
+ mutation: z.nativeEnum(CapabilityMutationClass),
219
+ relation: z.nativeEnum(CapabilityRelation).optional(),
220
+ distance: z.number().int().min(0).optional(),
221
+ resourceType: z.string().optional(),
222
+ });
223
+
224
+ export interface CapabilitySearchAnchor {
225
+ readonly resourceType: string | null;
226
+ readonly source: CapabilitySearchAnchorSource;
227
+ }
228
+
229
+ export const CapabilitySearchAnchorSchema: z.ZodType<CapabilitySearchAnchor> =
230
+ z.object({
231
+ resourceType: z.string().nullable(),
232
+ source: z.nativeEnum(CapabilitySearchAnchorSource),
114
233
  });
115
234
 
116
- export interface CapabilityListResponse {
117
- readonly capabilities: readonly CapabilityListEntry[];
235
+ export interface CapabilitySearchResponse {
236
+ readonly capabilities: readonly CapabilitySearchEntry[];
237
+ readonly anchor: CapabilitySearchAnchor;
238
+ /**
239
+ * Candidates considered before the authority filter. Compare with
240
+ * `capabilities.length` to see how much authority removed.
241
+ */
242
+ readonly consideredCount: number;
243
+ /** Opaque continuation token. Absent ⇒ this is the last page. */
244
+ readonly nextCursor?: string | undefined;
245
+ /** Graph build generation that answered an anchored search. */
246
+ readonly generation?: number | undefined;
118
247
  }
119
248
 
120
- export const CapabilityListResponseSchema: z.ZodType<CapabilityListResponse> =
249
+ export const CapabilitySearchResponseSchema: z.ZodType<CapabilitySearchResponse> =
121
250
  z.object({
122
- capabilities: z.array(CapabilityListEntrySchema).readonly(),
251
+ capabilities: z.array(CapabilitySearchEntrySchema).readonly(),
252
+ anchor: CapabilitySearchAnchorSchema,
253
+ consideredCount: z.number().int().min(0),
254
+ nextCursor: z.string().optional(),
255
+ generation: z.number().int().min(0).optional(),
123
256
  });
124
257
 
125
258
  // ─── describe ───────────────────────────────────────────────────────────────
@@ -0,0 +1,3 @@
1
+ export * from './lib/capability-invocation-binding';
2
+ export * from './lib/capability-sync-manifest';
3
+ export * from './lib/projected-capability';
@@ -0,0 +1,223 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ ConnectorAdapterKind,
5
+ ConnectorAdapterKindSchema,
6
+ } from '../../connector';
7
+
8
+ /**
9
+ * ─────────────────────────────────────────────────────────────────────────
10
+ * `CapabilityInvocationBinding` — HOW a projected capability is actually
11
+ * invoked, carried as DATA on the capability itself.
12
+ *
13
+ * This is the contract that lets the capability router resolve a target by
14
+ * LOOKUP instead of a hand-maintained `switch` over domains: every producer
15
+ * that projects a capability also declares the binding that executes it.
16
+ *
17
+ * `kind` is a CLOSED set ({@link CapabilityInvocationKind}). Adding a member
18
+ * is a kernel change AND requires a matching dispatch branch in whatever
19
+ * executes bindings — do not add a member that has no executor.
20
+ * ─────────────────────────────────────────────────────────────────────────
21
+ */
22
+ export enum CapabilityInvocationKind {
23
+ /** A direct HTTP call to a first-party service resolved from the registry. */
24
+ Http = 'http',
25
+ /** A connector operation executed by the connector backend. */
26
+ Connector = 'connector',
27
+ /** A workflow dispatched by slug. */
28
+ Workflow = 'workflow',
29
+ /** A tool call proxied to a registered MCP server. */
30
+ Mcp = 'mcp',
31
+ /** An in-process handler resolved by key inside the owning service. */
32
+ Inline = 'inline',
33
+ }
34
+
35
+ export const CapabilityInvocationKindSchema = z.nativeEnum(
36
+ CapabilityInvocationKind,
37
+ );
38
+
39
+ /** HTTP verbs a projected capability may bind to. Closed set. */
40
+ export enum CapabilityHttpMethod {
41
+ Get = 'GET',
42
+ Post = 'POST',
43
+ Put = 'PUT',
44
+ Patch = 'PATCH',
45
+ Delete = 'DELETE',
46
+ }
47
+
48
+ export const CapabilityHttpMethodSchema = z.nativeEnum(CapabilityHttpMethod);
49
+
50
+ /** Where an HTTP parameter is carried on the wire. Closed set. */
51
+ export enum CapabilityHttpParameterIn {
52
+ Path = 'path',
53
+ Query = 'query',
54
+ Header = 'header',
55
+ }
56
+
57
+ export const CapabilityHttpParameterInSchema = z.nativeEnum(
58
+ CapabilityHttpParameterIn,
59
+ );
60
+
61
+ /**
62
+ * RFC-6901 JSON Pointer into the capability INPUT document. Deliberately
63
+ * strict — a pointer is the only supported way to address input data, so a
64
+ * binding can never carry a free-form expression that some executor would
65
+ * have to interpret heuristically.
66
+ */
67
+ const JSON_POINTER_REGEX = /^(?:\/[^/]+)+$/;
68
+
69
+ export const CapabilityInputPointerSchema = z.string().regex(
70
+ JSON_POINTER_REGEX,
71
+ {
72
+ message:
73
+ 'inputPointer must be a non-empty RFC-6901 JSON Pointer into the capability input (e.g. `/projectId`).',
74
+ },
75
+ );
76
+
77
+ /** One HTTP parameter of an {@link HttpCapabilityInvocation}. */
78
+ export interface CapabilityHttpParameterBinding {
79
+ /** Wire name of the parameter (path placeholder / query key / header name). */
80
+ readonly name: string;
81
+ readonly in: CapabilityHttpParameterIn;
82
+ /** JSON Pointer into the capability input that supplies the value. */
83
+ readonly inputPointer: string;
84
+ /** Whether the executor must refuse the call when the pointer resolves to nothing. */
85
+ readonly required: boolean;
86
+ }
87
+
88
+ export const CapabilityHttpParameterBindingSchema: z.ZodType<CapabilityHttpParameterBinding> =
89
+ z.object({
90
+ name: z.string().min(1).max(200),
91
+ in: CapabilityHttpParameterInSchema,
92
+ inputPointer: CapabilityInputPointerSchema,
93
+ required: z.boolean(),
94
+ }) as unknown as z.ZodType<CapabilityHttpParameterBinding>;
95
+
96
+ /** How the request body of an {@link HttpCapabilityInvocation} is built. Closed set. */
97
+ export enum CapabilityHttpBodyMode {
98
+ /** The whole capability input document IS the body. */
99
+ WholeInput = 'whole-input',
100
+ /** A sub-document of the capability input, addressed by `inputPointer`, is the body. */
101
+ InputPointer = 'input-pointer',
102
+ }
103
+
104
+ export const CapabilityHttpBodyModeSchema = z.nativeEnum(
105
+ CapabilityHttpBodyMode,
106
+ );
107
+
108
+ export interface CapabilityHttpBodyBinding {
109
+ readonly mode: CapabilityHttpBodyMode;
110
+ /** REQUIRED iff `mode` is `input-pointer`; MUST be absent otherwise. */
111
+ readonly inputPointer?: string;
112
+ }
113
+
114
+ export const CapabilityHttpBodyBindingSchema: z.ZodType<CapabilityHttpBodyBinding> =
115
+ z
116
+ .object({
117
+ mode: CapabilityHttpBodyModeSchema,
118
+ inputPointer: CapabilityInputPointerSchema.optional(),
119
+ })
120
+ .superRefine((value, ctx) => {
121
+ const needsPointer = value.mode === CapabilityHttpBodyMode.InputPointer;
122
+ if (needsPointer && value.inputPointer === undefined) {
123
+ ctx.addIssue({
124
+ code: 'custom',
125
+ path: ['inputPointer'],
126
+ message: 'inputPointer is REQUIRED when mode is `input-pointer`.',
127
+ });
128
+ }
129
+ if (!needsPointer && value.inputPointer !== undefined) {
130
+ ctx.addIssue({
131
+ code: 'custom',
132
+ path: ['inputPointer'],
133
+ message: 'inputPointer is only allowed when mode is `input-pointer`.',
134
+ });
135
+ }
136
+ }) as unknown as z.ZodType<CapabilityHttpBodyBinding>;
137
+
138
+ /** Service name as registered in the kernel service registry (kebab-case). */
139
+ const SERVICE_NAME_REGEX = /^[a-z][a-z0-9-]*$/;
140
+
141
+ export interface HttpCapabilityInvocation {
142
+ readonly kind: CapabilityInvocationKind.Http;
143
+ /** Registry name of the target service — resolved, never a hardcoded host. */
144
+ readonly serviceName: string;
145
+ readonly method: CapabilityHttpMethod;
146
+ /** Route template with `:param` / `{param}` placeholders, always absolute. */
147
+ readonly path: string;
148
+ readonly parameters: readonly CapabilityHttpParameterBinding[];
149
+ /** Absent for verbs that carry no body. */
150
+ readonly body?: CapabilityHttpBodyBinding;
151
+ }
152
+
153
+ export interface ConnectorCapabilityInvocation {
154
+ readonly kind: CapabilityInvocationKind.Connector;
155
+ /** Connector descriptor key (e.g. `ACME_CRM`). */
156
+ readonly connectorKey: string;
157
+ /** Operation key within the connector (its capability verb). */
158
+ readonly operationKey: string;
159
+ /**
160
+ * The operation's OWN backend discriminator. It is part of the BINDING (not
161
+ * of the connector) because two operations of the same connector may be
162
+ * served by different backends, and it is what the capability router
163
+ * dispatches on today.
164
+ */
165
+ readonly adapterKind: ConnectorAdapterKind;
166
+ }
167
+
168
+ export interface WorkflowCapabilityInvocation {
169
+ readonly kind: CapabilityInvocationKind.Workflow;
170
+ readonly workflowSlug: string;
171
+ }
172
+
173
+ export interface McpCapabilityInvocation {
174
+ readonly kind: CapabilityInvocationKind.Mcp;
175
+ readonly serverId: string;
176
+ readonly toolName: string;
177
+ }
178
+
179
+ export interface InlineCapabilityInvocation {
180
+ readonly kind: CapabilityInvocationKind.Inline;
181
+ /** Key the owning service resolves to an in-process handler. */
182
+ readonly handlerKey: string;
183
+ }
184
+
185
+ export type CapabilityInvocationBinding =
186
+ | HttpCapabilityInvocation
187
+ | ConnectorCapabilityInvocation
188
+ | WorkflowCapabilityInvocation
189
+ | McpCapabilityInvocation
190
+ | InlineCapabilityInvocation;
191
+
192
+ export const CapabilityInvocationBindingSchema: z.ZodType<CapabilityInvocationBinding> =
193
+ z.discriminatedUnion('kind', [
194
+ z.object({
195
+ kind: z.literal(CapabilityInvocationKind.Http),
196
+ serviceName: z.string().regex(SERVICE_NAME_REGEX, {
197
+ message: 'serviceName must be a kebab-case service-registry name.',
198
+ }),
199
+ method: CapabilityHttpMethodSchema,
200
+ path: z.string().startsWith('/').min(1).max(2000),
201
+ parameters: z.array(CapabilityHttpParameterBindingSchema),
202
+ body: CapabilityHttpBodyBindingSchema.optional(),
203
+ }),
204
+ z.object({
205
+ kind: z.literal(CapabilityInvocationKind.Connector),
206
+ connectorKey: z.string().min(1).max(200),
207
+ operationKey: z.string().min(1).max(200),
208
+ adapterKind: ConnectorAdapterKindSchema,
209
+ }),
210
+ z.object({
211
+ kind: z.literal(CapabilityInvocationKind.Workflow),
212
+ workflowSlug: z.string().min(1).max(200),
213
+ }),
214
+ z.object({
215
+ kind: z.literal(CapabilityInvocationKind.Mcp),
216
+ serverId: z.string().min(1).max(200),
217
+ toolName: z.string().min(1).max(200),
218
+ }),
219
+ z.object({
220
+ kind: z.literal(CapabilityInvocationKind.Inline),
221
+ handlerKey: z.string().min(1).max(200),
222
+ }),
223
+ ]) as unknown as z.ZodType<CapabilityInvocationBinding>;
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ CapabilityContributionManifestSchema,
5
+ type CapabilityContributionManifest,
6
+ } from '../../capability';
7
+ import {
8
+ CapabilityInvocationBindingSchema,
9
+ type CapabilityInvocationBinding,
10
+ } from './capability-invocation-binding';
11
+
12
+ /**
13
+ * ─────────────────────────────────────────────────────────────────────────
14
+ * `CapabilitySyncManifest` — what an AUTHOR declares when publishing a
15
+ * capability, on EVERY publish path.
16
+ *
17
+ * It is the kernel `CapabilityContributionManifest` (what the capability IS)
18
+ * plus an optional {@link CapabilityInvocationBinding} (HOW it is invoked).
19
+ * The two are split across subpaths on purpose — `CapabilityInvocationBinding`
20
+ * depends on the `connector` subpath, which in turn depends on `capability`, so
21
+ * putting `invocation` directly on the contribution manifest would close a
22
+ * subpath cycle. This composition lives HERE, next to `ProjectedCapability`,
23
+ * which composes the same two halves for the DERIVED path.
24
+ *
25
+ * One shape, every producer:
26
+ * - a `*.capability.contribution.json` file / an `xema.contributions.inline[]`
27
+ * entry, parsed by `biome-host-api`'s `CapabilityParser`;
28
+ * - the `@XemaCapability` decorator's boot-time runtime sync;
29
+ * - `capability-registry-api`'s own contribution-sync handler.
30
+ * All three end on `POST /capabilities/sync` with this exact payload.
31
+ *
32
+ * `invocation` is OPTIONAL on the contract because a capability may instead be
33
+ * backed by an org `CapabilityProviderBinding` or by a live runner. It is NOT a
34
+ * soft default: a ref with none of the three FAILS FAST at the capability-router
35
+ * (`CapabilityInvocationBindingMissingError`) rather than degrading.
36
+ *
37
+ * NOTE the mechanical reason this is an INTERSECTION and not a widened object:
38
+ * `CapabilityContributionManifestSchema` is a `z.object` and therefore STRIPS
39
+ * unknown keys, so parsing an envelope through it alone would silently drop the
40
+ * binding. Intersecting merges both parsed outputs, so the binding survives —
41
+ * validated by the same kernel schema every other path uses.
42
+ * ─────────────────────────────────────────────────────────────────────────
43
+ */
44
+ export interface CapabilitySyncManifest extends CapabilityContributionManifest {
45
+ readonly invocation?: CapabilityInvocationBinding;
46
+ }
47
+
48
+ export const CapabilitySyncManifestSchema: z.ZodType<CapabilitySyncManifest> =
49
+ z.intersection(
50
+ CapabilityContributionManifestSchema,
51
+ z.object({ invocation: CapabilityInvocationBindingSchema.optional() }),
52
+ ) as unknown as z.ZodType<CapabilitySyncManifest>;
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ CapabilityContributionManifestSchema,
5
+ type CapabilityContributionManifest,
6
+ } from '../../capability';
7
+ import {
8
+ CapabilityInvocationBindingSchema,
9
+ type CapabilityInvocationBinding,
10
+ } from './capability-invocation-binding';
11
+
12
+ /**
13
+ * Where a projected capability came from.
14
+ *
15
+ * `sourceKey` scopes the projector's PRUNE set: a projector may only ever
16
+ * delete refs it previously projected, and a ref is attributed to exactly one
17
+ * source per reconcile. `biomeId` / `biomeVersion` are the same provenance the
18
+ * contribution path stamps — authors never declare them inline.
19
+ */
20
+ export interface ProjectedCapabilityProvenance {
21
+ readonly sourceKey: string;
22
+ readonly biomeId: string;
23
+ readonly biomeVersion: string;
24
+ }
25
+
26
+ export const ProjectedCapabilityProvenanceSchema: z.ZodType<ProjectedCapabilityProvenance> =
27
+ z.object({
28
+ sourceKey: z.string().min(1).max(200),
29
+ biomeId: z.string().min(1).max(200),
30
+ biomeVersion: z.string().min(1).max(200),
31
+ }) as unknown as z.ZodType<ProjectedCapabilityProvenance>;
32
+
33
+ /**
34
+ * ─────────────────────────────────────────────────────────────────────────
35
+ * `ProjectedCapability` — one capability DERIVED by a producer, rather than
36
+ * hand-authored as a contribution file.
37
+ *
38
+ * It deliberately EXTENDS {@link CapabilityContributionManifest} instead of
39
+ * restating its fields: the hand-authored path, the decorator path and the
40
+ * projection path must all end up on the SAME `POST /capabilities/register`
41
+ * wire shape, so there is exactly one place that shape is described. A
42
+ * projection adds only the two things a hand-authored descriptor does not
43
+ * carry:
44
+ *
45
+ * - {@link CapabilityInvocationBinding} — HOW to invoke it, as data, so the
46
+ * router resolves a target by lookup instead of a hardcoded switch;
47
+ * - {@link ProjectedCapabilityProvenance} — WHICH source owns it, which is
48
+ * what scopes the reconcile engine's prune set.
49
+ * ─────────────────────────────────────────────────────────────────────────
50
+ */
51
+ export interface ProjectedCapability extends CapabilityContributionManifest {
52
+ readonly invocation: CapabilityInvocationBinding;
53
+ readonly provenance: ProjectedCapabilityProvenance;
54
+ }
55
+
56
+ export const ProjectedCapabilitySchema: z.ZodType<ProjectedCapability> = z
57
+ .intersection(
58
+ CapabilityContributionManifestSchema,
59
+ z.object({
60
+ invocation: CapabilityInvocationBindingSchema,
61
+ provenance: ProjectedCapabilityProvenanceSchema,
62
+ }),
63
+ ) as unknown as z.ZodType<ProjectedCapability>;
@@ -99,6 +99,21 @@ export interface CredentialFieldSpec {
99
99
  readonly type: CredentialFieldType;
100
100
  /** When true, the wizard refuses submission unless the field is filled. */
101
101
  readonly required: boolean;
102
+ /**
103
+ * DECLARED secrecy of the field's value. `true` ⇒ the value is a secret and
104
+ * MUST travel to credential custody (the broker's opaque `secretValue`
105
+ * bundle); `false` ⇒ the value is non-secret configuration and is stored in
106
+ * the provision's plain JSON config.
107
+ *
108
+ * This is a DECLARATION, never an inference. Secrecy was previously derived
109
+ * from `type === CredentialFieldType.PASSWORD`, which made a *rendering*
110
+ * affordance load-bearing for a *custody* decision: a secret rendered as
111
+ * `TEXT` (an API key an author wanted visible) silently landed in
112
+ * non-secret storage, and a non-secret masked for ergonomics silently landed
113
+ * in custody. `type` now means "how to render it" and `isSecret` means
114
+ * "where it may be stored" — the two are independent and both required.
115
+ */
116
+ readonly isSecret: boolean;
102
117
  /** Optional placeholder copy. */
103
118
  readonly placeholder?: string;
104
119
  /** Optional secondary hint shown beneath the input. */
@@ -119,6 +134,7 @@ export const CredentialFieldSpecSchema = z.strictObject({
119
134
  label: z.string().min(1),
120
135
  type: z.nativeEnum(CredentialFieldType),
121
136
  required: z.boolean(),
137
+ isSecret: z.boolean(),
122
138
  placeholder: z.string().optional(),
123
139
  hint: z.string().optional(),
124
140
  transform: z.nativeEnum(CredentialFieldTransform),