Skip to content

Commit 71748a7

Browse files
committed
feat(schema): add plugin asset version requirements
Amp-Thread-ID: https://ampcode.com/threads/T-019f8a17-1cf9-744d-8aeb-c429c20f97f8
1 parent 4d7dfc5 commit 71748a7

2 files changed

Lines changed: 60 additions & 6 deletions

File tree

packages/foundation/schema/src/model/__tests__/plugin.test.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('PluginSchema', () => {
6262
availability: 'available',
6363
installations: [],
6464
components: [{ kind: 'skill', name: 'latex', enabled: true }],
65-
assets: ['tool:tectonic'],
65+
assets: [{ id: 'tool:tectonic', versionRange: '>=0.16.0 <0.17.0' }],
6666
managementCapabilities: {
6767
install: true,
6868
uninstall: true,
@@ -74,7 +74,7 @@ describe('PluginSchema', () => {
7474
).toMatchObject({
7575
provider: 'codex',
7676
installations: [],
77-
assets: ['tool:tectonic'],
77+
assets: [{ id: 'tool:tectonic', versionRange: '>=0.16.0 <0.17.0' }],
7878
});
7979
});
8080

@@ -109,7 +109,51 @@ describe('PluginSchema', () => {
109109
availability: 'available',
110110
installations: [],
111111
components: [],
112-
assets: ['agent:codex'],
112+
assets: [{ id: 'agent:codex' }],
113+
managementCapabilities: {
114+
install: true,
115+
uninstall: true,
116+
update: false,
117+
enable: false,
118+
disable: false,
119+
},
120+
}).success,
121+
).toBe(false);
122+
});
123+
124+
it('rejects the former string shorthand for tool requirements', () => {
125+
expect(
126+
PluginSchema.safeParse({
127+
provider: 'codex',
128+
id: 'legacy@openai',
129+
name: 'legacy',
130+
keywords: [],
131+
availability: 'available',
132+
installations: [],
133+
components: [],
134+
assets: ['tool:tectonic'],
135+
managementCapabilities: {
136+
install: true,
137+
uninstall: true,
138+
update: false,
139+
enable: false,
140+
disable: false,
141+
},
142+
}).success,
143+
).toBe(false);
144+
});
145+
146+
it('rejects an empty version compatibility range', () => {
147+
expect(
148+
PluginSchema.safeParse({
149+
provider: 'codex',
150+
id: 'invalid-range@openai',
151+
name: 'invalid-range',
152+
keywords: [],
153+
availability: 'available',
154+
installations: [],
155+
components: [],
156+
assets: [{ id: 'tool:tectonic', versionRange: '' }],
113157
managementCapabilities: {
114158
install: true,
115159
uninstall: true,

packages/foundation/schema/src/model/plugin.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ export const PluginLinksSchema = z.object({
111111
});
112112
export type PluginLinks = z.infer<typeof PluginLinksSchema>;
113113

114+
/**
115+
* A plugin's compatibility requirement on a trusted managed tool. The host selects the exact
116+
* version; `versionRange` only gates compatibility and never instructs the host what to download.
117+
*/
118+
export const PluginAssetRequirementSchema = z.object({
119+
id: ManagedToolAssetIdSchema,
120+
versionRange: z.string().min(1).optional(),
121+
});
122+
export type PluginAssetRequirement = z.infer<typeof PluginAssetRequirementSchema>;
123+
114124
const pluginFields = {
115125
/** Opaque provider-native id; pair with `provider` for identity. */
116126
id: z.string().min(1),
@@ -130,10 +140,10 @@ const pluginFields = {
130140
installations: z.array(PluginInstallationSchema),
131141
components: z.array(PluginComponentSchema),
132142
/**
133-
* Managed runtime dependencies required by this plugin (for example `tool:tectonic`). Plugins
134-
* declare only trusted catalog ids; download URLs, versions, and integrity stay host-owned.
143+
* Managed runtime dependencies required by this plugin. Plugins declare trusted catalog ids and
144+
* optional compatibility ranges; exact versions, download URLs, and integrity stay host-owned.
135145
*/
136-
assets: z.array(ManagedToolAssetIdSchema),
146+
assets: z.array(PluginAssetRequirementSchema),
137147
managementCapabilities: PluginManagementCapabilitiesSchema,
138148
} as const;
139149

0 commit comments

Comments
 (0)