diff --git a/.changeset/remove-unenforced-plugin-loading-config.md b/.changeset/remove-unenforced-plugin-loading-config.md new file mode 100644 index 0000000000..cf54fb95de --- /dev/null +++ b/.changeset/remove-unenforced-plugin-loading-config.md @@ -0,0 +1,55 @@ +--- +"@objectstack/spec": major +--- + +refactor(spec)!: remove the plugin sandboxing / integrity / approval config that never existed (#3896 follow-up) + +`DynamicLoadingConfigSchema`, `PluginDiscoveryConfigSchema` and +`PluginDiscoverySourceSchema` declared a plugin security control set — +`defaultSandbox`, `requireIntegrity`, `allowedSources`, and discovery's +`requireApproval` ("require admin approval before loading discovered plugins"). + +**None of it was ever wired to anything.** The three schemas were an island: not +composed into any parent schema, not read by any runtime, referenced only by +their own round-trip tests. They were nonetheless published into `json-schema/` +and the authorable key surface, where an author — very often an AI (ADR-0033) — +would read them as capabilities this platform has. A reader of the spec could +reasonably conclude ObjectStack sandboxes dynamically loaded plugins. It does +not. + +That is the ADR-0049 false-compliance shape, and the precedent for a +SAFETY-shaped instance is to remove rather than mark dead: +`tool.requiresConfirmation` was pruned in #3715 because it was "unenforced on +every path, so it was false compliance, not merely dead". This is the same case, +one layer up. + +Found while building the empty-state gate (#3945): `allowedSources` documented +`[]` as admitting every source, and checking who enforced that turned up nobody. + +**No `retiredKey()` tombstones, deliberately.** A tombstone earns its keep by +making a removal audible at a parse the author actually reaches — and nothing +parses these schemas, so the prescription could never be delivered. The +silent-strip that the key-vanish guard exists to prevent was already these keys' +permanent condition: writing one has always been a no-op, because no parent +schema ever accepted them. The guard's baseline entries in +`json-schema.manifest.json` and `authorable-surface.json` are therefore dropped +in this PR as the deliberate removal both files document as the legitimate path, +rather than tombstoning 15 keys nobody could have successfully authored. + +**Breaking, in the narrow sense.** `packages/spec/src/kernel/index.ts` +re-exports this module with `export *`, and `./kernel` is a published subpath, so +`DynamicLoadingConfig`, `PluginDiscoveryConfig`, `PluginDiscoverySource` and +their schemas were importable as types. Nothing in this repo imported them. +Marked `major` because removing a public export is breaking regardless of use; +in practice it folds into the unreleased 17.0.0. + +The rest of `plugin-runtime.zod.ts` is untouched — including +`ActivationEventSchema`, the one export in the file with real consumers. Note +that the remainder (`DynamicLoadRequest`, `DynamicUnloadRequest`, +`DynamicPluginResult`, `PluginSource`, `DynamicPluginOperation`) also has no +runtime consumer today; it is left in place because those are operation +contracts, not security promises, and the enforce-or-remove call on them is a +design decision rather than a correction. + +**Rebuilding this surface is a design job, not a schema job**: write the runtime +first, then declare only what it enforces. diff --git a/content/docs/references/kernel/plugin-runtime.mdx b/content/docs/references/kernel/plugin-runtime.mdx index 46b5370f22..ac8e87c23b 100644 --- a/content/docs/references/kernel/plugin-runtime.mdx +++ b/content/docs/references/kernel/plugin-runtime.mdx @@ -38,8 +38,8 @@ This protocol enables: ## TypeScript Usage ```typescript -import { DynamicLoadRequest, DynamicLoadingConfig, DynamicPluginOperation, DynamicPluginResult, DynamicUnloadRequest, PluginDiscoveryConfig, PluginDiscoverySource, PluginSource } from '@objectstack/spec/kernel'; -import type { DynamicLoadRequest, DynamicLoadingConfig, DynamicPluginOperation, DynamicPluginResult, DynamicUnloadRequest, PluginDiscoveryConfig, PluginDiscoverySource, PluginSource } from '@objectstack/spec/kernel'; +import { DynamicLoadRequest, DynamicPluginOperation, DynamicPluginResult, DynamicUnloadRequest, PluginSource } from '@objectstack/spec/kernel'; +import type { DynamicLoadRequest, DynamicPluginOperation, DynamicPluginResult, DynamicUnloadRequest, PluginSource } from '@objectstack/spec/kernel'; // Validate data const result = DynamicLoadRequest.parse(data); @@ -64,25 +64,6 @@ Request to dynamically load a plugin at runtime | **timeout** | `integer` | ✅ | Maximum time to complete loading in ms | ---- - -## DynamicLoadingConfig - -Dynamic plugin loading subsystem configuration - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable runtime load/unload of plugins | -| **maxDynamicPlugins** | `integer` | ✅ | Upper limit on runtime-loaded plugins | -| **discovery** | `{ enabled: boolean; sources: { type: Enum<'registry' \| 'npm' \| 'directory' \| 'url'>; endpoint: string; pollInterval: integer; filter?: object }[]; autoLoad: boolean; requireApproval: boolean }` | optional | Runtime plugin discovery configuration | -| **defaultSandbox** | `boolean` | ✅ | Sandbox dynamically loaded plugins by default | -| **allowedSources** | `Enum<'npm' \| 'local' \| 'url' \| 'registry' \| 'git'>[]` | optional | [EXPERIMENTAL — not enforced] Restrict which plugin source types are permitted: undefined = any source, [] = deny-all, a subset = exactly those types | -| **requireIntegrity** | `boolean` | ✅ | Require integrity hash verification for remote sources | -| **operationTimeout** | `integer` | ✅ | Default timeout for load/unload operations in ms | - - --- ## DynamicPluginOperation @@ -134,38 +115,6 @@ Request to dynamically unload a plugin at runtime | **dependentAction** | `Enum<'cascade' \| 'warn' \| 'block'>` | ✅ | How to handle plugins that depend on this one | ---- - -## PluginDiscoveryConfig - -Runtime plugin discovery configuration - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | | -| **sources** | `{ type: Enum<'registry' \| 'npm' \| 'directory' \| 'url'>; endpoint: string; pollInterval: integer; filter?: object }[]` | ✅ | | -| **autoLoad** | `boolean` | ✅ | Automatically load newly discovered plugins | -| **requireApproval** | `boolean` | ✅ | Require admin approval before loading discovered plugins | - - ---- - -## PluginDiscoverySource - -Source for runtime plugin discovery - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `Enum<'registry' \| 'npm' \| 'directory' \| 'url'>` | ✅ | Discovery source type | -| **endpoint** | `string` | ✅ | Registry URL, directory path, or manifest URL | -| **pollInterval** | `integer` | ✅ | How often to re-scan for new plugins (0 = manual) | -| **filter** | `{ tags?: string[]; vendors?: string[]; minTrustLevel?: Enum<'verified' \| 'trusted' \| 'community' \| 'untrusted'> }` | optional | | - - --- ## PluginSource diff --git a/packages/spec/api-surface.json b/packages/spec/api-surface.json index c8745503b0..731171e585 100644 --- a/packages/spec/api-surface.json +++ b/packages/spec/api-surface.json @@ -1402,9 +1402,6 @@ "DynamicLoadRequest (type)", "DynamicLoadRequestInput (type)", "DynamicLoadRequestSchema (const)", - "DynamicLoadingConfig (type)", - "DynamicLoadingConfigInput (type)", - "DynamicLoadingConfigSchema (const)", "DynamicPluginOperation (type)", "DynamicPluginOperationSchema (const)", "DynamicPluginResult (type)", @@ -1630,10 +1627,6 @@ "PluginDependencyResolutionResultSchema (const)", "PluginDependencyResolutionSchema (const)", "PluginDependencySchema (const)", - "PluginDiscoveryConfig (type)", - "PluginDiscoveryConfigSchema (const)", - "PluginDiscoverySource (type)", - "PluginDiscoverySourceSchema (const)", "PluginDynamicImport (type)", "PluginDynamicImportSchema (const)", "PluginEngines (type)", diff --git a/packages/spec/authorable-surface.json b/packages/spec/authorable-surface.json index 364a491377..0530f97286 100644 --- a/packages/spec/authorable-surface.json +++ b/packages/spec/authorable-surface.json @@ -1,5 +1,5 @@ { - "description": "Ratchet of every AUTHORABLE key in the spec — what a metadata author may write, which for this platform IS the third-party API. Auto-updated on additions (commit the change). A key that disappears without a tombstone fails gen:schema, because these schemas are not .strict() and Zod would silently strip it. \"[RETIRED]\" marks a tombstoned key that still rejects with an upgrade prescription. See #3855, ADR-0059 §5.", + "description": "Ratchet of every AUTHORABLE key in the spec \u2014 what a metadata author may write, which for this platform IS the third-party API. Auto-updated on additions (commit the change). A key that disappears without a tombstone fails gen:schema, because these schemas are not .strict() and Zod would silently strip it. \"[RETIRED]\" marks a tombstoned key that still rejects with an upgrade prescription. See #3855, ADR-0059 \u00a75.", "keys": [ "ai/AIKnowledge:indexes", "ai/AIKnowledge:sources", @@ -4631,13 +4631,6 @@ "kernel/DynamicLoadRequest:sandbox", "kernel/DynamicLoadRequest:source", "kernel/DynamicLoadRequest:timeout", - "kernel/DynamicLoadingConfig:allowedSources", - "kernel/DynamicLoadingConfig:defaultSandbox", - "kernel/DynamicLoadingConfig:discovery", - "kernel/DynamicLoadingConfig:enabled", - "kernel/DynamicLoadingConfig:maxDynamicPlugins", - "kernel/DynamicLoadingConfig:operationTimeout", - "kernel/DynamicLoadingConfig:requireIntegrity", "kernel/DynamicPluginResult:durationMs", "kernel/DynamicPluginResult:error", "kernel/DynamicPluginResult:operation", @@ -5174,14 +5167,6 @@ "kernel/PluginDependencyResolutionResult:resolved", "kernel/PluginDependencyResolutionResult:success", "kernel/PluginDependencyResolutionResult:warnings", - "kernel/PluginDiscoveryConfig:autoLoad", - "kernel/PluginDiscoveryConfig:enabled", - "kernel/PluginDiscoveryConfig:requireApproval", - "kernel/PluginDiscoveryConfig:sources", - "kernel/PluginDiscoverySource:endpoint", - "kernel/PluginDiscoverySource:filter", - "kernel/PluginDiscoverySource:pollInterval", - "kernel/PluginDiscoverySource:type", "kernel/PluginDynamicImport:enabled", "kernel/PluginDynamicImport:mode", "kernel/PluginDynamicImport:prefetch", diff --git a/packages/spec/json-schema.manifest.json b/packages/spec/json-schema.manifest.json index 688ab8a625..a2e630920f 100644 --- a/packages/spec/json-schema.manifest.json +++ b/packages/spec/json-schema.manifest.json @@ -1,5 +1,5 @@ { - "description": "Ratchet manifest of every JSON Schema emitted by scripts/build-schemas.ts. Auto-appended when new schemas are added (commit the change). A listed schema that a build no longer emits fails gen:schema — remove a key ONLY for a deliberate retirement. See #2978.", + "description": "Ratchet manifest of every JSON Schema emitted by scripts/build-schemas.ts. Auto-appended when new schemas are added (commit the change). A listed schema that a build no longer emits fails gen:schema \u2014 remove a key ONLY for a deliberate retirement. See #2978.", "schemas": [ "ai/AIKnowledge", "ai/AIModelConfig", @@ -970,7 +970,6 @@ "kernel/DisablePackageResponse", "kernel/DistributedStateConfig", "kernel/DynamicLoadRequest", - "kernel/DynamicLoadingConfig", "kernel/DynamicPluginOperation", "kernel/DynamicPluginResult", "kernel/DynamicUnloadRequest", @@ -1075,8 +1074,6 @@ "kernel/PluginDependency", "kernel/PluginDependencyResolution", "kernel/PluginDependencyResolutionResult", - "kernel/PluginDiscoveryConfig", - "kernel/PluginDiscoverySource", "kernel/PluginDynamicImport", "kernel/PluginEngines", "kernel/PluginErrorEvent", diff --git a/packages/spec/scripts/liveness/empty-state-registry.mts b/packages/spec/scripts/liveness/empty-state-registry.mts index fca529705a..5a25f6ca50 100644 --- a/packages/spec/scripts/liveness/empty-state-registry.mts +++ b/packages/spec/scripts/liveness/empty-state-registry.mts @@ -16,7 +16,7 @@ // "restricts" something — carries opposite meanings when it is empty: // // object.apiMethods `undefined` = unrestricted, `[]` = deny-all (closed on empty) -// allowedSources "(empty = all allowed)" (open on empty) +// allowedSources "(empty = all allowed)" — REMOVED with its schema (#3896 follow-up) // sharing criteria was match-all, now matches nothing (closed on empty) // // Nothing in the metadata distinguishes them. A maintainer knows by memory which @@ -98,14 +98,6 @@ export const EMPTY_STATE_REGISTRY: EmptyStateEntry[] = [ 'Deliberate default-open, and the reference example of stating it fully: `undefined` = unrestricted (an object is API-exposed unless it opts into a whitelist — exposure is the CRUD default), `[]` = deny-all, a subset = the derived closure (#3391/#3543). The empty ARRAY is closed; only ABSENCE is open. That two-part contract is what makes it safe to author against, and object.zod.ts additionally emits a ⚠ in its describe text when stripping leaves the whitelist empty.', evidence: 'packages/spec/src/data/api-derivation.ts', }, - { - file: 'packages/spec/src/kernel/plugin-runtime.zod.ts', - property: 'allowedSources', - semantics: 'open', - rationale: - 'A source allow-list for dynamically loaded plugins — a supply-chain gate. Corrected to the `apiMethods` three-state: absence stays open (no policy declared, matching how every other config default behaves and how the runtime behaves today), but `[]` now DENIES instead of admitting everything. The vacuous allow-list was the whole defect: emptiness was both the likeliest authoring slip and the widest grant. Registered `open` rather than `closed` because absence really is permissive here — evading the scanner with softer wording would be the same silence this gate exists to break. NOTE it has no runtime consumer at all: the whole DynamicLoadingConfig block (with `requireIntegrity`, `defaultSandbox`) is declared-but-unenforced, ADR-0049 false compliance, tracked separately — an unimplemented gate is read as the specification by whoever implements it.', - evidence: 'packages/spec/src/kernel/plugin-runtime.zod.ts', - }, { file: 'packages/spec/src/security/sharing.zod.ts', property: 'condition', diff --git a/packages/spec/src/kernel/plugin-runtime.test.ts b/packages/spec/src/kernel/plugin-runtime.test.ts index 1db765439b..aab3d714fd 100644 --- a/packages/spec/src/kernel/plugin-runtime.test.ts +++ b/packages/spec/src/kernel/plugin-runtime.test.ts @@ -6,9 +6,6 @@ import { DynamicLoadRequestSchema, DynamicUnloadRequestSchema, DynamicPluginResultSchema, - PluginDiscoverySourceSchema, - PluginDiscoveryConfigSchema, - DynamicLoadingConfigSchema, } from './plugin-runtime.zod'; describe('Plugin Runtime Management Protocol', () => { @@ -209,164 +206,4 @@ describe('Plugin Runtime Management Protocol', () => { }); }); - describe('PluginDiscoverySourceSchema', () => { - it('should accept registry discovery source', () => { - const source = { - type: 'registry', - endpoint: 'https://plugins.objectstack.dev/api/v1', - pollInterval: 300000, - filter: { - tags: ['analytics', 'reporting'], - minTrustLevel: 'trusted' as const, - }, - }; - const result = PluginDiscoverySourceSchema.parse(source); - expect(result.type).toBe('registry'); - expect(result.pollInterval).toBe(300000); - expect(result.filter?.tags).toHaveLength(2); - }); - - it('should accept directory discovery source', () => { - const source = { - type: 'directory', - endpoint: '/opt/objectstack/plugins', - }; - const result = PluginDiscoverySourceSchema.parse(source); - expect(result.type).toBe('directory'); - expect(result.pollInterval).toBe(0); // default - }); - }); - - describe('PluginDiscoveryConfigSchema', () => { - it('should apply defaults', () => { - const config = PluginDiscoveryConfigSchema.parse({}); - expect(config.enabled).toBe(false); - expect(config.sources).toEqual([]); - expect(config.autoLoad).toBe(false); - expect(config.requireApproval).toBe(true); - }); - - it('should accept full discovery config', () => { - const config = { - enabled: true, - sources: [ - { - type: 'registry' as const, - endpoint: 'https://plugins.objectstack.dev', - pollInterval: 60000, - }, - { - type: 'directory' as const, - endpoint: '/opt/plugins', - }, - ], - autoLoad: true, - requireApproval: false, - }; - const result = PluginDiscoveryConfigSchema.parse(config); - expect(result.sources).toHaveLength(2); - expect(result.autoLoad).toBe(true); - }); - }); - - describe('DynamicLoadingConfigSchema', () => { - it('should apply defaults', () => { - const config = DynamicLoadingConfigSchema.parse({}); - expect(config.enabled).toBe(false); - expect(config.maxDynamicPlugins).toBe(50); - expect(config.defaultSandbox).toBe(true); - expect(config.requireIntegrity).toBe(true); - expect(config.operationTimeout).toBe(60000); - }); - - it('should accept full dynamic loading config', () => { - const config = { - enabled: true, - maxDynamicPlugins: 100, - discovery: { - enabled: true, - sources: [ - { - type: 'registry' as const, - endpoint: 'https://plugins.objectstack.dev', - }, - ], - autoLoad: false, - requireApproval: true, - }, - defaultSandbox: true, - allowedSources: ['npm', 'registry'] as const, - requireIntegrity: true, - operationTimeout: 120000, - }; - const result = DynamicLoadingConfigSchema.parse(config); - expect(result.enabled).toBe(true); - expect(result.maxDynamicPlugins).toBe(100); - expect(result.discovery?.enabled).toBe(true); - expect(result.allowedSources).toHaveLength(2); - }); - }); - - describe('Integration scenarios', () => { - it('should support dynamic loading from npm with lazy activation', () => { - const loadRequest = DynamicLoadRequestSchema.parse({ - pluginId: 'com.acme.crm', - source: { - type: 'npm', - location: '@acme/crm-plugin', - version: '^3.0.0', - }, - activationEvents: [ - { type: 'onObject', pattern: 'account' }, - { type: 'onObject', pattern: 'contact' }, - { type: 'onRoute', pattern: '/api/v1/crm/*' }, - ], - sandbox: true, - }); - expect(loadRequest.activationEvents).toHaveLength(3); - expect(loadRequest.sandbox).toBe(true); - }); - - it('should support graceful unload with dependency awareness', () => { - const unloadRequest = DynamicUnloadRequestSchema.parse({ - pluginId: 'com.acme.analytics', - strategy: 'drain', - timeout: 60000, - dependentAction: 'cascade', - cleanupCache: true, - }); - expect(unloadRequest.strategy).toBe('drain'); - expect(unloadRequest.dependentAction).toBe('cascade'); - }); - - it('should support complete dynamic loading subsystem config', () => { - const config = DynamicLoadingConfigSchema.parse({ - enabled: true, - maxDynamicPlugins: 25, - discovery: { - enabled: true, - sources: [ - { - type: 'registry', - endpoint: 'https://plugins.objectstack.dev/api/v1', - pollInterval: 300000, - filter: { - minTrustLevel: 'trusted', - }, - }, - ], - autoLoad: false, - requireApproval: true, - }, - defaultSandbox: true, - allowedSources: ['npm', 'registry', 'local'], - requireIntegrity: true, - operationTimeout: 90000, - }); - expect(config.enabled).toBe(true); - expect(config.discovery?.sources).toHaveLength(1); - expect(config.discovery?.requireApproval).toBe(true); - expect(config.defaultSandbox).toBe(true); - }); - }); }); diff --git a/packages/spec/src/kernel/plugin-runtime.zod.ts b/packages/spec/src/kernel/plugin-runtime.zod.ts index 3b0a3a170f..73008270a1 100644 --- a/packages/spec/src/kernel/plugin-runtime.zod.ts +++ b/packages/spec/src/kernel/plugin-runtime.zod.ts @@ -224,137 +224,32 @@ export const DynamicPluginResultSchema = lazySchema(() => z.object({ }).describe('Result of a dynamic plugin operation')); /** - * Plugin Discovery Source - * Defines where to discover available plugins at runtime + * REMOVED — the plugin DISCOVERY + DYNAMIC-LOADING config surface (#3896 follow-up). + * + * `DynamicLoadingConfigSchema`, `PluginDiscoveryConfigSchema` and + * `PluginDiscoverySourceSchema` declared a plugin sandboxing / integrity / + * source-allow-list / approval-before-load control set. None of it was ever + * composed into a parent schema, exported from the package root, or read by any + * runtime: the three schemas were an ISLAND, reachable only from their own + * round-trip tests — yet still published into `json-schema/` and the authorable + * key surface, where an author (very often an AI, ADR-0033) would read them as a + * capability this platform has. + * + * That is the ADR-0049 false-compliance shape, and the precedent for a + * SAFETY-shaped one is to REMOVE rather than mark dead: `tool.requiresConfirmation` + * was pruned in #3715 for exactly this reason — "unenforced on every path, so it + * was false compliance, not merely dead". + * + * No `retiredKey()` tombstones. A tombstone earns its keep by making the removal + * audible at a parse the author actually reaches, and NOTHING parses these + * schemas — a prescription nobody can receive is noise, and the silent-strip the + * key-vanish guard protects against was already these keys' permanent condition, + * not something this change introduces. The guard's baseline entries are dropped + * deliberately in this PR instead; see the changeset. + * + * Rebuilding this surface is a design job, not a schema job: write the runtime + * first, then declare only what it enforces. */ -export const PluginDiscoverySourceSchema = lazySchema(() => z.object({ - /** - * Discovery source type - */ - type: z.enum([ - 'registry', // ObjectStack plugin registry API - 'npm', // npm registry search - 'directory', // Local filesystem directory scan - 'url', // Remote manifest URL - ]).describe('Discovery source type'), - - /** - * Source endpoint or path - */ - endpoint: z.string().describe('Registry URL, directory path, or manifest URL'), - - /** - * Polling interval in milliseconds (0 = manual only) - */ - pollInterval: z.number().int().min(0).default(0) - .describe('How often to re-scan for new plugins (0 = manual)'), - - /** - * Filter criteria for discovered plugins - */ - filter: z.object({ - /** - * Only discover plugins matching these tags - */ - tags: z.array(z.string()).optional(), - - /** - * Only discover plugins from these vendors - */ - vendors: z.array(z.string()).optional(), - - /** - * Minimum trust level - */ - minTrustLevel: z.enum(['verified', 'trusted', 'community', 'untrusted']).optional(), - }).optional(), -}).describe('Source for runtime plugin discovery')); - -/** - * Plugin Discovery Configuration - * Controls how the kernel discovers available plugins at runtime - */ -export const PluginDiscoveryConfigSchema = lazySchema(() => z.object({ - /** - * Enable runtime plugin discovery - */ - enabled: z.boolean().default(false), - - /** - * Discovery sources - */ - sources: z.array(PluginDiscoverySourceSchema).default([]), - - /** - * Auto-load discovered plugins matching criteria - */ - autoLoad: z.boolean().default(false) - .describe('Automatically load newly discovered plugins'), - - /** - * Require approval before loading discovered plugins - */ - requireApproval: z.boolean().default(true) - .describe('Require admin approval before loading discovered plugins'), -}).describe('Runtime plugin discovery configuration')); - -/** - * Dynamic Loading Configuration - * Top-level configuration for the dynamic plugin loading subsystem - */ -export const DynamicLoadingConfigSchema = lazySchema(() => z.object({ - /** - * Enable dynamic loading/unloading at runtime - */ - enabled: z.boolean().default(false) - .describe('Enable runtime load/unload of plugins'), - - /** - * Maximum number of dynamically loaded plugins - */ - maxDynamicPlugins: z.number().int().min(1).default(50) - .describe('Upper limit on runtime-loaded plugins'), - - /** - * Plugin discovery configuration - */ - discovery: PluginDiscoveryConfigSchema.optional(), - - /** - * Default sandbox policy for dynamically loaded plugins - */ - defaultSandbox: z.boolean().default(true) - .describe('Sandbox dynamically loaded plugins by default'), - - /** - * Allowed plugin sources — three-state, the same contract as - * `object.apiMethods` (#3391/#3543): `undefined` = no source policy declared, - * `[]` = deny-all, a subset = exactly those source types. - * - * The empty ARRAY is closed; only ABSENCE is open. The previous wording - * collapsed the two, making this a vacuous allow-list — one where the value an - * author reaches by mistake is also the widest grant. That is the shape #3896 - * turned into an over-share, and this is a supply-chain gate. - * - * Nothing enforces this field yet (hence the marker), which is precisely why - * the wording mattered: an unimplemented property's description is the - * specification whoever implements it builds to. - */ - allowedSources: z.array(z.enum(['npm', 'local', 'url', 'registry', 'git'])).optional() - .describe('[EXPERIMENTAL — not enforced] Restrict which plugin source types are permitted: undefined = any source, [] = deny-all, a subset = exactly those types'), - - /** - * Require integrity verification for remote plugins - */ - requireIntegrity: z.boolean().default(true) - .describe('Require integrity hash verification for remote sources'), - - /** - * Global timeout for dynamic operations in milliseconds - */ - operationTimeout: z.number().int().min(1000).default(60000) - .describe('Default timeout for load/unload operations in ms'), -}).describe('Dynamic plugin loading subsystem configuration')); // Export types export type DynamicPluginOperation = z.infer; @@ -363,11 +258,7 @@ export type ActivationEvent = z.infer; export type DynamicLoadRequest = z.infer; export type DynamicUnloadRequest = z.infer; export type DynamicPluginResult = z.infer; -export type PluginDiscoverySource = z.infer; -export type PluginDiscoveryConfig = z.infer; -export type DynamicLoadingConfig = z.infer; // Export input types for schemas with defaults export type DynamicLoadRequestInput = z.input; export type DynamicUnloadRequestInput = z.input; -export type DynamicLoadingConfigInput = z.input;