@uppercut-labs/agent-native 0.0.0-stage → 1.1.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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +164 -2
  3. package/agent/agent-prompt-short.md +1 -0
  4. package/agent/agent-prompt.md +26 -0
  5. package/bin/agent-native-site.mjs +269 -0
  6. package/bin/discover-routes.mjs +162 -0
  7. package/bin/verify-publication.mjs +317 -0
  8. package/dist/adapters/zod.d.ts +4 -0
  9. package/dist/adapters/zod.d.ts.map +1 -0
  10. package/dist/adapters/zod.js +20 -0
  11. package/dist/adapters/zod.js.map +1 -0
  12. package/dist/astro.d.ts +13 -0
  13. package/dist/astro.d.ts.map +1 -0
  14. package/dist/astro.js +41 -0
  15. package/dist/astro.js.map +1 -0
  16. package/dist/auth.d.ts +52 -0
  17. package/dist/auth.d.ts.map +1 -0
  18. package/dist/auth.js +128 -0
  19. package/dist/auth.js.map +1 -0
  20. package/dist/browser.d.ts +47 -0
  21. package/dist/browser.d.ts.map +1 -0
  22. package/dist/browser.js +219 -0
  23. package/dist/browser.js.map +1 -0
  24. package/dist/cli.d.ts +80 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +484 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/core/assert-never.d.ts +2 -0
  29. package/dist/core/assert-never.d.ts.map +1 -0
  30. package/dist/core/assert-never.js +5 -0
  31. package/dist/core/assert-never.js.map +1 -0
  32. package/dist/core/binding-internal.d.ts +10 -0
  33. package/dist/core/binding-internal.d.ts.map +1 -0
  34. package/dist/core/binding-internal.js +15 -0
  35. package/dist/core/binding-internal.js.map +1 -0
  36. package/dist/core/composition.d.ts +119 -0
  37. package/dist/core/composition.d.ts.map +1 -0
  38. package/dist/core/composition.js +545 -0
  39. package/dist/core/composition.js.map +1 -0
  40. package/dist/core/contracts.d.ts +55 -0
  41. package/dist/core/contracts.d.ts.map +1 -0
  42. package/dist/core/contracts.js +271 -0
  43. package/dist/core/contracts.js.map +1 -0
  44. package/dist/core/diagnostics.d.ts +14 -0
  45. package/dist/core/diagnostics.d.ts.map +1 -0
  46. package/dist/core/diagnostics.js +29 -0
  47. package/dist/core/diagnostics.js.map +1 -0
  48. package/dist/core/executor.d.ts +52 -0
  49. package/dist/core/executor.d.ts.map +1 -0
  50. package/dist/core/executor.js +98 -0
  51. package/dist/core/executor.js.map +1 -0
  52. package/dist/core/registry.d.ts +37 -0
  53. package/dist/core/registry.d.ts.map +1 -0
  54. package/dist/core/registry.js +75 -0
  55. package/dist/core/registry.js.map +1 -0
  56. package/dist/core/schema.d.ts +9 -0
  57. package/dist/core/schema.d.ts.map +1 -0
  58. package/dist/core/schema.js +36 -0
  59. package/dist/core/schema.js.map +1 -0
  60. package/dist/discovery.d.ts +16 -0
  61. package/dist/discovery.d.ts.map +1 -0
  62. package/dist/discovery.js +23 -0
  63. package/dist/discovery.js.map +1 -0
  64. package/dist/doctor.d.ts +200 -0
  65. package/dist/doctor.d.ts.map +1 -0
  66. package/dist/doctor.js +287 -0
  67. package/dist/doctor.js.map +1 -0
  68. package/dist/harness-codex.d.ts +17 -0
  69. package/dist/harness-codex.d.ts.map +1 -0
  70. package/dist/harness-codex.js +485 -0
  71. package/dist/harness-codex.js.map +1 -0
  72. package/dist/harness-mcp-bridge.d.ts +14 -0
  73. package/dist/harness-mcp-bridge.d.ts.map +1 -0
  74. package/dist/harness-mcp-bridge.js +159 -0
  75. package/dist/harness-mcp-bridge.js.map +1 -0
  76. package/dist/harness.d.ts +72 -0
  77. package/dist/harness.d.ts.map +1 -0
  78. package/dist/harness.js +101 -0
  79. package/dist/harness.js.map +1 -0
  80. package/dist/http.d.ts +20 -0
  81. package/dist/http.d.ts.map +1 -0
  82. package/dist/http.js +537 -0
  83. package/dist/http.js.map +1 -0
  84. package/dist/index.d.ts +16 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +8 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/init.d.ts +73 -0
  89. package/dist/init.d.ts.map +1 -0
  90. package/dist/init.js +1039 -0
  91. package/dist/init.js.map +1 -0
  92. package/dist/mcp-apps.d.ts +9 -0
  93. package/dist/mcp-apps.d.ts.map +1 -0
  94. package/dist/mcp-apps.js +171 -0
  95. package/dist/mcp-apps.js.map +1 -0
  96. package/dist/mcp-core.d.ts +64 -0
  97. package/dist/mcp-core.d.ts.map +1 -0
  98. package/dist/mcp-core.js +358 -0
  99. package/dist/mcp-core.js.map +1 -0
  100. package/dist/mcp.d.ts +3 -0
  101. package/dist/mcp.d.ts.map +1 -0
  102. package/dist/mcp.js +2 -0
  103. package/dist/mcp.js.map +1 -0
  104. package/dist/next-browser.d.ts +14 -0
  105. package/dist/next-browser.d.ts.map +1 -0
  106. package/dist/next-browser.js +14 -0
  107. package/dist/next-browser.js.map +1 -0
  108. package/dist/next.d.ts +24 -0
  109. package/dist/next.d.ts.map +1 -0
  110. package/dist/next.js +28 -0
  111. package/dist/next.js.map +1 -0
  112. package/dist/package-version.d.ts +2 -0
  113. package/dist/package-version.d.ts.map +1 -0
  114. package/dist/package-version.js +3 -0
  115. package/dist/package-version.js.map +1 -0
  116. package/dist/publication.d.ts +115 -0
  117. package/dist/publication.d.ts.map +1 -0
  118. package/dist/publication.js +441 -0
  119. package/dist/publication.js.map +1 -0
  120. package/dist/registry.d.ts +3 -0
  121. package/dist/registry.d.ts.map +1 -0
  122. package/dist/registry.js +2 -0
  123. package/dist/registry.js.map +1 -0
  124. package/docs/adopt-existing-site.md +189 -0
  125. package/docs/browser.md +70 -0
  126. package/docs/capabilities-and-bindings.md +141 -0
  127. package/docs/cli.md +88 -0
  128. package/docs/compatibility.md +32 -0
  129. package/docs/composition-and-versioning.md +113 -0
  130. package/docs/doctor-and-troubleshooting.md +58 -0
  131. package/docs/evidence/GHSA-ch52-4w7c-c8xp-2026-10-03.md +17 -0
  132. package/docs/evidence/GHSA-ch52-4w7c-c8xp-2026-10-10.md +30 -0
  133. package/docs/evidence/GHSA-ch52-4w7c-c8xp-2026-10-12.md +10 -0
  134. package/docs/evidence/UAN-002-house-style-review.md +29 -0
  135. package/docs/evidence/UAN-007-astro.md +43 -0
  136. package/docs/evidence/UAN-008-worker-sidecar.md +56 -0
  137. package/docs/evidence/UAN-009-init.md +43 -0
  138. package/docs/evidence/UAN-010-auth.md +17 -0
  139. package/docs/evidence/UAN-011-discovery.md +18 -0
  140. package/docs/evidence/UAN-012-mcp-apps.md +14 -0
  141. package/docs/evidence/UAN-013-astro-on-demand.md +47 -0
  142. package/docs/evidence/UAN-014-next.md +57 -0
  143. package/docs/evidence/UAN-015-node-hono.md +69 -0
  144. package/docs/evidence/UAN-016-composition.md +52 -0
  145. package/docs/evidence/UAN-017-version-migrations.md +50 -0
  146. package/docs/evidence/UAN-018-surface-overrides.md +30 -0
  147. package/docs/evidence/UAN-019-doctor.md +19 -0
  148. package/docs/evidence/UAN-020-negative-security-fixtures.md +61 -0
  149. package/docs/evidence/UAN-020-operational-failure-safety.md +49 -0
  150. package/docs/evidence/UAN-021-docs-coverage.md +30 -0
  151. package/docs/evidence/UAN-022-cross-platform-license-notices.md +39 -0
  152. package/docs/evidence/UAN-022-overhead-license-audit.md +233 -0
  153. package/docs/evidence/UAN-022-package-portability.md +54 -0
  154. package/docs/evidence/UAN-023-commercial-agent-hosts.md +36 -0
  155. package/docs/evidence/UAN-023-inspector-host-probe.md +37 -0
  156. package/docs/evidence/UAN-023-inspector-web-and-astro-adoption.md +44 -0
  157. package/docs/evidence/UAN-023-native-webmcp-chrome.md +44 -0
  158. package/docs/evidence/UAN-024-release-readiness-2026-10-10.md +33 -0
  159. package/docs/evidence/UAN-024-release-readiness.md +41 -0
  160. package/docs/evidence/codex-harness.md +56 -0
  161. package/docs/evidence/gate-2-domain-expansion.md +35 -0
  162. package/docs/evidence/recovery-and-selection-recipes-2026-10-15.md +63 -0
  163. package/docs/for-agents.md +50 -0
  164. package/docs/frameworks.md +62 -0
  165. package/docs/getting-started.md +109 -0
  166. package/docs/harness-codex.md +117 -0
  167. package/docs/harness-mcp-bridge.md +30 -0
  168. package/docs/http.md +80 -0
  169. package/docs/installation.md +95 -0
  170. package/docs/mcp-apps.md +57 -0
  171. package/docs/mcp.md +178 -0
  172. package/docs/next.md +73 -0
  173. package/docs/permissions-and-discovery.md +58 -0
  174. package/docs/programmatic-harness.md +88 -0
  175. package/docs/readability-frameworks.md +18 -0
  176. package/docs/reference.md +161 -0
  177. package/docs/release-handoff.md +41 -0
  178. package/docs/site-publication.md +85 -0
  179. package/docs/stability.md +53 -0
  180. package/docs/static-sites.md +141 -0
  181. package/docs/support-and-limitations.md +20 -0
  182. package/docs/surfaces.md +27 -0
  183. package/docs/upgrading-to-1.0.md +26 -0
  184. package/package.json +188 -4
  185. package/skill/agent-native/SKILL.md +54 -0
  186. package/skill/agent-native/references/adapter-decision.md +19 -0
  187. package/skill/agent-native/references/verification.md +25 -0
@@ -0,0 +1,189 @@
1
+ # Make an existing website agent-native
2
+
3
+ **Target:** Agent Native 1.0.1 · Node.js 22+ · an existing JavaScript/TypeScript website.
4
+ **Result:** A real operation the website already supports, available to an agent over an actual HTTP and/or MCP server surface, with the same input checks and explicit authorization the application expects.
5
+
6
+ Agent Native is not a chatbot widget or an automatic site conversion tool. It defines reusable operations that software—including suitable agents—can discover and invoke through selected adapters. Your existing pages, functions and ownership rules remain your responsibility.
7
+
8
+ ## 1. Identify one operation your website already provides
9
+
10
+ Choose something low risk: search public content, retrieve an album, list public catalog entries, or convert a value. Find the *existing function* your website already calls. Prefer a public read on your first pass, not user-specific private data or an update/delete. Record the input/output shape and whether an ordinary visitor is entitled to the result.
11
+
12
+ A representative complete example is [E10 existing functions retrofit](https://github.com/uppercut-labs/agent-native/tree/main/examples/e10-existing-functions-retrofit). It leaves an existing `searchContent({query, limit})` function intact, defines a typed Agent Native capability around it, and preserves the site's established `GET /api/content/search` shape. It is much closer to a real integration than inventing a new `greet` operation.
13
+
14
+ Compare any user-named stack or host with the package manifest, lockfile, actual entry point, and build/deployment configuration. Explain a mismatch and follow the explicit request; then choose an adapter for the website's actual execution model:
15
+
16
+ | Your website | How remote agents call it | Example and limitation |
17
+ | --- | --- | --- |
18
+ | Existing Node/Hono server | Mount a standard Web `Request`/`Response` HTTP or MCP handler in your current router | [E05](https://github.com/uppercut-labs/agent-native/tree/main/examples/e05-node-cli); app must provide TLS/auth/rate limits |
19
+ | Next.js App Router | Use package `/next` route factory on Node runtime | [E03](https://github.com/uppercut-labs/agent-native/tree/main/examples/e03-next-reading-list); keep registry server-only |
20
+ | Astro on-demand | Add app-owned endpoints with an existing compatible server adapter | [E02](https://github.com/uppercut-labs/agent-native/tree/main/examples/e02-astro-on-demand-catalog); do not silently change output modes |
21
+ | Astro fully static | Use a separate Worker/sidecar server for HTTP/MCP while leaving site prerendered | [E01/E04](https://github.com/uppercut-labs/agent-native/tree/main/examples); static HTML cannot answer a dynamic remote MCP request by itself |
22
+ | Browser-local only | Optional WebMCP browser registration with ordinary human fallback | [E06](https://github.com/uppercut-labs/agent-native/tree/main/examples/e06-browser-only-theme-controls); draft browser API, not universal host support |
23
+
24
+ ## 2. Install the version you can actually test
25
+
26
+ In the consuming website repository with Node 22 or later, install the public package:
27
+
28
+ ```sh
29
+ npm install @uppercut-labs/agent-native@1.0.1
30
+ npm install zod@4.6.5
31
+ ```
32
+
33
+ Zod is only needed if you use the `/schema/zod` adapter. Check the app’s current pin
34
+ first; an incompatible schema peer can stay isolated in the separate sidecar instead
35
+ of silently changing the existing website’s dependencies. To serve MCP, add the tested official server SDK peer separately:
36
+
37
+ ```sh
38
+ npm install @modelcontextprotocol/server@2.3.0
39
+ ```
40
+
41
+ For official client verification, install `npm install --save-dev @modelcontextprotocol/client@2.3.0`.
42
+ Import `Client` and `StreamableHTTPClientTransport` from `@modelcontextprotocol/client`;
43
+ connect the transport to your actual mounted MCP URL, list tools, and call the expected
44
+ capability with its schema-valid input. For Next, also install `server-only@0.0.1` and
45
+ follow the [authorized route example](next.md).
46
+
47
+ No package-level `agent-native` executable ships. The `@uppercut-labs/agent-native/init` import is an experimental, narrowly recognized programmatic Astro static retrofit; it does **not** automatically discover your business functions, publish a Worker or install a global `init` command. See [installation](installation.md) and [Astro init details](getting-started.md).
48
+
49
+ ## 3. Define and bind your **existing** function
50
+
51
+ E10's [capability module](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/src/capability.mjs) is a runnable reference. It uses:
52
+
53
+ - `defineCapability({identity, description, input, output, risk:'read', access:{kind:'public'}})` from `/contracts`;
54
+ - `fromZod(z.object(...))` from `/schema/zod` for input and output contracts;
55
+ - `bindCapability(capability, {id, targets:['local','server'], execute: existingFunction})` from `/registry`;
56
+ - `createCapabilityRegistry([capability], [binding])` to check definitions and bindings.
57
+
58
+ The tested definition, binding and public-read-only authorizer are:
59
+
60
+ <!-- source:examples/e10-existing-functions-retrofit/project/src/capability.mjs#adoption-contract -->
61
+ ~~~js
62
+ const contentItem = z.object({
63
+ slug: z.string(),
64
+ title: z.string(),
65
+ summary: z.string(),
66
+ });
67
+
68
+ export const searchContentCapability = defineCapability({
69
+ identity: { namespace: 'content', name: 'search', majorVersion: 1 },
70
+ description: 'Search the existing public content collection.',
71
+ input: fromZod(
72
+ z.object({
73
+ query: z.string().trim().min(1).max(80),
74
+ limit: z.number().int().min(1).max(10).optional(),
75
+ }),
76
+ ),
77
+ output: fromZod(
78
+ z.object({
79
+ query: z.string(),
80
+ results: z.array(contentItem),
81
+ }),
82
+ ),
83
+ risk: 'read',
84
+ access: { kind: 'public' },
85
+ surfaces: {
86
+ // docs:start http-get-override
87
+ http: {
88
+ path: '/api/content/search',
89
+ method: 'GET',
90
+ query: { query: 'q' },
91
+ },
92
+ // docs:end http-get-override
93
+ // docs:start cli-command-override
94
+ cli: {
95
+ command: 'content-search',
96
+ aliases: ['search'],
97
+ },
98
+ // docs:end cli-command-override
99
+ },
100
+ });
101
+
102
+ export const searchContentBinding = bindCapability(searchContentCapability, {
103
+ id: 'existing-content-search',
104
+ targets: ['local', 'server'],
105
+ execute: searchContent,
106
+ });
107
+
108
+ export const registry = createCapabilityRegistry([searchContentCapability], [searchContentBinding]);
109
+
110
+ export const publicReadAuthorization = {
111
+ authorize(request) {
112
+ return request.risk === 'read' && request.access.kind === 'public';
113
+ },
114
+ };
115
+ ~~~
116
+
117
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/src/capability.mjs#L8)
118
+ <!-- /source -->
119
+
120
+ The example preserves an established GET route with the same contract, instead of inventing a duplicate endpoint:
121
+
122
+ <!-- source:examples/e10-existing-functions-retrofit/project/src/capability.mjs#http-get-override -->
123
+ ~~~js
124
+ http: {
125
+ path: '/api/content/search',
126
+ method: 'GET',
127
+ query: { query: 'q' },
128
+ },
129
+ ~~~
130
+
131
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/src/capability.mjs#L33)
132
+ <!-- /source -->
133
+
134
+ Identity namespace and name use lower-case slug segments separated by dots or hyphens,
135
+ such as `{ namespace: 'blog', name: 'list-posts', majorVersion: 1 }`.
136
+
137
+ For your own site, adapt the namespace/operation names and input/output schemas from the actual operation. A registry is not a permission policy: supply an application authorization port **on every execution**, even for explicitly public reads. The E10 sample authorizer permits exactly public reads. Do not copy it to a protected write or assume an unauthenticated visitor owns someone else's data. [Capabilities and bindings](capabilities-and-bindings.md) explains this boundary.
138
+
139
+ ## 4. Mount the agent surface in an actual server
140
+
141
+ E10's [HTTP mount](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/src/http.mjs) wraps the same registry in `createHttpHandler` from `@uppercut-labs/agent-native/http`, supplying a caller and explicit `authorization` through `resolveExecutionContext`. It adapts the Web-standard handler to its pre-existing Node HTTP server and leaves unrelated `/api/health` and content routes intact.
142
+
143
+ <!-- source:examples/e10-existing-functions-retrofit/project/src/http.mjs#adoption-http -->
144
+ ~~~js
145
+ const generatedHandler = createHttpHandler(registry, {
146
+ resolveExecutionContext: () => ({
147
+ caller: { kind: 'anonymous' },
148
+ authorization: publicReadAuthorization,
149
+ }),
150
+ });
151
+ ~~~
152
+
153
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/src/http.mjs#L7)
154
+ <!-- /source -->
155
+
156
+ By default, public read capabilities may be invoked by `POST /agent-native/v1/capabilities/{namespace}/{name}/v{major}/invoke` and are described at `GET /agent-native/v1/openapi.json`; E10 demonstrates an approved GET override at the original path. The HTTP adapter in 1.0.1 exposes **only public reads**; user-specific or destructive operations are not automatically routable. See [HTTP/OpenAPI](http.md).
157
+
158
+ For agent-native MCP clients, add the isolated `/mcp` adapter and mount it at a real `/mcp` route, with the host's request-specific authorization context. Use the official MCP server SDK and a compatible MCP client. Successful structured results appear under `structuredContent.result`. MCP protected discovery, scopes, revocable grants and per-resource authorization are covered in [remote MCP](mcp.md) and [permissions](permissions-and-discovery.md). **Do not turn on protected MCP in the beginner read-only sample without an actual verified identity source.**
159
+
160
+ If your website has no server, select a separately deployed Worker or equivalent server to host those routes, wire CORS/host/origin carefully and keep the static site untouched. Local workerd evidence is not a claim that your DNS or production Worker was deployed. See [static sites](static-sites.md).
161
+
162
+ ## 5. Prove the behavior, including a refusal
163
+
164
+ For the checked E10 example, run this in the *public package source repository* to export and exercise its independent copy:
165
+
166
+ ```sh
167
+ npm ci
168
+ npm run example:e10
169
+ ```
170
+
171
+ To verify the published registry package independently, run `npm run check:adoption`.
172
+ It installs the 1.0.0 compatibility baseline in a clean consumer, preserves the E10 domain function, runs its HTTP/CLI
173
+ tests, and uses the official MCP client over an actual local socket. The client validates
174
+ `structuredContent.result`, rejects invalid input before the function, and cannot discover
175
+ or call a protected canary. This is maintainer loopback proof, not an outsider pilot.
176
+
177
+ Inside the generated E10 project, the [README](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/README.md) documents `npm ci`, `npm test`, `npm run search -- --query night --limit 1`, `npm start` and `curl 'http://127.0.0.1:8788/api/content/search?q=night&limit=1'`. In this fixture, that URL returns its first search match. Missing query, a fractional limit or an empty CLI query fails input validation before executing the existing function. POST to the preserved read GET route returns `405` with `Allow: GET`; unrelated health and catalog routes remain available.
178
+
179
+ For **your site**, perform the equivalent on its actual local server, not the package fixture. Record the operation name and version, URL/host, expected schema, valid result, malformed input failure, denied or hidden operation, and unchanged human website flow. For MCP, use a compatible client to list tools and call one (not a hand-built success assertion). If only local route tests passed, describe that honestly; deployed provider and live commercial-host claims require separate verification. See [compatibility](compatibility.md) and [support limits](support-and-limitations.md).
180
+
181
+ ## 6. Take it from local proof to a deployed project
182
+
183
+ Your application owns deployment routing, TLS, Origin/Host/CORS rules, rate limits, credential verification, resource authorization, persistence, secret management, observability and rollback. Never put server SDKs, credential stores or private schemas in browser assets. Calls that time out may still have completed writes; don't retry a mutation blindly without a real idempotency contract and state check.
184
+
185
+ Use [release/provider handoff](release-handoff.md) to plan actual host acceptance, and [doctor and troubleshooting](doctor-and-troubleshooting.md) if a binding/route is unavailable. Treat the current [stability](stability.md) and [support matrix](support-and-limitations.md) as release-specific.
186
+
187
+ ## Hand this to a coding agent
188
+
189
+ For a repo-aware AI assistant, read [the agent integration guide](for-agents.md) and give it the portable skill or copyable prompt. Neither automatically grants access to the customer's site, deployment credentials or private operations.
@@ -0,0 +1,70 @@
1
+ # Browser-local WebMCP
2
+
3
+ Import the browser-only subpath so application browser bundles do not load HTTP or MCP server
4
+ adapters:
5
+
6
+ ~~~ts
7
+ import { createBrowserCapabilityAdapter } from '@uppercut-labs/agent-native/browser';
8
+
9
+ const adapter = createBrowserCapabilityAdapter(document);
10
+ const report = await adapter.sync(registry);
11
+ ~~~
12
+
13
+ The adapter feature-detects the current draft API at document.modelContext. It registers
14
+ capabilities only when the registry has exactly one browser binding. The WebMCP tool name is a
15
+ deterministic length-coded projection of the capability identity and follows the draft's allowed
16
+ ASCII character set and 128-character limit.
17
+
18
+ By default, discovery includes only public read capabilities. An application may explicitly opt
19
+ in to a protected page-local tool with canExpose; every execution still goes through the shared
20
+ executor and the application's AuthorizationPort. resolveExecutionContext is read for each tool
21
+ call, so authorization and identity are not captured as browser secrets. Resync after route or
22
+ authentication changes to abort stale registrations and register the current projection.
23
+
24
+ The adapter owns each registration with the WebMCP registerTool(tool, { signal }) AbortSignal.
25
+ Calling dispose() or receiving pagehide on the document's owning Window aborts those
26
+ registrations. Repeated adapter creation for the same Document returns the same adapter instance.
27
+ The registration signal controls availability; WebMCP passes a separate execution signal to the
28
+ tool callback. Revoked callbacks also reject retained references after a resync or dispose.
29
+
30
+ The normal human interface should work when WebMCP is absent. E06 demonstrates this with a
31
+ page-local theme action and no server fallback. E12 uses the same converter function through local
32
+ and browser bindings. The lifecycle fixtures use a simulated API and are labeled as such. On
33
+ 2026-10-10, the built E06 and E01 pages also passed against native WebMCP in Chrome 155 with the
34
+ opt-in `WebMCP` feature. That run covered host-side tool listing and invocation, invalid input,
35
+ `pagehide` cleanup and Astro client navigation. See the
36
+ [native Chrome evidence](evidence/UAN-023-native-webmcp-chrome.md). No shipping browser agent was tested.
37
+
38
+ The API shape follows the current WebMCP draft at https://webmachinelearning.github.io/webmcp/.
39
+ That draft exposes Document.modelContext.registerTool() in secure contexts, and the registration
40
+ options accept an AbortSignal. Tool annotations follow the draft: read capabilities set
41
+ `readOnlyHint` and destructive capabilities set `consequentialHint`. The only verified browser is
42
+ Chrome 155 with WebMCP enabled through `chrome://flags/#enable-webmcp-testing` or an origin
43
+ trial. Default Chrome does not expose the API, and no agent host is certified.
44
+
45
+ ## Verify the browser-only binding
46
+
47
+ With Node.js 22 or newer, run `npm ci` and `npm run example:e06` from the package root.
48
+ The exported page binds its theme action only to `browser`:
49
+
50
+ <!-- source:examples/e06-browser-only-theme-controls/project/src/theme.mjs#browser-theme-binding -->
51
+ ~~~js
52
+ const binding = bindCapability(capability, {
53
+ id: 'page-theme',
54
+ targets: ['browser'],
55
+ execute: async ({ theme }) => {
56
+ document.documentElement.dataset.theme = theme;
57
+ return { theme };
58
+ },
59
+ });
60
+ ~~~
61
+
62
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e06-browser-only-theme-controls/project/src/theme.mjs#L23)
63
+ <!-- /source -->
64
+
65
+ The simulated lifecycle test checks registration, protected invocation, disposal, and the
66
+ ordinary button fallback. A server invocation fails with `binding-unavailable`; use a
67
+ separate server binding only if the application actually implements one. See the
68
+ [E06 standalone instructions](https://github.com/uppercut-labs/agent-native/blob/main/examples/e06-browser-only-theme-controls/project/README.md).
69
+ The simulated fixture alone implies no native result; `node test/uan023-native-webmcp.mjs`
70
+ repeats E06 and E01 in a locally installed Chrome with the feature enabled.
@@ -0,0 +1,141 @@
1
+ # Capabilities and bindings
2
+
3
+ Identity namespace and name are lower-case slug segments separated by dots or hyphens,
4
+ with at most 64 characters each; majorVersion is a positive safe integer.
5
+
6
+ A capability definition describes a versioned outcome and its runtime input/output contracts. It
7
+ does not need an implementation. A binding supplies that implementation for named runtime
8
+ targets. Definitions and bindings compose into an immutable checked registry; requests execute
9
+ through the shared gateway.
10
+
11
+ ## Frozen authoring signatures
12
+
13
+ The v0 authoring surface is exercised in the installable examples under `examples/`. Definitions
14
+ require identity, description, input and output schema ports, risk, and access. Public access is
15
+ allowed only for read capabilities. Protected access carries one or more scopes; it does not issue
16
+ or persist grants.
17
+
18
+ ```ts
19
+ defineCapability({
20
+ identity: { namespace: 'example.catalog', name: 'album.lookup', majorVersion: 1 },
21
+ description: 'Look up a public sample album by its slug.',
22
+ input: albumInput,
23
+ output: albumOutput,
24
+ risk: 'read',
25
+ access: { kind: 'public' },
26
+ });
27
+ ```
28
+
29
+ Each binding has a unique slug and one or more explicit targets: `browser`, `server`, or `local`.
30
+ No runtime is inferred. A server-only binding is unavailable to a browser request. If multiple
31
+ bindings match one capability and runtime, the caller must specify `bindingId`; otherwise execution
32
+ returns a `binding-ambiguous` failure.
33
+
34
+ Every execution request provides a runtime, caller, and `AuthorizationPort`. The binding receives that caller as `context.caller`, after authorization, so it can scope records to it. The executor validates
35
+ input first, awaits `authorize` for every invocation, invokes only the selected binding after an
36
+ allow result, then validates output. Authorization errors deny execution. Public-read examples
37
+ implement their own explicit policy port; there is no implicit allow-all default. The port is the
38
+ adopter's policy boundary, not a durable grant store or identity integration.
39
+
40
+ The result is a discriminated `success` or `failure`. Failure reasons include invalid identity,
41
+ missing capability, unavailable or ambiguous binding, invalid input, denied authorization,
42
+ authorization error, invalid output, and handler failure. Each failure includes a stable
43
+ `DiagnosticObservation` without input, output, credentials, or exception text.
44
+
45
+ ## Exact identity and explicit binding selection
46
+
47
+ Run `npm run example:e11` at the repository root for an installed, executable
48
+ [selection and diagnostics recipe](https://github.com/uppercut-labs/agent-native/blob/main/examples/e11-diagnostics-fault-lab/project/src/executor-selection.mjs).
49
+ After exporting it, `npm run selection` in `examples/e11-diagnostics-fault-lab/.exported`
50
+ repeats the sequence. Failed requests invoke no handler:
51
+
52
+ | Request | Outcome |
53
+ | --- | --- |
54
+ | `name: 'example.selection binding.read'` | `invalid-identity` |
55
+ | Valid identity with `name: 'missing'` | `capability-missing` |
56
+ | Browser runtime with local-only bindings | `binding-unavailable` |
57
+ | Local runtime with two bindings, no selection | `binding-ambiguous` |
58
+ | Exact identity, local runtime, `bindingId: 'primary'` | Success, one handler call |
59
+
60
+ <!-- source:examples/e11-diagnostics-fault-lab/project/src/executor-selection.mjs#select-binding -->
61
+ ~~~js
62
+ const selected = await executeCapability(registry, {
63
+ identity: { namespace: 'example.selection', name: 'binding.read', majorVersion: 1 },
64
+ runtime: 'local',
65
+ bindingId: 'primary',
66
+ input: {},
67
+ caller: { kind: 'anonymous' },
68
+ authorization: publicRead,
69
+ });
70
+ assert.equal(selected.kind, 'success');
71
+ assert.equal(selected.bindingId, 'primary');
72
+ assert.deepEqual(selected.value, { value: 'primary' });
73
+ ~~~
74
+
75
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e11-diagnostics-fault-lab/project/src/executor-selection.mjs#L65)
76
+ <!-- /source -->
77
+
78
+ Namespace, name and major version are separate fields. Never pass a display label or a
79
+ space-separated phrase as the name, normalize it into another identity, or silently select
80
+ the first binding. If an application exposes an executor wrapper with a fixed namespace,
81
+ state that namespace in both its schema descriptions and tool description, give the valid
82
+ name example `binding.read`, and reject combined labels. Expose a `bindingId` selection only
83
+ when the application's contract permits it. The MCP adapter exposes capabilities with one
84
+ server binding; it does not select between ambiguous bindings on behalf of a client.
85
+
86
+ The same recipe exercises the [MCP operator failure hook](mcp.md#operator-failure-hook).
87
+ Keep detailed reasons in a private operator sink; do not expose hidden capabilities or
88
+ unredacted exceptions to diagnose generic client errors.
89
+
90
+ ## Runnable examples
91
+
92
+ - [E01 domain execution slice](https://github.com/uppercut-labs/agent-native/blob/main/examples/e01-album-catalog/project/README.md) proves found and
93
+ missing album outcomes plus malformed-slug rejection through local execution.
94
+ - [E12 shared unit converter](https://github.com/uppercut-labs/agent-native/blob/main/examples/e12-shared-unit-converter/project/README.md) proves the
95
+ converter through an explicit local binding and the same executor.
96
+
97
+ Run `npm run example:e01` and `npm run example:e12` at the repository root for fresh tarball
98
+ exports, independent installs, tests, and CLI output. These examples do not implement the full E01
99
+ Astro retrofit or claim browser, MCP, HTTP/OpenAPI, or real-host support.
100
+
101
+ ## Tested source and first run
102
+
103
+ With Node.js 22 or newer, run `npm ci` in the repository root, then
104
+ `npm run example:e01` and `npm run example:e12`. E01 returns a found or missing album through
105
+ the local executor; a malformed slug fails input validation before its handler runs. E12 prints
106
+ a versioned JSON conversion result with `30.48 cm`; an unsupported unit returns
107
+ `invalid-input` before the shared converter runs. See the [E01 project](https://github.com/uppercut-labs/agent-native/blob/main/examples/e01-album-catalog/project/README.md)
108
+ and [E12 project](https://github.com/uppercut-labs/agent-native/blob/main/examples/e12-shared-unit-converter/project/README.md) for independent exports.
109
+
110
+ E01 executes a registered album lookup with an explicit public-read policy:
111
+
112
+ <!-- source:examples/e01-album-catalog/project/src/catalog.mjs#local-album-execution -->
113
+ ~~~js
114
+ /** @param {unknown} input */
115
+ export function runAlbumLookup(input) {
116
+ return executeCapability(registry, {
117
+ identity: getAlbumCapability.identity,
118
+ runtime: 'local',
119
+ input,
120
+ caller: { kind: 'anonymous' },
121
+ authorization: publicReadAuthorization,
122
+ });
123
+ }
124
+ ~~~
125
+
126
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e01-album-catalog/project/src/catalog.mjs#L36)
127
+ <!-- /source -->
128
+
129
+ E12 binds one conversion function to local and browser runtimes:
130
+
131
+ <!-- source:examples/e12-shared-unit-converter/project/src/converter.mjs#shared-converter-bindings -->
132
+ ~~~js
133
+ export const localConversionBinding = bindCapability(convertDistanceCapability, {
134
+ id: 'local-converter',
135
+ targets: ['local'],
136
+ execute: convertDistance,
137
+ });
138
+ ~~~
139
+
140
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e12-shared-unit-converter/project/src/converter.mjs#L44)
141
+ <!-- /source -->
package/docs/cli.md ADDED
@@ -0,0 +1,88 @@
1
+ # Generated CLI
2
+
3
+ This slice exports a runner from the separate `@uppercut-labs/agent-native/cli` entrypoint and includes runnable installed application CLIs in E05/E12. Applications own their capability executable. The development branch also provides a separate [site publication command](site-publication.md). Applications provide
4
+ a capability registry, caller, authorization port, and stream adapter. The library does not
5
+ discover application capabilities or credentials implicitly.
6
+
7
+ ```ts
8
+ import { runCapabilityCli } from '@uppercut-labs/agent-native/cli';
9
+
10
+ const status = await runCapabilityCli(process.argv.slice(2), {
11
+ registry,
12
+ caller: { kind: 'anonymous' },
13
+ authorization,
14
+ credentialProfiles,
15
+ }, {
16
+ writeStdout: (value) => process.stdout.write(value),
17
+ writeStderr: (value) => process.stderr.write(value),
18
+ readStdin: async () => readStdin(),
19
+ });
20
+ process.exitCode = status;
21
+ ```
22
+
23
+ The help list filters to public reads by default, and a selected hidden or unknown identity gets the same generic unavailable message. Applications may provide an async `canDiscover` callback for protected help metadata and `surfaceExposure: { cli: { destructive: ['account:delete@1'] } }` for exact destructive exposure. A destructive entry still needs the app's `canDiscover` approval; direct execution is independently checked by the shared executor.
24
+
25
+ The registry generates the application CLI help list and simple field flags from each input JSON Schema. String,
26
+ number, integer, boolean, and enum properties accept typed flags; nested objects and arrays require
27
+ `--input-json JSON`. Use `--input-json -` to read one JSON document from the provided stdin port.
28
+ Capability identity must match the complete `namespace:name@major` form. There is no fallback to a
29
+ different version or binding.
30
+
31
+ Definitions may replace that invocation name with an established command and explicit aliases:
32
+
33
+ E10 binds its existing command names in the same tested definition:
34
+
35
+ <!-- source:examples/e10-existing-functions-retrofit/project/src/capability.mjs#cli-command-override -->
36
+ ~~~js
37
+ cli: {
38
+ command: 'content-search',
39
+ aliases: ['search'],
40
+ },
41
+ ~~~
42
+
43
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/src/capability.mjs#L40)
44
+ <!-- /source -->
45
+
46
+ Commands and aliases are lowercase slugs and must be unique across the registry. They resolve to
47
+ the same canonical capability identity used for execution and result envelopes; an override does
48
+ not change the contract identity.
49
+
50
+ Every invocation names `--mode local` or `--mode remote`. Local mode uses the shared executor and
51
+ requires the configured authorization port; an optional `--binding-id` selects one exact local
52
+ binding. Remote mode requires both `--profile NAME` and a configured own-property credential entry.
53
+ The profile includes an HTTPS base URL and bearer token. HTTP is permitted only for localhost and
54
+ loopback addresses for local fixtures. The CLI rejects URL userinfo and does not print tokens.
55
+ Remote mode rejects `--binding-id`; runtime binding selection belongs to the remote host.
56
+
57
+ A remote `--timeout-ms` races the request against a deadline and also signals abort to cooperative
58
+ fetchers. A fetcher that ignores abort cannot hold the CLI past that timeout. The generated remote
59
+ HTTP surface exposes public reads only; protected writes are rejected before any network request.
60
+ Local mode does not apply `--timeout-ms` to a binding. Remote JSON is checked against the selected
61
+ capability output schema before it can be reported as success.
62
+
63
+ For invocations, stdout contains exactly one JSON envelope using `uan.cli-result/v1`; stderr carries
64
+ effective target and redacted failure diagnostics. Exit codes are 0 for success, 2 for invalid input,
65
+ 3 for denied authorization, and 1 for other failures. Help and version commands are human-readable.
66
+ The CLI runner itself never spawns a shell. Applications that launch it should pass argv as an array
67
+ and disable shell interpretation.
68
+
69
+ See the installed E05 and E12 application examples for local and loopback HTTP fixtures, positive
70
+ output, timeout, credential, malformed-input, and handler-failure paths. E05 mounts the generated
71
+ HTTP and MCP handlers into one pinned Node/Hono application and checks the MCP surface with the
72
+ official client SDK. It remains a local fixture, not a deployment or external credential provider.
73
+
74
+ ## Discovery filtering
75
+
76
+ Help lists public reads by default. Applications may provide an async `canDiscover` callback for protected metadata and `surfaceExposure: { cli: { destructive: ['account:delete@1'] } }` for exact destructive exposure. Local help requires one unique local binding; remote help lists only public reads with one server binding. Direct destructive local invocation requires the explicit CLI exposure entry before input reaches a binding, and the shared executor independently checks authorization. Close `canDiscover` over a trusted caller from the application's auth boundary; do not infer identity from argv.
77
+
78
+ ## Exact runner options
79
+
80
+ The [generated API and CLI reference](reference.md) lists every package import
81
+ and each argument recognized by the current parser. `npm run docs:check`
82
+ compares that page with the package manifest and CLI source.
83
+
84
+ With Node.js 22 or newer, run `npm ci` then `npm run example:e10` at the repository
85
+ root. In its exported project, `npm run search -- --query night --limit 1` yields the
86
+ same versioned result envelope as the existing function. An empty query exits 2 with
87
+ `invalid-input`; pass a nonempty query to repair it. See the
88
+ [E10 CLI fixture](https://github.com/uppercut-labs/agent-native/blob/main/examples/e10-existing-functions-retrofit/project/README.md).
@@ -0,0 +1,32 @@
1
+ # Compatibility and evidence
2
+
3
+ To wrap a working site’s existing operation, follow [adopt an existing site](adopt-existing-site.md).
4
+
5
+ Agent Native `1.0.1`, with the same runtime and API as `1.0.0`. The table describes the exact local fixtures and hosts
6
+ that were tested; it is not a claim for all releases of a runtime or every client.
7
+
8
+ | Surface | Runnable check | Verified boundary |
9
+ | --- | --- | --- |
10
+ | Static Astro | `npm run example:e01` | Astro 7.3.5 static build and simulated WebMCP lifecycle; native Chrome 155 WebMCP (opt-in feature) [evidence](evidence/UAN-023-native-webmcp-chrome.md) |
11
+ | Astro on-demand | `npm run example:e02` | Astro 7.3.5 with its existing Node adapter, HTTP route and official MCP SDK client |
12
+ | Next App Router | `npm run example:e03` | Next 16.3.8 on Node, production route smoke and browser artifact scan |
13
+ | Cloudflare Worker | `npm run example:e04` | Wrangler 4.147.0 local workerd, HTTP/OpenAPI and official MCP SDK client |
14
+ | Node/Hono | `npm run example:e05` | Hono 4.13.12 and Node adapter 2.1.3 on loopback |
15
+ | Browser-only WebMCP | `npm run example:e06` | Simulated `document.modelContext` and native Chrome 155 with the opt-in `WebMCP` feature; ordinary button fallback in default Chrome |
16
+ | Deployed protected MCP | [Domain Expansion evidence](evidence/gate-2-domain-expansion.md) | Cloudflare Worker with Neon in production: official MCP SDK client 2.3.0 and Claude Code 2.1.285 against `https://domains.uppercut-labs.com/mcp`, with bearer tokens, grants, typed outcomes and a rollback drill |
17
+ | Commercial agent hosts over MCP | `node test/uan023-agent-host.mjs agy` or `claude` (opt-in inference) | Antigravity CLI 1.2.14/1.3.3 and Claude Code 2.1.286 called the E04 flagship tool, with found and invalid cases ([evidence](evidence/UAN-023-commercial-agent-hosts.md)) |
18
+ | MCP Apps | `npm run example:e09` | Official ext-apps App/AppBridge fixture and local MCP client; no commercial host claim |
19
+ | Codex local harness | `npm run proof:codex -- --model gpt-5.6-luna` (explicit inference opt-in) | CLI 0.145.0, managed ChatGPT login, two file writes and same-thread resume across processes; [evidence](evidence/codex-harness.md) |
20
+
21
+ Use Node.js 22 or newer and `npm ci` at the repository root before a listed command.
22
+ Each command packs the package into an independent exported project; the root
23
+ `npm run check` runs all twelve examples on Node 22 and 24 in CI.
24
+ The [example coverage manifest](https://github.com/uppercut-labs/agent-native/blob/main/docs-site/coverage.json) maps all E01-E12 scripts, source,
25
+ guides, negative probes, requirements and the CI gate. A malformed input or denied request
26
+ is part of each example's test, not a successful operation to copy into an application.
27
+
28
+ For a missing optional peer, install the exact adapter dependency in
29
+ [installation](installation.md). For an inert route, check the application's adapter, runtime
30
+ and route mapping in [frameworks](frameworks.md). Native browser support is verified only in
31
+ Chrome 155 with WebMCP opted in; a simulated `modelContext` cannot prove other browsers. See
32
+ [support and limitations](support-and-limitations.md) for the release boundary.
@@ -0,0 +1,113 @@
1
+ # Composition and versioning
2
+
3
+ Reusable capability packs distribute contracts, not providers. A pack owns an authority and a
4
+ namespace; its definitions use the combined namespace `<authority>.<namespace>`. Definitions remain
5
+ ordinary `CapabilityDefinition` values, and a consumer binds the exact definition it implements.
6
+ Composition does not wrap handlers, grant scopes, or create a privileged execution path.
7
+
8
+ ## Exact major selection and aliases
9
+
10
+ `majorVersion` is part of the canonical identity. A pack can therefore export v1 and v2 together,
11
+ and `composeCapabilityPacks()` keeps both definitions. The E08 v2 consumer selects one exact major:
12
+
13
+ <!-- source:examples/e08-reusable-versioned-pack/project/src/demo-b.mjs#select-v2-major -->
14
+ ~~~js
15
+ const selected = composition.select({
16
+ namespace: 'example.org.catalog',
17
+ name: 'album.lookup',
18
+ majorVersion: 2,
19
+ });
20
+ if (selected === undefined) throw new Error('v2 contract is not installed');
21
+ ~~~
22
+
23
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e08-reusable-versioned-pack/project/src/demo-b.mjs#L13)
24
+ <!-- /source -->
25
+
26
+ `composition.resolve()` accepts either a canonical ID or an explicitly configured alias. Every
27
+ import must choose `{ kind: 'none' }` or `{ kind: 'explicit', aliases: [...] }`. An alias stores one
28
+ full canonical ID; it never means "latest" and never falls forward. Thus an `album-v1` alias that
29
+ targets `example.org.catalog:album.lookup@1` continues to select v1 when v2 is installed. Missing
30
+ targets, duplicate aliases, and duplicate canonical identities fail composition with source
31
+ locations.
32
+
33
+ The E08 v1 consumer pins an alias explicitly instead of inferring a latest version:
34
+
35
+ <!-- source:examples/e08-reusable-versioned-pack/project/src/demo.mjs#compose-v1-alias -->
36
+ ~~~js
37
+ const composition = composeCapabilityPacks([
38
+ {
39
+ pack: catalogPack,
40
+ source: 'src/demo.mjs:consumer-import',
41
+ aliasPolicy: {
42
+ kind: 'explicit',
43
+ aliases: [{ name: 'album-v1', capabilityId: 'example.org.catalog:album.lookup@1' }],
44
+ },
45
+ },
46
+ ]);
47
+ const selected = composition.resolve('album-v1');
48
+ if (selected === undefined) throw new Error('v1 alias is not configured');
49
+ ~~~
50
+
51
+ [View tested source](https://github.com/uppercut-labs/agent-native/blob/main/examples/e08-reusable-versioned-pack/project/src/demo.mjs#L10)
52
+ <!-- /source -->
53
+
54
+ ## Contract migration reports
55
+
56
+ `compareCapabilityDefinitions(previous, next)` returns a structural report over input and output
57
+ JSON Schema plus risk, access, and HTTP/CLI surface metadata. Reports identify paths and before/after values for
58
+ property additions/removals, likely one-to-one property renames, required-field changes, defaults,
59
+ unit annotations such as `x-unit`, and other represented schema values. Rename detection is a
60
+ structural aid, not proof of author intent.
61
+
62
+ `assertCompatibleCapabilityReplacement(previous, next)` is for an unchanged canonical identity. It
63
+ allows an implementation or description change when input/output schemas and risk/access metadata
64
+ remain compatible. It rejects a breaking definition replacement, so a v1 ID cannot silently acquire
65
+ v2 requirements or output shape.
66
+
67
+ For a new major, use `defineCapabilityMigration({ previous, next, semanticReview })`. The semantic
68
+ note and named reviewer are mandatory even when the two schemas are identical. JSON Schema can show
69
+ represented structural changes; it cannot prove behavioral equivalence, units outside the schema,
70
+ side effects, ordering, freshness, or other semantics. A pack that contains multiple majors of one
71
+ capability family must attach the reviewed adjacent-major migrations and lifecycle policy; pack
72
+ definition fails when either is missing.
73
+
74
+ ## Deprecation and removal
75
+
76
+ Lifecycle is application policy, not npm semver inference. `defineCapabilityLifecyclePolicy()`
77
+ records each canonical major as `supported`, `deprecated`, or `removed`. Deprecation needs an
78
+ operator-facing note. Removal needs a note and reviewer.
79
+
80
+ `validateCapabilityPackMigration()` compares an earlier pack to a later pack. It checks unchanged
81
+ IDs with the replacement guard, requires reviewed migration records for added majors of an existing
82
+ capability family, and reports additions/removals. A supported major cannot disappear implicitly. A
83
+ major may be removed only when the previous policy already marked it deprecated and the new policy
84
+ records an explicit reviewed removal. Package version changes do not alter these states.
85
+
86
+ ## Generated surfaces
87
+
88
+ `capabilitySurfaceNames(identity)` maps each exact major identity deterministically:
89
+
90
+ | Surface | Mapping |
91
+ | --- | --- |
92
+ | CLI | `<namespace>:<name>@<major>` |
93
+ | HTTP | `/capabilities/<namespace>/<name>/v<major>/invoke` |
94
+ | MCP | length-encoded `cap_..._v<major>` tool name |
95
+ | OpenAPI | length-encoded `invoke_..._v<major>` operation ID |
96
+
97
+ HTTP segments are encoded separately. MCP names over 128 characters receive a visible
98
+ `_fnv1a64_<digest>` qualifier rather than silent truncation. Aliases are composition lookup names;
99
+ they do not replace canonical CLI IDs, HTTP paths, MCP names, or OpenAPI operations. Adapters expose
100
+ only definitions present in the registry and keep their existing authorization/discovery policies.
101
+
102
+ Run `npm run example:e08` for the independently installed contract-only package. It exports v1 and
103
+ v2 album lookup contracts, keeps a stable v1 alias, uses two consumer bindings, checks old/new output
104
+ fixtures, and exercises migration failures. E12 retains its established canonical unit-converter ID.
105
+
106
+ ## Reproduce coexistence
107
+
108
+ With Node.js 22 or newer, run `npm ci` and `npm run example:e08` from the
109
+ repository root. The standalone package installs v1 and v2 contracts and each
110
+ consumer selects its intended major; the v1 alias remains pinned. A breaking
111
+ same-ID replacement fails composition. Give the changed contract a new major,
112
+ add a reviewed migration record, and keep the earlier major until its lifecycle
113
+ policy explicitly permits removal. See [E08 setup](https://github.com/uppercut-labs/agent-native/blob/main/examples/e08-reusable-versioned-pack/project/README.md).