@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.
- 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 +3 -1
- package/dist/capability/index.d.ts.map +1 -1
- package/dist/capability/index.js +3 -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/capability-reach.d.ts +13 -0
- package/dist/capability/lib/capability-reach.d.ts.map +1 -0
- package/dist/capability/lib/capability-reach.js +34 -0
- package/dist/capability/lib/capability-reach.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 -6
- package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
- package/dist/contribution/lib/contribution-kind.js +1 -6
- 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 +4 -2
- package/dist/search/lib/search-contribution.d.ts.map +1 -1
- package/dist/search/lib/search-contribution.js +46 -4
- 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 +7 -0
- package/dist/space/lib/space.d.ts.map +1 -1
- package/dist/space/lib/space.js +48 -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/biome/lib/biome-manifest.ts +41 -1
- package/src/capability/index.ts +3 -1
- package/src/capability/lib/authority-effect.ts +88 -0
- package/src/capability/lib/capability-contribution.ts +44 -0
- package/src/capability/lib/capability-domain.ts +51 -0
- package/src/capability/lib/capability-grant.ts +1 -1
- package/src/capability/lib/capability-reach.ts +119 -0
- 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 +165 -61
- 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 +138 -31
- 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 +110 -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/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
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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/
|
|
16
|
-
* POST /xema/capabilities/describe
|
|
17
|
-
* POST /xema/capabilities/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
|
-
|
|
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.
|
|
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
|
-
* `
|
|
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
|
-
|
|
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.
|
|
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
|
|
160
|
+
* grant-cache key share the same shape.
|
|
70
161
|
*/
|
|
71
|
-
export interface
|
|
162
|
+
export interface CapabilitySearchRequest {
|
|
72
163
|
readonly executionContext: Record<string, unknown>;
|
|
73
|
-
readonly
|
|
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
|
|
172
|
+
export const CapabilitySearchRequestSchema: z.ZodType<CapabilitySearchRequest> =
|
|
77
173
|
z.object({
|
|
78
174
|
executionContext: z.record(z.string(), z.unknown()),
|
|
79
|
-
|
|
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
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
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
|
|
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
|
|
102
|
-
readonly
|
|
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
|
|
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
|
-
|
|
113
|
-
|
|
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
|
|
117
|
-
readonly capabilities: readonly
|
|
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
|
|
249
|
+
export const CapabilitySearchResponseSchema: z.ZodType<CapabilitySearchResponse> =
|
|
121
250
|
z.object({
|
|
122
|
-
capabilities: z.array(
|
|
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,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>;
|