@xemahq/kernel-contracts 0.50.0 → 0.57.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 (194) hide show
  1. package/dist/biome/lib/biome-manifest.d.ts +9 -0
  2. package/dist/biome/lib/biome-manifest.d.ts.map +1 -1
  3. package/dist/biome/lib/biome-manifest.js +2 -0
  4. package/dist/biome/lib/biome-manifest.js.map +1 -1
  5. package/dist/capability/index.d.ts +3 -1
  6. package/dist/capability/index.d.ts.map +1 -1
  7. package/dist/capability/index.js +3 -1
  8. package/dist/capability/index.js.map +1 -1
  9. package/dist/capability/lib/authority-effect.d.ts +8 -0
  10. package/dist/capability/lib/authority-effect.d.ts.map +1 -0
  11. package/dist/capability/lib/authority-effect.js +17 -0
  12. package/dist/capability/lib/authority-effect.js.map +1 -0
  13. package/dist/capability/lib/capability-contribution.d.ts +4 -0
  14. package/dist/capability/lib/capability-contribution.d.ts.map +1 -1
  15. package/dist/capability/lib/capability-contribution.js +4 -0
  16. package/dist/capability/lib/capability-contribution.js.map +1 -1
  17. package/dist/capability/lib/capability-domain.d.ts +8 -0
  18. package/dist/capability/lib/capability-domain.d.ts.map +1 -0
  19. package/dist/capability/lib/capability-domain.js +24 -0
  20. package/dist/capability/lib/capability-domain.js.map +1 -0
  21. package/dist/capability/lib/capability-reach.d.ts +13 -0
  22. package/dist/capability/lib/capability-reach.d.ts.map +1 -0
  23. package/dist/capability/lib/capability-reach.js +34 -0
  24. package/dist/capability/lib/capability-reach.js.map +1 -0
  25. package/dist/capability/lib/meta-tool.d.ts +51 -14
  26. package/dist/capability/lib/meta-tool.d.ts.map +1 -1
  27. package/dist/capability/lib/meta-tool.js +66 -12
  28. package/dist/capability/lib/meta-tool.js.map +1 -1
  29. package/dist/capability-projection/index.d.ts +4 -0
  30. package/dist/capability-projection/index.d.ts.map +1 -0
  31. package/dist/capability-projection/index.js +20 -0
  32. package/dist/capability-projection/index.js.map +1 -0
  33. package/dist/capability-projection/lib/capability-invocation-binding.d.ts +72 -0
  34. package/dist/capability-projection/lib/capability-invocation-binding.d.ts.map +1 -0
  35. package/dist/capability-projection/lib/capability-invocation-binding.js +101 -0
  36. package/dist/capability-projection/lib/capability-invocation-binding.js.map +1 -0
  37. package/dist/capability-projection/lib/capability-sync-manifest.d.ts +8 -0
  38. package/dist/capability-projection/lib/capability-sync-manifest.d.ts.map +1 -0
  39. package/dist/capability-projection/lib/capability-sync-manifest.js +8 -0
  40. package/dist/capability-projection/lib/capability-sync-manifest.js.map +1 -0
  41. package/dist/capability-projection/lib/projected-capability.d.ts +15 -0
  42. package/dist/capability-projection/lib/projected-capability.d.ts.map +1 -0
  43. package/dist/capability-projection/lib/projected-capability.js +17 -0
  44. package/dist/capability-projection/lib/projected-capability.js.map +1 -0
  45. package/dist/connector/lib/connector-adapter-contribution.d.ts +2 -0
  46. package/dist/connector/lib/connector-adapter-contribution.d.ts.map +1 -1
  47. package/dist/connector/lib/onboarding-manifest.d.ts +2 -0
  48. package/dist/connector/lib/onboarding-manifest.d.ts.map +1 -1
  49. package/dist/connector/lib/onboarding-manifest.js +1 -0
  50. package/dist/connector/lib/onboarding-manifest.js.map +1 -1
  51. package/dist/connector/lib/provider-descriptor.d.ts +2 -0
  52. package/dist/connector/lib/provider-descriptor.d.ts.map +1 -1
  53. package/dist/contribution/lib/contribution-kind.d.ts +1 -6
  54. package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
  55. package/dist/contribution/lib/contribution-kind.js +1 -6
  56. package/dist/contribution/lib/contribution-kind.js.map +1 -1
  57. package/dist/distribution/index.d.ts +1 -0
  58. package/dist/distribution/index.d.ts.map +1 -1
  59. package/dist/distribution/index.js +1 -0
  60. package/dist/distribution/index.js.map +1 -1
  61. package/dist/distribution/lib/distribution-lock.d.ts +6 -0
  62. package/dist/distribution/lib/distribution-lock.d.ts.map +1 -1
  63. package/dist/distribution/lib/distribution-lock.js +9 -1
  64. package/dist/distribution/lib/distribution-lock.js.map +1 -1
  65. package/dist/distribution/lib/first-party-capability-domains.d.ts +15 -0
  66. package/dist/distribution/lib/first-party-capability-domains.d.ts.map +1 -0
  67. package/dist/distribution/lib/first-party-capability-domains.js +52 -0
  68. package/dist/distribution/lib/first-party-capability-domains.js.map +1 -0
  69. package/dist/object/lib/xema-object-ref.d.ts +0 -5
  70. package/dist/object/lib/xema-object-ref.d.ts.map +1 -1
  71. package/dist/object/lib/xema-object-ref.js +12 -62
  72. package/dist/object/lib/xema-object-ref.js.map +1 -1
  73. package/dist/output-surface-inspect/index.d.ts +1 -0
  74. package/dist/output-surface-inspect/index.d.ts.map +1 -1
  75. package/dist/output-surface-inspect/index.js +1 -0
  76. package/dist/output-surface-inspect/index.js.map +1 -1
  77. package/dist/output-surface-inspect/lib/buffers.d.ts +52 -0
  78. package/dist/output-surface-inspect/lib/buffers.d.ts.map +1 -0
  79. package/dist/output-surface-inspect/lib/buffers.js +132 -0
  80. package/dist/output-surface-inspect/lib/buffers.js.map +1 -0
  81. package/dist/output-surface-inspect/lib/output-surface-inspect.d.ts +6 -1
  82. package/dist/output-surface-inspect/lib/output-surface-inspect.d.ts.map +1 -1
  83. package/dist/output-surface-inspect/lib/output-surface-inspect.js +12 -1
  84. package/dist/output-surface-inspect/lib/output-surface-inspect.js.map +1 -1
  85. package/dist/output-surface-inspect/lib/step-schema.d.ts +2 -1
  86. package/dist/output-surface-inspect/lib/step-schema.d.ts.map +1 -1
  87. package/dist/output-surface-inspect/lib/step-schema.js +9 -2
  88. package/dist/output-surface-inspect/lib/step-schema.js.map +1 -1
  89. package/dist/output-surface-inspect/runtime/index.d.ts +1 -0
  90. package/dist/output-surface-inspect/runtime/index.d.ts.map +1 -1
  91. package/dist/output-surface-inspect/runtime/index.js +1 -0
  92. package/dist/output-surface-inspect/runtime/index.js.map +1 -1
  93. package/dist/search/index.d.ts +1 -0
  94. package/dist/search/index.d.ts.map +1 -1
  95. package/dist/search/index.js +1 -0
  96. package/dist/search/index.js.map +1 -1
  97. package/dist/search/lib/route-template.d.ts +31 -0
  98. package/dist/search/lib/route-template.d.ts.map +1 -0
  99. package/dist/search/lib/route-template.js +142 -0
  100. package/dist/search/lib/route-template.js.map +1 -0
  101. package/dist/search/lib/search-contribution.d.ts +4 -2
  102. package/dist/search/lib/search-contribution.d.ts.map +1 -1
  103. package/dist/search/lib/search-contribution.js +46 -4
  104. package/dist/search/lib/search-contribution.js.map +1 -1
  105. package/dist/search-source/lib/indexable-document.d.ts +22 -0
  106. package/dist/search-source/lib/indexable-document.d.ts.map +1 -1
  107. package/dist/search-source/lib/indexable-document.js +10 -1
  108. package/dist/search-source/lib/indexable-document.js.map +1 -1
  109. package/dist/search-source/lib/search-index-event.d.ts +10 -0
  110. package/dist/search-source/lib/search-index-event.d.ts.map +1 -1
  111. package/dist/search-source/lib/search-ref.d.ts.map +1 -1
  112. package/dist/search-source/lib/search-ref.js +11 -2
  113. package/dist/search-source/lib/search-ref.js.map +1 -1
  114. package/dist/search-source/lib/search-replay.d.ts +10 -0
  115. package/dist/search-source/lib/search-replay.d.ts.map +1 -1
  116. package/dist/service-registry/lib/service-descriptor.d.ts +1 -0
  117. package/dist/service-registry/lib/service-descriptor.d.ts.map +1 -1
  118. package/dist/space/lib/space-ref-parser.d.ts +6 -4
  119. package/dist/space/lib/space-ref-parser.d.ts.map +1 -1
  120. package/dist/space/lib/space-ref-parser.js +90 -126
  121. package/dist/space/lib/space-ref-parser.js.map +1 -1
  122. package/dist/space/lib/space-traversal.d.ts.map +1 -1
  123. package/dist/space/lib/space-traversal.js +9 -13
  124. package/dist/space/lib/space-traversal.js.map +1 -1
  125. package/dist/space/lib/space.d.ts +7 -0
  126. package/dist/space/lib/space.d.ts.map +1 -1
  127. package/dist/space/lib/space.js +48 -29
  128. package/dist/space/lib/space.js.map +1 -1
  129. package/dist/subject/lib/subject.d.ts +1 -0
  130. package/dist/subject/lib/subject.d.ts.map +1 -1
  131. package/dist/subject/lib/subject.js +1 -0
  132. package/dist/subject/lib/subject.js.map +1 -1
  133. package/dist/surface/index.d.ts +4 -0
  134. package/dist/surface/index.d.ts.map +1 -0
  135. package/dist/surface/index.js +20 -0
  136. package/dist/surface/index.js.map +1 -0
  137. package/dist/surface/lib/surface-contribution.d.ts +24 -0
  138. package/dist/surface/lib/surface-contribution.d.ts.map +1 -0
  139. package/dist/surface/lib/surface-contribution.js +23 -0
  140. package/dist/surface/lib/surface-contribution.js.map +1 -0
  141. package/dist/surface/lib/surface-kind.d.ts +23 -0
  142. package/dist/surface/lib/surface-kind.d.ts.map +1 -0
  143. package/dist/surface/lib/surface-kind.js +27 -0
  144. package/dist/surface/lib/surface-kind.js.map +1 -0
  145. package/dist/surface/lib/vista-surface.d.ts +11 -0
  146. package/dist/surface/lib/vista-surface.d.ts.map +1 -0
  147. package/dist/surface/lib/vista-surface.js +13 -0
  148. package/dist/surface/lib/vista-surface.js.map +1 -0
  149. package/dist/widget/lib/chat-widget-envelope.d.ts.map +1 -1
  150. package/dist/widget/lib/chat-widget-envelope.js.map +1 -1
  151. package/dist/workflow/lib/enums.d.ts.map +1 -1
  152. package/dist/workflow/lib/enums.js.map +1 -1
  153. package/package.json +1 -1
  154. package/src/biome/lib/biome-manifest.ts +41 -1
  155. package/src/capability/index.ts +3 -1
  156. package/src/capability/lib/authority-effect.ts +88 -0
  157. package/src/capability/lib/capability-contribution.ts +44 -0
  158. package/src/capability/lib/capability-domain.ts +51 -0
  159. package/src/capability/lib/capability-grant.ts +1 -1
  160. package/src/capability/lib/capability-reach.ts +119 -0
  161. package/src/capability/lib/meta-tool.ts +172 -39
  162. package/src/capability-projection/index.ts +3 -0
  163. package/src/capability-projection/lib/capability-invocation-binding.ts +223 -0
  164. package/src/capability-projection/lib/capability-sync-manifest.ts +52 -0
  165. package/src/capability-projection/lib/projected-capability.ts +63 -0
  166. package/src/connector/lib/onboarding-manifest.ts +16 -0
  167. package/src/contribution/lib/contribution-kind.ts +165 -61
  168. package/src/distribution/index.ts +1 -0
  169. package/src/distribution/lib/distribution-lock.ts +36 -0
  170. package/src/distribution/lib/first-party-capability-domains.ts +166 -0
  171. package/src/object/lib/xema-object-ref.ts +27 -82
  172. package/src/output-surface-inspect/index.ts +2 -0
  173. package/src/output-surface-inspect/lib/buffers.ts +252 -0
  174. package/src/output-surface-inspect/lib/output-surface-inspect.ts +65 -6
  175. package/src/output-surface-inspect/lib/step-schema.ts +36 -3
  176. package/src/output-surface-inspect/runtime/index.ts +1 -0
  177. package/src/search/index.ts +1 -0
  178. package/src/search/lib/route-template.ts +381 -0
  179. package/src/search/lib/search-contribution.ts +138 -31
  180. package/src/search-source/lib/indexable-document.ts +44 -0
  181. package/src/search-source/lib/search-ref.ts +22 -9
  182. package/src/service-registry/lib/service-descriptor.ts +16 -0
  183. package/src/space/lib/skill-mirror-path.ts +1 -1
  184. package/src/space/lib/space-ref-parser.ts +190 -165
  185. package/src/space/lib/space-traversal.ts +34 -17
  186. package/src/space/lib/space.ts +110 -35
  187. package/src/subject/lib/subject.ts +16 -0
  188. package/src/surface/index.ts +3 -0
  189. package/src/surface/lib/surface-contribution.ts +101 -0
  190. package/src/surface/lib/surface-kind.ts +65 -0
  191. package/src/surface/lib/vista-surface.ts +44 -0
  192. package/src/widget/lib/chat-widget-envelope.ts +6 -2
  193. package/src/workflow/lib/enums.ts +6 -7
  194. package/src/capability/lib/first-party-capability-domains.ts +0 -77
@@ -0,0 +1,119 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * `CapabilityReach` — DECLARED answer to "how far outside the owner does
5
+ * invoking this capability reach?".
6
+ *
7
+ * WHY THIS EXISTS
8
+ * ---------------
9
+ * `AgentReachTier` (sandbox | as-owner | connected | full) is the CEILING a
10
+ * user picks for an agent. Turning that ceiling into a concrete capability set
11
+ * used to require a hand-maintained list in `authorization-api`
12
+ * (`CONNECTED_TIER_STARTER_CAPABILITIES`) — a central roster that every new
13
+ * integration capability had to be added to, in a different repo from the
14
+ * capability itself. That is the classic drift shape this platform has been
15
+ * removing everywhere else: the fact lives on the thing, not in a list about
16
+ * the thing.
17
+ *
18
+ * A capability now declares its own reach, and the tier resolves from the
19
+ * declarations. Adding an integration capability is a one-line declaration at
20
+ * the producer; no edit to `authorization-api`, and no way to forget one.
21
+ *
22
+ * PRECEDENCE — declared wins, derivation is the floor
23
+ * ---------------------------------------------------
24
+ * Absent ⇒ derived. A `connector`-kind invocation binding reaches an external
25
+ * system by construction, so it derives {@link CapabilityReach.Integration}
26
+ * with no declaration at all. Everything else derives
27
+ * {@link CapabilityReach.Owner}, which is the SAFE default: it grants nothing
28
+ * beyond what the owner already holds.
29
+ *
30
+ * A declaration may therefore only ever be needed to RAISE reach above the
31
+ * derived floor (an http-bound capability that nonetheless touches org-wide
32
+ * state, e.g. the knowledge base). It is deliberately NOT possible to declare
33
+ * a capability as lower-reach than its binding implies — that would let a
34
+ * producer opt its own connector out of the tier system.
35
+ */
36
+ export enum CapabilityReach {
37
+ /**
38
+ * Confined to what the invoking subject already holds. The default, and the
39
+ * only reach an `as-owner` agent gets. Grants no additional authority.
40
+ */
41
+ Owner = 'owner',
42
+ /**
43
+ * Reaches a connected external system (SCM, tracker, mail, the governed
44
+ * public web) or org-wide shared state (the knowledge base). This is exactly
45
+ * the set the `connected` tier opens up.
46
+ */
47
+ Integration = 'integration',
48
+ /**
49
+ * Reaches the Xema control plane itself — installing biomes, minting
50
+ * credentials, altering grants. Only the `full` tier admits these, and the
51
+ * self-escalation guard applies independently.
52
+ */
53
+ Platform = 'platform',
54
+ }
55
+
56
+ export const CapabilityReachSchema = z.nativeEnum(CapabilityReach);
57
+
58
+ /**
59
+ * The reach a capability has when it declares none. Kept as a named constant so
60
+ * the projector, the tier resolver and any future consumer cannot disagree
61
+ * about what "absent" means — and so the safe value is stated once, not
62
+ * repeated at each call site.
63
+ */
64
+ export const DEFAULT_CAPABILITY_REACH = CapabilityReach.Owner;
65
+
66
+ /**
67
+ * Which reaches a given {@link CapabilityReach} tier admits, most-restrictive
68
+ * first. Exported so the tier resolver reads a declared table rather than
69
+ * re-deriving the ordering, and so a new reach member is a compile error at
70
+ * every consumer rather than a silent omission.
71
+ */
72
+ export const REACH_RANK: Readonly<Record<CapabilityReach, number>> = Object.freeze({
73
+ [CapabilityReach.Owner]: 0,
74
+ [CapabilityReach.Integration]: 1,
75
+ [CapabilityReach.Platform]: 2,
76
+ });
77
+
78
+ /** True when `candidate` is within `ceiling`. Total over the closed set. */
79
+ export function reachWithin(candidate: CapabilityReach, ceiling: CapabilityReach): boolean {
80
+ return REACH_RANK[candidate] <= REACH_RANK[ceiling];
81
+ }
82
+
83
+ /**
84
+ * Combine a DERIVED reach with an optional DECLARED one, taking the wider of
85
+ * the two.
86
+ *
87
+ * This is a monotone join, NOT `declared ?? derived`, and the asymmetry is the
88
+ * whole safety property: a declaration may RAISE reach (the knowledge base is
89
+ * `Integration` despite riding an ordinary http route) but can never LOWER it.
90
+ * Without that, a producer could declare its own connector as `Owner` and quietly
91
+ * escape the tier system — the same bypass `joinAuthorityEffect` exists to
92
+ * prevent for the self-escalation guard.
93
+ *
94
+ * Absent declaration ⇒ the derived value, unchanged.
95
+ */
96
+ export function joinReach(
97
+ derived: CapabilityReach,
98
+ declared?: CapabilityReach | undefined,
99
+ ): CapabilityReach {
100
+ if (declared === undefined) return derived;
101
+ return REACH_RANK[declared] > REACH_RANK[derived] ? declared : derived;
102
+ }
103
+
104
+ /**
105
+ * The reach a capability has by construction, before any declaration.
106
+ *
107
+ * A `connector`-kind invocation binding reaches a connected external system —
108
+ * that is what a connector IS — so it derives {@link CapabilityReach.Integration}
109
+ * with no author involvement. Every other binding kind derives the safe floor;
110
+ * raising it is the author's explicit act.
111
+ *
112
+ * Kept here, beside the enum, so the projector and the tier resolver cannot
113
+ * drift on what "derived" means.
114
+ */
115
+ export function deriveReachFromInvocationKind(invocationKind: string): CapabilityReach {
116
+ return invocationKind === 'connector'
117
+ ? CapabilityReach.Integration
118
+ : DEFAULT_CAPABILITY_REACH;
119
+ }
@@ -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>;