@unbrained/pm-cli 2026.5.10 → 2026.5.11

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 (124) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.pi/README.md +10 -1
  3. package/.pi/agents/pm-triage-agent.md +19 -0
  4. package/.pi/agents/pm-verification-agent.md +21 -0
  5. package/.pi/chains/pm-native-delivery.chain.md +11 -0
  6. package/.pi/extensions/pm-cli/index.js +276 -36
  7. package/.pi/skills/pm-native/SKILL.md +6 -2
  8. package/CHANGELOG.md +7 -0
  9. package/README.md +9 -1
  10. package/dist/cli/argv-utils.d.ts +5 -0
  11. package/dist/cli/argv-utils.js +34 -0
  12. package/dist/cli/argv-utils.js.map +1 -0
  13. package/dist/cli/bootstrap-args.d.ts +15 -0
  14. package/dist/cli/bootstrap-args.js +211 -0
  15. package/dist/cli/bootstrap-args.js.map +1 -1
  16. package/dist/cli/commander-usage.js +109 -3
  17. package/dist/cli/commander-usage.js.map +1 -1
  18. package/dist/cli/commands/completion.js +7 -3
  19. package/dist/cli/commands/completion.js.map +1 -1
  20. package/dist/cli/commands/contracts.d.ts +19 -0
  21. package/dist/cli/commands/contracts.js +33 -1
  22. package/dist/cli/commands/contracts.js.map +1 -1
  23. package/dist/cli/commands/create.js +112 -51
  24. package/dist/cli/commands/create.js.map +1 -1
  25. package/dist/cli/commands/docs.js +9 -2
  26. package/dist/cli/commands/docs.js.map +1 -1
  27. package/dist/cli/commands/extension.d.ts +3 -1
  28. package/dist/cli/commands/extension.js +174 -2
  29. package/dist/cli/commands/extension.js.map +1 -1
  30. package/dist/cli/commands/files.js +9 -2
  31. package/dist/cli/commands/files.js.map +1 -1
  32. package/dist/cli/commands/init.d.ts +2 -0
  33. package/dist/cli/commands/init.js +21 -1
  34. package/dist/cli/commands/init.js.map +1 -1
  35. package/dist/cli/commands/metadata-normalizers.d.ts +4 -0
  36. package/dist/cli/commands/metadata-normalizers.js +37 -0
  37. package/dist/cli/commands/metadata-normalizers.js.map +1 -0
  38. package/dist/cli/commands/reindex.js +173 -135
  39. package/dist/cli/commands/reindex.js.map +1 -1
  40. package/dist/cli/commands/search.js +16 -6
  41. package/dist/cli/commands/search.js.map +1 -1
  42. package/dist/cli/commands/test.js +9 -2
  43. package/dist/cli/commands/test.js.map +1 -1
  44. package/dist/cli/commands/update.js +70 -39
  45. package/dist/cli/commands/update.js.map +1 -1
  46. package/dist/cli/error-guidance.d.ts +9 -1
  47. package/dist/cli/error-guidance.js +147 -6
  48. package/dist/cli/error-guidance.js.map +1 -1
  49. package/dist/cli/help-json-payload.js +11 -1
  50. package/dist/cli/help-json-payload.js.map +1 -1
  51. package/dist/cli/main.js +69 -6
  52. package/dist/cli/main.js.map +1 -1
  53. package/dist/cli/register-setup.js +14 -0
  54. package/dist/cli/register-setup.js.map +1 -1
  55. package/dist/cli/telemetry-flush.d.ts +2 -0
  56. package/dist/cli/telemetry-flush.js +4 -0
  57. package/dist/cli/telemetry-flush.js.map +1 -0
  58. package/dist/cli.js +1 -2
  59. package/dist/cli.js.map +1 -1
  60. package/dist/core/extensions/extension-types.d.ts +72 -0
  61. package/dist/core/extensions/extension-types.js +24 -0
  62. package/dist/core/extensions/extension-types.js.map +1 -1
  63. package/dist/core/extensions/loader.d.ts +1 -0
  64. package/dist/core/extensions/loader.js +766 -7
  65. package/dist/core/extensions/loader.js.map +1 -1
  66. package/dist/core/lock/lock.js +2 -0
  67. package/dist/core/lock/lock.js.map +1 -1
  68. package/dist/core/sentry/instrument.d.ts +15 -0
  69. package/dist/core/sentry/instrument.js +35 -3
  70. package/dist/core/sentry/instrument.js.map +1 -1
  71. package/dist/core/shared/constants.js +20 -0
  72. package/dist/core/shared/constants.js.map +1 -1
  73. package/dist/core/shared/errors.d.ts +8 -0
  74. package/dist/core/shared/errors.js.map +1 -1
  75. package/dist/core/shared/levenshtein.d.ts +1 -0
  76. package/dist/core/shared/levenshtein.js +37 -0
  77. package/dist/core/shared/levenshtein.js.map +1 -0
  78. package/dist/core/store/paths.js +34 -1
  79. package/dist/core/store/paths.js.map +1 -1
  80. package/dist/core/store/settings.js +210 -1
  81. package/dist/core/store/settings.js.map +1 -1
  82. package/dist/core/telemetry/runtime.d.ts +1 -0
  83. package/dist/core/telemetry/runtime.js +102 -3
  84. package/dist/core/telemetry/runtime.js.map +1 -1
  85. package/dist/mcp/server.js +3 -1
  86. package/dist/mcp/server.js.map +1 -1
  87. package/dist/pi/native.js +57 -4
  88. package/dist/pi/native.js.map +1 -1
  89. package/dist/sdk/cli-contracts.d.ts +21 -1
  90. package/dist/sdk/cli-contracts.js +250 -0
  91. package/dist/sdk/cli-contracts.js.map +1 -1
  92. package/dist/sdk/index.d.ts +12 -1
  93. package/dist/sdk/index.js +8 -1
  94. package/dist/sdk/index.js.map +1 -1
  95. package/dist/types.d.ts +41 -0
  96. package/dist/types.js.map +1 -1
  97. package/docs/CLAUDE_CODE_PLUGIN.md +39 -0
  98. package/docs/EXTENSIONS.md +687 -0
  99. package/docs/MIGRATION_CLI_SIMPLIFICATION.md +64 -0
  100. package/docs/PI_PACKAGE.md +95 -10
  101. package/docs/SDK.md +441 -0
  102. package/docs/examples/ci/github-actions-pm-extension-gate.yml +53 -0
  103. package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +41 -0
  104. package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +45 -0
  105. package/docs/examples/policy-restricted-extension/README.md +74 -0
  106. package/docs/examples/policy-restricted-extension/index.js +21 -0
  107. package/docs/examples/policy-restricted-extension/manifest.json +21 -0
  108. package/docs/examples/policy-restricted-extension/package.json +8 -0
  109. package/docs/examples/sdk-app-embedding/README.md +39 -0
  110. package/docs/examples/sdk-app-embedding/package.json +9 -0
  111. package/docs/examples/sdk-app-embedding/run-embedded-pm.mjs +61 -0
  112. package/docs/examples/sdk-contract-consumer/README.md +57 -0
  113. package/docs/examples/sdk-contract-consumer/inspect-contracts.mjs +47 -0
  114. package/docs/examples/sdk-contract-consumer/package.json +10 -0
  115. package/docs/examples/starter-extension/README.md +57 -42
  116. package/docs/examples/starter-extension/manifest.json +15 -0
  117. package/marketplace.json +3 -3
  118. package/package.json +1 -1
  119. package/plugins/pm-cli-claude/.claude-plugin/plugin.json +2 -2
  120. package/plugins/pm-cli-claude/README.md +55 -14
  121. package/plugins/pm-cli-claude/agents/pm-delivery-chain.md +88 -0
  122. package/plugins/pm-cli-claude/agents/pm-triage-agent.md +83 -0
  123. package/plugins/pm-cli-claude/agents/pm-verification-agent.md +88 -0
  124. package/plugins/pm-cli-claude/hooks/session-start.mjs +87 -22
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { pathExists } from "../fs/fs-utils.js";
5
5
  import { resolveGlobalPmRoot } from "../store/paths.js";
6
- import { KNOWN_EXTENSION_CAPABILITIES, EXTENSION_CAPABILITY_CONTRACT_VERSION, EXTENSION_CAPABILITY_LEGACY_ALIASES, } from "./extension-types.js";
6
+ import { KNOWN_EXTENSION_CAPABILITIES, KNOWN_EXTENSION_POLICY_MODES, KNOWN_EXTENSION_POLICY_SURFACES, KNOWN_EXTENSION_SANDBOX_PROFILES, KNOWN_EXTENSION_TRUST_MODES, EXTENSION_CAPABILITY_CONTRACT_VERSION, EXTENSION_CAPABILITY_LEGACY_ALIASES, } from "./extension-types.js";
7
7
  export * from "./extension-types.js";
8
8
  const DEFAULT_EXTENSION_PRIORITY = 100;
9
9
  /* Types now in extension-types.ts - re-exported via `export * from "./extension-types.js"` above */
@@ -16,6 +16,517 @@ function isKnownExtensionCapability(value) {
16
16
  function collectUnknownExtensionCapabilities(capabilities) {
17
17
  return capabilities.filter((capability) => !isKnownExtensionCapability(capability));
18
18
  }
19
+ const DEFAULT_EXTENSION_POLICY = Object.freeze({
20
+ mode: "off",
21
+ trust_mode: "off",
22
+ require_provenance: false,
23
+ trusted_extensions: [],
24
+ default_sandbox_profile: "none",
25
+ allowed_extensions: [],
26
+ blocked_extensions: [],
27
+ allowed_capabilities: [],
28
+ blocked_capabilities: [],
29
+ allowed_surfaces: [],
30
+ blocked_surfaces: [],
31
+ allowed_commands: [],
32
+ blocked_commands: [],
33
+ allowed_actions: [],
34
+ blocked_actions: [],
35
+ allowed_services: [],
36
+ blocked_services: [],
37
+ extension_overrides: [],
38
+ });
39
+ let extensionReloadEpoch = 0;
40
+ export function nextExtensionReloadToken(seed = Date.now()) {
41
+ extensionReloadEpoch += 1;
42
+ return `${extensionReloadEpoch}-${seed}`;
43
+ }
44
+ function normalizePolicyName(value) {
45
+ if (typeof value !== "string") {
46
+ return "";
47
+ }
48
+ return value.trim().toLowerCase();
49
+ }
50
+ function normalizePolicyStringSet(values) {
51
+ return new Set((values ?? [])
52
+ .map((value) => value.trim().toLowerCase())
53
+ .filter((value) => value.length > 0));
54
+ }
55
+ function normalizePolicySurfaceToken(value) {
56
+ const normalized = value.trim().toLowerCase();
57
+ if (normalized.length === 0) {
58
+ return "";
59
+ }
60
+ const segments = normalized
61
+ .split(/[.:/]/)
62
+ .map((segment) => segment.replace(/[\s_-]+/g, ""))
63
+ .filter((segment) => segment.length > 0);
64
+ if (segments.length === 0) {
65
+ return "";
66
+ }
67
+ if (segments.length === 1) {
68
+ return segments[0];
69
+ }
70
+ return `${segments[0]}.${segments.slice(1).join("")}`;
71
+ }
72
+ function normalizePolicySurfaceSet(values) {
73
+ return new Set((values ?? [])
74
+ .map((value) => normalizePolicySurfaceToken(value))
75
+ .filter((value) => value.length > 0));
76
+ }
77
+ function normalizePolicyMode(value) {
78
+ const normalized = normalizePolicyName(value);
79
+ if (KNOWN_EXTENSION_POLICY_MODES.includes(normalized)) {
80
+ return normalized;
81
+ }
82
+ return "off";
83
+ }
84
+ function normalizePolicyTrustMode(value) {
85
+ const normalized = normalizePolicyName(value);
86
+ if (KNOWN_EXTENSION_TRUST_MODES.includes(normalized)) {
87
+ return normalized;
88
+ }
89
+ return "off";
90
+ }
91
+ function normalizePolicySandboxProfile(value) {
92
+ const normalized = normalizePolicyName(value);
93
+ if (KNOWN_EXTENSION_SANDBOX_PROFILES.includes(normalized)) {
94
+ return normalized;
95
+ }
96
+ return "none";
97
+ }
98
+ function toSortedList(values) {
99
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
100
+ }
101
+ function normalizeExtensionPolicy(settings) {
102
+ const policy = settings.extensions.policy;
103
+ const mode = normalizePolicyMode(policy?.mode);
104
+ const trustMode = normalizePolicyTrustMode(policy?.trust_mode);
105
+ const requireProvenance = policy?.require_provenance === true;
106
+ const trustedExtensions = normalizePolicyStringSet(policy?.trusted_extensions);
107
+ const defaultSandboxProfile = normalizePolicySandboxProfile(policy?.default_sandbox_profile);
108
+ const allowedExtensions = normalizePolicyStringSet(policy?.allowed_extensions);
109
+ const blockedExtensions = normalizePolicyStringSet(policy?.blocked_extensions);
110
+ const allowedCapabilities = normalizePolicyStringSet(policy?.allowed_capabilities);
111
+ const blockedCapabilities = normalizePolicyStringSet(policy?.blocked_capabilities);
112
+ const allowedSurfaces = normalizePolicySurfaceSet(policy?.allowed_surfaces);
113
+ const blockedSurfaces = normalizePolicySurfaceSet(policy?.blocked_surfaces);
114
+ const allowedCommands = normalizePolicyStringSet(policy?.allowed_commands);
115
+ const blockedCommands = normalizePolicyStringSet(policy?.blocked_commands);
116
+ const allowedActions = normalizePolicyStringSet(policy?.allowed_actions);
117
+ const blockedActions = normalizePolicyStringSet(policy?.blocked_actions);
118
+ const allowedServices = normalizePolicyStringSet(policy?.allowed_services);
119
+ const blockedServices = normalizePolicyStringSet(policy?.blocked_services);
120
+ const overridesByName = new Map();
121
+ for (const rawOverride of policy?.extension_overrides ?? []) {
122
+ const name = normalizePolicyName(rawOverride.name);
123
+ if (name.length === 0) {
124
+ continue;
125
+ }
126
+ overridesByName.set(name, {
127
+ name,
128
+ disabled: rawOverride.disabled === true,
129
+ requireTrusted: rawOverride.require_trusted === true,
130
+ requireProvenance: rawOverride.require_provenance === true,
131
+ sandboxProfile: rawOverride.sandbox_profile !== undefined
132
+ ? normalizePolicySandboxProfile(rawOverride.sandbox_profile)
133
+ : undefined,
134
+ allowedCapabilities: normalizePolicyStringSet(rawOverride.allowed_capabilities),
135
+ blockedCapabilities: normalizePolicyStringSet(rawOverride.blocked_capabilities),
136
+ allowedSurfaces: normalizePolicySurfaceSet(rawOverride.allowed_surfaces),
137
+ blockedSurfaces: normalizePolicySurfaceSet(rawOverride.blocked_surfaces),
138
+ allowedCommands: normalizePolicyStringSet(rawOverride.allowed_commands),
139
+ blockedCommands: normalizePolicyStringSet(rawOverride.blocked_commands),
140
+ allowedActions: normalizePolicyStringSet(rawOverride.allowed_actions),
141
+ blockedActions: normalizePolicyStringSet(rawOverride.blocked_actions),
142
+ allowedServices: normalizePolicyStringSet(rawOverride.allowed_services),
143
+ blockedServices: normalizePolicyStringSet(rawOverride.blocked_services),
144
+ });
145
+ }
146
+ const warnings = [];
147
+ for (const capability of toSortedList([...allowedCapabilities, ...blockedCapabilities])) {
148
+ if (!isKnownExtensionCapability(capability)) {
149
+ warnings.push(`extension_policy_unknown_capability:${capability}`);
150
+ }
151
+ }
152
+ for (const override of [...overridesByName.values()].sort((left, right) => left.name.localeCompare(right.name))) {
153
+ for (const capability of toSortedList([...override.allowedCapabilities, ...override.blockedCapabilities])) {
154
+ if (!isKnownExtensionCapability(capability)) {
155
+ warnings.push(`extension_policy_unknown_capability:${override.name}:${capability}`);
156
+ }
157
+ }
158
+ }
159
+ const knownSurfaces = new Set(KNOWN_EXTENSION_POLICY_SURFACES);
160
+ for (const surface of toSortedList([...allowedSurfaces, ...blockedSurfaces])) {
161
+ if (!knownSurfaces.has(surface)) {
162
+ warnings.push(`extension_policy_unknown_surface:${surface}`);
163
+ }
164
+ }
165
+ for (const override of [...overridesByName.values()].sort((left, right) => left.name.localeCompare(right.name))) {
166
+ for (const surface of toSortedList([...override.allowedSurfaces, ...override.blockedSurfaces])) {
167
+ if (!knownSurfaces.has(surface)) {
168
+ warnings.push(`extension_policy_unknown_surface:${override.name}:${surface}`);
169
+ }
170
+ }
171
+ }
172
+ return {
173
+ mode,
174
+ trustMode,
175
+ requireProvenance,
176
+ trustedExtensions,
177
+ defaultSandboxProfile,
178
+ allowedExtensions,
179
+ blockedExtensions,
180
+ allowedCapabilities,
181
+ blockedCapabilities,
182
+ allowedSurfaces,
183
+ blockedSurfaces,
184
+ allowedCommands,
185
+ blockedCommands,
186
+ allowedActions,
187
+ blockedActions,
188
+ allowedServices,
189
+ blockedServices,
190
+ overridesByName,
191
+ warnings: [...new Set(warnings)].sort((left, right) => left.localeCompare(right)),
192
+ };
193
+ }
194
+ function serializeExtensionPolicy(policy) {
195
+ const overrides = [...policy.overridesByName.values()]
196
+ .sort((left, right) => left.name.localeCompare(right.name))
197
+ .map((override) => ({
198
+ name: override.name,
199
+ ...(override.disabled ? { disabled: true } : {}),
200
+ ...(override.requireTrusted ? { require_trusted: true } : {}),
201
+ ...(override.requireProvenance ? { require_provenance: true } : {}),
202
+ ...(override.sandboxProfile ? { sandbox_profile: override.sandboxProfile } : {}),
203
+ ...(override.allowedCapabilities.size > 0 ? { allowed_capabilities: toSortedList(override.allowedCapabilities) } : {}),
204
+ ...(override.blockedCapabilities.size > 0 ? { blocked_capabilities: toSortedList(override.blockedCapabilities) } : {}),
205
+ ...(override.allowedSurfaces.size > 0 ? { allowed_surfaces: toSortedList(override.allowedSurfaces) } : {}),
206
+ ...(override.blockedSurfaces.size > 0 ? { blocked_surfaces: toSortedList(override.blockedSurfaces) } : {}),
207
+ ...(override.allowedCommands.size > 0 ? { allowed_commands: toSortedList(override.allowedCommands) } : {}),
208
+ ...(override.blockedCommands.size > 0 ? { blocked_commands: toSortedList(override.blockedCommands) } : {}),
209
+ ...(override.allowedActions.size > 0 ? { allowed_actions: toSortedList(override.allowedActions) } : {}),
210
+ ...(override.blockedActions.size > 0 ? { blocked_actions: toSortedList(override.blockedActions) } : {}),
211
+ ...(override.allowedServices.size > 0 ? { allowed_services: toSortedList(override.allowedServices) } : {}),
212
+ ...(override.blockedServices.size > 0 ? { blocked_services: toSortedList(override.blockedServices) } : {}),
213
+ }));
214
+ return {
215
+ mode: policy.mode,
216
+ trust_mode: policy.trustMode,
217
+ require_provenance: policy.requireProvenance,
218
+ trusted_extensions: toSortedList(policy.trustedExtensions),
219
+ default_sandbox_profile: policy.defaultSandboxProfile,
220
+ allowed_extensions: toSortedList(policy.allowedExtensions),
221
+ blocked_extensions: toSortedList(policy.blockedExtensions),
222
+ allowed_capabilities: toSortedList(policy.allowedCapabilities),
223
+ blocked_capabilities: toSortedList(policy.blockedCapabilities),
224
+ allowed_surfaces: toSortedList(policy.allowedSurfaces),
225
+ blocked_surfaces: toSortedList(policy.blockedSurfaces),
226
+ allowed_commands: toSortedList(policy.allowedCommands),
227
+ blocked_commands: toSortedList(policy.blockedCommands),
228
+ allowed_actions: toSortedList(policy.allowedActions),
229
+ blocked_actions: toSortedList(policy.blockedActions),
230
+ allowed_services: toSortedList(policy.allowedServices),
231
+ blocked_services: toSortedList(policy.blockedServices),
232
+ extension_overrides: overrides,
233
+ };
234
+ }
235
+ function hydrateExtensionPolicy(policy) {
236
+ const overridesByName = new Map();
237
+ for (const rawOverride of policy.extension_overrides ?? []) {
238
+ const name = normalizePolicyName(rawOverride.name);
239
+ if (name.length === 0) {
240
+ continue;
241
+ }
242
+ overridesByName.set(name, {
243
+ name,
244
+ disabled: rawOverride.disabled === true,
245
+ requireTrusted: rawOverride.require_trusted === true,
246
+ requireProvenance: rawOverride.require_provenance === true,
247
+ sandboxProfile: rawOverride.sandbox_profile !== undefined
248
+ ? normalizePolicySandboxProfile(rawOverride.sandbox_profile)
249
+ : undefined,
250
+ allowedCapabilities: normalizePolicyStringSet(rawOverride.allowed_capabilities),
251
+ blockedCapabilities: normalizePolicyStringSet(rawOverride.blocked_capabilities),
252
+ allowedSurfaces: normalizePolicySurfaceSet(rawOverride.allowed_surfaces),
253
+ blockedSurfaces: normalizePolicySurfaceSet(rawOverride.blocked_surfaces),
254
+ allowedCommands: normalizePolicyStringSet(rawOverride.allowed_commands),
255
+ blockedCommands: normalizePolicyStringSet(rawOverride.blocked_commands),
256
+ allowedActions: normalizePolicyStringSet(rawOverride.allowed_actions),
257
+ blockedActions: normalizePolicyStringSet(rawOverride.blocked_actions),
258
+ allowedServices: normalizePolicyStringSet(rawOverride.allowed_services),
259
+ blockedServices: normalizePolicyStringSet(rawOverride.blocked_services),
260
+ });
261
+ }
262
+ return {
263
+ mode: normalizePolicyMode(policy.mode),
264
+ trustMode: normalizePolicyTrustMode(policy.trust_mode),
265
+ requireProvenance: policy.require_provenance === true,
266
+ trustedExtensions: normalizePolicyStringSet(policy.trusted_extensions),
267
+ defaultSandboxProfile: normalizePolicySandboxProfile(policy.default_sandbox_profile),
268
+ allowedExtensions: normalizePolicyStringSet(policy.allowed_extensions),
269
+ blockedExtensions: normalizePolicyStringSet(policy.blocked_extensions),
270
+ allowedCapabilities: normalizePolicyStringSet(policy.allowed_capabilities),
271
+ blockedCapabilities: normalizePolicyStringSet(policy.blocked_capabilities),
272
+ allowedSurfaces: normalizePolicySurfaceSet(policy.allowed_surfaces),
273
+ blockedSurfaces: normalizePolicySurfaceSet(policy.blocked_surfaces),
274
+ allowedCommands: normalizePolicyStringSet(policy.allowed_commands),
275
+ blockedCommands: normalizePolicyStringSet(policy.blocked_commands),
276
+ allowedActions: normalizePolicyStringSet(policy.allowed_actions),
277
+ blockedActions: normalizePolicyStringSet(policy.blocked_actions),
278
+ allowedServices: normalizePolicyStringSet(policy.allowed_services),
279
+ blockedServices: normalizePolicyStringSet(policy.blocked_services),
280
+ overridesByName,
281
+ warnings: [],
282
+ };
283
+ }
284
+ function resolvePolicyOverride(policy, extensionName) {
285
+ return policy.overridesByName.get(normalizePolicyName(extensionName)) ?? null;
286
+ }
287
+ function evaluatePolicySet(allowed, blocked, value, notAllowlistedReason, blockedReason) {
288
+ if (blocked.has(value)) {
289
+ return blockedReason;
290
+ }
291
+ if (allowed.size > 0 && !allowed.has(value)) {
292
+ return notAllowlistedReason;
293
+ }
294
+ return null;
295
+ }
296
+ function resolvePolicyCapabilityReason(policy, extension, capability) {
297
+ const normalizedCapability = capability.trim().toLowerCase();
298
+ const override = resolvePolicyOverride(policy, extension.name);
299
+ const allowed = override && override.allowedCapabilities.size > 0 ? override.allowedCapabilities : policy.allowedCapabilities;
300
+ const blocked = new Set([
301
+ ...policy.blockedCapabilities,
302
+ ...(override ? override.blockedCapabilities : []),
303
+ ]);
304
+ return evaluatePolicySet(allowed, blocked, normalizedCapability, "capability_not_allowlisted", "capability_blocked");
305
+ }
306
+ function resolvePolicySurfaceReason(policy, extension, surface) {
307
+ const override = resolvePolicyOverride(policy, extension.name);
308
+ const allowed = override && override.allowedSurfaces.size > 0 ? override.allowedSurfaces : policy.allowedSurfaces;
309
+ const blocked = new Set([
310
+ ...policy.blockedSurfaces,
311
+ ...(override ? override.blockedSurfaces : []),
312
+ ]);
313
+ return evaluatePolicySet(allowed, blocked, surface, "surface_not_allowlisted", "surface_blocked");
314
+ }
315
+ function resolvePolicyCommandReason(policy, extension, command) {
316
+ const normalizedCommand = normalizeCommandName(command);
317
+ if (normalizedCommand.length === 0) {
318
+ return null;
319
+ }
320
+ const override = resolvePolicyOverride(policy, extension.name);
321
+ const allowed = override && override.allowedCommands.size > 0 ? override.allowedCommands : policy.allowedCommands;
322
+ const blocked = new Set([
323
+ ...policy.blockedCommands,
324
+ ...(override ? override.blockedCommands : []),
325
+ ]);
326
+ return evaluatePolicySet(allowed, blocked, normalizedCommand, "command_not_allowlisted", "command_blocked");
327
+ }
328
+ function resolvePolicyActionReason(policy, extension, action) {
329
+ const normalizedAction = normalizePolicyName(action).replace(/\s+/g, "-");
330
+ if (normalizedAction.length === 0) {
331
+ return null;
332
+ }
333
+ const override = resolvePolicyOverride(policy, extension.name);
334
+ const allowed = override && override.allowedActions.size > 0 ? override.allowedActions : policy.allowedActions;
335
+ const blocked = new Set([
336
+ ...policy.blockedActions,
337
+ ...(override ? override.blockedActions : []),
338
+ ]);
339
+ return evaluatePolicySet(allowed, blocked, normalizedAction, "action_not_allowlisted", "action_blocked");
340
+ }
341
+ function resolvePolicyServiceReason(policy, extension, service) {
342
+ const normalizedService = normalizePolicyName(service);
343
+ if (normalizedService.length === 0) {
344
+ return null;
345
+ }
346
+ const override = resolvePolicyOverride(policy, extension.name);
347
+ const allowed = override && override.allowedServices.size > 0 ? override.allowedServices : policy.allowedServices;
348
+ const blocked = new Set([
349
+ ...policy.blockedServices,
350
+ ...(override ? override.blockedServices : []),
351
+ ]);
352
+ return evaluatePolicySet(allowed, blocked, normalizedService, "service_not_allowlisted", "service_blocked");
353
+ }
354
+ function resolvePolicyExtensionReason(policy, extension) {
355
+ const name = normalizePolicyName(extension.name);
356
+ const override = resolvePolicyOverride(policy, extension.name);
357
+ if (override?.disabled === true) {
358
+ return "extension_override_disabled";
359
+ }
360
+ return evaluatePolicySet(policy.allowedExtensions, policy.blockedExtensions, name, "extension_not_allowlisted", "extension_blocked");
361
+ }
362
+ function resolvePolicyTrustReason(policy, extension) {
363
+ if (policy.trustMode === "off") {
364
+ return null;
365
+ }
366
+ const override = resolvePolicyOverride(policy, extension.name);
367
+ const name = normalizePolicyName(extension.name);
368
+ const trusted = extension.trusted === true;
369
+ const provenanceVerified = extension.provenanceVerified === true;
370
+ if (policy.trustedExtensions.size > 0 && !policy.trustedExtensions.has(name)) {
371
+ return "extension_not_trusted";
372
+ }
373
+ if ((override?.requireTrusted === true || policy.trustMode === "warn" || policy.trustMode === "enforce") && !trusted) {
374
+ return "extension_untrusted";
375
+ }
376
+ if ((policy.requireProvenance || override?.requireProvenance === true) && !provenanceVerified) {
377
+ return "provenance_missing_or_unverified";
378
+ }
379
+ return null;
380
+ }
381
+ function resolvePolicySandboxReason(policy, extension) {
382
+ if (policy.mode === "off") {
383
+ return null;
384
+ }
385
+ const override = resolvePolicyOverride(policy, extension.name);
386
+ const profile = override?.sandboxProfile ?? extension.sandboxProfile ?? policy.defaultSandboxProfile;
387
+ if (profile === "none") {
388
+ return null;
389
+ }
390
+ const permissions = extension.permissions;
391
+ if (!permissions) {
392
+ return "sandbox_permissions_missing";
393
+ }
394
+ const hasPermission = (name) => permissions[name] === true;
395
+ if (profile === "restricted") {
396
+ if (hasPermission("process_spawn")) {
397
+ return "sandbox_restricted_disallows_process_spawn";
398
+ }
399
+ if (hasPermission("env_write")) {
400
+ return "sandbox_restricted_disallows_env_write";
401
+ }
402
+ return null;
403
+ }
404
+ if (profile === "strict") {
405
+ if (hasPermission("process_spawn")) {
406
+ return "sandbox_strict_disallows_process_spawn";
407
+ }
408
+ if (hasPermission("network")) {
409
+ return "sandbox_strict_disallows_network";
410
+ }
411
+ if (hasPermission("fs_write")) {
412
+ return "sandbox_strict_disallows_fs_write";
413
+ }
414
+ if (hasPermission("env_write")) {
415
+ return "sandbox_strict_disallows_env_write";
416
+ }
417
+ }
418
+ return null;
419
+ }
420
+ function buildPolicyWarning(mode, scope, extension, reason, details = {}) {
421
+ const tokens = Object.entries(details)
422
+ .sort((left, right) => left[0].localeCompare(right[0]))
423
+ .map(([key, value]) => `${key}=${value}`)
424
+ .join(":");
425
+ const suffix = tokens.length > 0 ? `:${tokens}` : "";
426
+ return `extension_policy_${mode}_${scope}:${extension.layer}:${extension.name}:reason=${reason}${suffix}`;
427
+ }
428
+ function evaluateExtensionPolicyForExtension(policy, extension) {
429
+ if (policy.mode === "off" && policy.trustMode === "off") {
430
+ return { allowed: true, warning: null };
431
+ }
432
+ const reason = resolvePolicyExtensionReason(policy, extension);
433
+ const trustReason = resolvePolicyTrustReason(policy, extension);
434
+ const sandboxReason = resolvePolicySandboxReason(policy, extension);
435
+ const extensionEnforced = reason && policy.mode === "enforce";
436
+ const trustEnforced = trustReason && policy.trustMode === "enforce";
437
+ const sandboxEnforced = sandboxReason && policy.mode === "enforce";
438
+ if (!reason && !trustReason && !sandboxReason) {
439
+ return { allowed: true, warning: null };
440
+ }
441
+ if (extensionEnforced) {
442
+ return {
443
+ allowed: false,
444
+ warning: buildPolicyWarning("blocked", "extension", extension, reason),
445
+ };
446
+ }
447
+ if (trustEnforced) {
448
+ return {
449
+ allowed: false,
450
+ warning: buildPolicyWarning("blocked", "trust", extension, trustReason),
451
+ };
452
+ }
453
+ if (sandboxEnforced) {
454
+ return {
455
+ allowed: false,
456
+ warning: buildPolicyWarning("blocked", "extension", extension, sandboxReason),
457
+ };
458
+ }
459
+ if (reason && policy.mode === "warn") {
460
+ return {
461
+ allowed: true,
462
+ warning: buildPolicyWarning("violation", "extension", extension, reason),
463
+ };
464
+ }
465
+ if (trustReason && policy.trustMode === "warn") {
466
+ return {
467
+ allowed: true,
468
+ warning: buildPolicyWarning("violation", "trust", extension, trustReason),
469
+ };
470
+ }
471
+ if (sandboxReason && policy.mode === "warn") {
472
+ return {
473
+ allowed: true,
474
+ warning: buildPolicyWarning("violation", "extension", extension, sandboxReason),
475
+ };
476
+ }
477
+ return {
478
+ allowed: true,
479
+ warning: null,
480
+ };
481
+ }
482
+ function evaluateExtensionPolicyForCapability(policy, extension, capability) {
483
+ if (policy.mode === "off") {
484
+ return { allowed: true, warning: null };
485
+ }
486
+ const reason = resolvePolicyCapabilityReason(policy, extension, capability);
487
+ if (!reason) {
488
+ return { allowed: true, warning: null };
489
+ }
490
+ return {
491
+ allowed: policy.mode === "warn",
492
+ warning: buildPolicyWarning(policy.mode === "warn" ? "violation" : "blocked", "capability", extension, reason, { capability: capability.trim().toLowerCase() }),
493
+ };
494
+ }
495
+ function evaluateExtensionPolicyForRegistration(policy, extension, surface, method, capability, details) {
496
+ if (policy.mode === "off") {
497
+ return { allowed: true, warning: null };
498
+ }
499
+ const capabilityReason = typeof capability === "string" ? resolvePolicyCapabilityReason(policy, extension, capability) : null;
500
+ const surfaceReason = resolvePolicySurfaceReason(policy, extension, surface);
501
+ const commandReason = details?.command ? resolvePolicyCommandReason(policy, extension, details.command) : null;
502
+ const actionReason = details?.action ? resolvePolicyActionReason(policy, extension, details.action) : null;
503
+ const serviceReason = details?.service ? resolvePolicyServiceReason(policy, extension, details.service) : null;
504
+ const reason = capabilityReason ?? surfaceReason ?? commandReason ?? actionReason ?? serviceReason;
505
+ if (!reason) {
506
+ return { allowed: true, warning: null };
507
+ }
508
+ const warningDetails = {
509
+ method: normalizePolicyName(method).replace(/\s+/g, "_"),
510
+ surface,
511
+ };
512
+ if (capability) {
513
+ warningDetails.capability = capability;
514
+ }
515
+ if (details?.command) {
516
+ warningDetails.command = normalizeCommandName(details.command);
517
+ }
518
+ if (details?.action) {
519
+ warningDetails.action = normalizePolicyName(details.action).replace(/\s+/g, "-");
520
+ }
521
+ if (details?.service) {
522
+ warningDetails.service = normalizePolicyName(details.service);
523
+ }
524
+ const warning = buildPolicyWarning(policy.mode === "warn" ? "violation" : "blocked", "registration", extension, reason, warningDetails);
525
+ return {
526
+ allowed: policy.mode === "warn",
527
+ warning,
528
+ };
529
+ }
19
530
  function resolveLegacyExtensionCapabilityAlias(capability) {
20
531
  const normalized = capability.trim().toLowerCase();
21
532
  if (normalized.length === 0) {
@@ -182,6 +693,94 @@ function parseManifest(raw) {
182
693
  }
183
694
  priority = candidate.priority;
184
695
  }
696
+ let manifestVersion;
697
+ if ("manifest_version" in candidate && candidate.manifest_version !== undefined && candidate.manifest_version !== null) {
698
+ if (typeof candidate.manifest_version !== "number" || !Number.isInteger(candidate.manifest_version)) {
699
+ return null;
700
+ }
701
+ manifestVersion = candidate.manifest_version;
702
+ }
703
+ let trusted;
704
+ if ("trusted" in candidate && candidate.trusted !== undefined && candidate.trusted !== null) {
705
+ if (typeof candidate.trusted !== "boolean") {
706
+ return null;
707
+ }
708
+ trusted = candidate.trusted;
709
+ }
710
+ let sandboxProfile;
711
+ if ("sandbox_profile" in candidate && candidate.sandbox_profile !== undefined && candidate.sandbox_profile !== null) {
712
+ if (typeof candidate.sandbox_profile !== "string") {
713
+ return null;
714
+ }
715
+ const normalizedProfile = normalizePolicySandboxProfile(candidate.sandbox_profile);
716
+ if (normalizedProfile !== candidate.sandbox_profile.trim().toLowerCase()) {
717
+ return null;
718
+ }
719
+ sandboxProfile = normalizedProfile;
720
+ }
721
+ let provenance;
722
+ if ("provenance" in candidate && candidate.provenance !== undefined && candidate.provenance !== null) {
723
+ const provenanceRecord = asRecord(candidate.provenance);
724
+ if (!provenanceRecord) {
725
+ return null;
726
+ }
727
+ const source = typeof provenanceRecord.source === "string" && provenanceRecord.source.trim().length > 0
728
+ ? provenanceRecord.source.trim()
729
+ : undefined;
730
+ const signature = typeof provenanceRecord.signature === "string" && provenanceRecord.signature.trim().length > 0
731
+ ? provenanceRecord.signature.trim()
732
+ : undefined;
733
+ const attestation = typeof provenanceRecord.attestation === "string" && provenanceRecord.attestation.trim().length > 0
734
+ ? provenanceRecord.attestation.trim()
735
+ : undefined;
736
+ const verified = provenanceRecord.verified === undefined || provenanceRecord.verified === null
737
+ ? undefined
738
+ : typeof provenanceRecord.verified === "boolean"
739
+ ? provenanceRecord.verified
740
+ : null;
741
+ if (verified === null) {
742
+ return null;
743
+ }
744
+ provenance = {
745
+ ...(source ? { source } : {}),
746
+ ...(signature ? { signature } : {}),
747
+ ...(attestation ? { attestation } : {}),
748
+ ...(typeof verified === "boolean" ? { verified } : {}),
749
+ };
750
+ }
751
+ let permissions;
752
+ if ("permissions" in candidate && candidate.permissions !== undefined && candidate.permissions !== null) {
753
+ const permissionsRecord = asRecord(candidate.permissions);
754
+ if (!permissionsRecord) {
755
+ return null;
756
+ }
757
+ const parseOptionalBoolean = (value) => {
758
+ if (value === undefined || value === null) {
759
+ return undefined;
760
+ }
761
+ if (typeof value !== "boolean") {
762
+ return null;
763
+ }
764
+ return value;
765
+ };
766
+ const fsRead = parseOptionalBoolean(permissionsRecord.fs_read);
767
+ const fsWrite = parseOptionalBoolean(permissionsRecord.fs_write);
768
+ const network = parseOptionalBoolean(permissionsRecord.network);
769
+ const envRead = parseOptionalBoolean(permissionsRecord.env_read);
770
+ const envWrite = parseOptionalBoolean(permissionsRecord.env_write);
771
+ const processSpawn = parseOptionalBoolean(permissionsRecord.process_spawn);
772
+ if ([fsRead, fsWrite, network, envRead, envWrite, processSpawn].includes(null)) {
773
+ return null;
774
+ }
775
+ permissions = {
776
+ ...(typeof fsRead === "boolean" ? { fs_read: fsRead } : {}),
777
+ ...(typeof fsWrite === "boolean" ? { fs_write: fsWrite } : {}),
778
+ ...(typeof network === "boolean" ? { network } : {}),
779
+ ...(typeof envRead === "boolean" ? { env_read: envRead } : {}),
780
+ ...(typeof envWrite === "boolean" ? { env_write: envWrite } : {}),
781
+ ...(typeof processSpawn === "boolean" ? { process_spawn: processSpawn } : {}),
782
+ };
783
+ }
185
784
  let capabilities = [];
186
785
  let legacyCapabilityAliases = [];
187
786
  if ("capabilities" in candidate && candidate.capabilities !== undefined && candidate.capabilities !== null) {
@@ -197,6 +796,11 @@ function parseManifest(raw) {
197
796
  version: candidate.version.trim(),
198
797
  entry: candidate.entry.trim(),
199
798
  priority,
799
+ manifest_version: manifestVersion,
800
+ trusted,
801
+ provenance,
802
+ sandbox_profile: sandboxProfile,
803
+ permissions,
200
804
  capabilities,
201
805
  legacy_capability_aliases: legacyCapabilityAliases.length > 0 ? legacyCapabilityAliases : undefined,
202
806
  };
@@ -303,6 +907,11 @@ function summarizeCandidate(candidate) {
303
907
  entry: candidate.manifest.entry,
304
908
  priority: candidate.manifest.priority,
305
909
  entry_path: candidate.entry_path,
910
+ manifest_version: candidate.manifest.manifest_version,
911
+ trusted: candidate.manifest.trusted,
912
+ provenance: candidate.manifest.provenance,
913
+ sandbox_profile: candidate.manifest.sandbox_profile,
914
+ permissions: candidate.manifest.permissions,
306
915
  capabilities: [...candidate.manifest.capabilities],
307
916
  };
308
917
  }
@@ -441,6 +1050,8 @@ export async function discoverExtensions(options) {
441
1050
  const roots = resolveExtensionRoots(options.pmRoot, options.cwd ?? process.cwd());
442
1051
  const configured_enabled = normalizeNames(options.settings.extensions.enabled);
443
1052
  const configured_disabled = normalizeNames(options.settings.extensions.disabled);
1053
+ const policy = normalizeExtensionPolicy(options.settings);
1054
+ const serializedPolicy = serializeExtensionPolicy(policy);
444
1055
  if (options.noExtensions) {
445
1056
  return {
446
1057
  disabled_by_flag: true,
@@ -449,14 +1060,50 @@ export async function discoverExtensions(options) {
449
1060
  configured_disabled,
450
1061
  discovered: [],
451
1062
  effective: [],
452
- warnings: [],
1063
+ warnings: [...policy.warnings],
1064
+ policy: serializedPolicy,
453
1065
  };
454
1066
  }
455
1067
  const enabled = new Set(configured_enabled);
456
1068
  const disabled = new Set(configured_disabled);
457
1069
  const globalScan = await scanExtensionLayer("global", roots.global, enabled, disabled);
458
1070
  const projectScan = await scanExtensionLayer("project", roots.project, enabled, disabled);
459
- const effective = buildEffectiveExtensions(globalScan.candidates, projectScan.candidates).map(summarizeCandidate);
1071
+ const policyWarnings = [...policy.warnings];
1072
+ const effectiveCandidates = buildEffectiveExtensions(globalScan.candidates, projectScan.candidates);
1073
+ const effective = [];
1074
+ for (const candidate of effectiveCandidates) {
1075
+ const extensionRef = {
1076
+ layer: candidate.layer,
1077
+ name: candidate.manifest.name,
1078
+ trusted: candidate.manifest.trusted === true,
1079
+ provenanceVerified: candidate.manifest.provenance?.verified === true,
1080
+ sandboxProfile: candidate.manifest.sandbox_profile,
1081
+ permissions: candidate.manifest.permissions && typeof candidate.manifest.permissions === "object"
1082
+ ? {
1083
+ fs_read: candidate.manifest.permissions.fs_read,
1084
+ fs_write: candidate.manifest.permissions.fs_write,
1085
+ network: candidate.manifest.permissions.network,
1086
+ env_read: candidate.manifest.permissions.env_read,
1087
+ env_write: candidate.manifest.permissions.env_write,
1088
+ process_spawn: candidate.manifest.permissions.process_spawn,
1089
+ }
1090
+ : undefined,
1091
+ };
1092
+ const extensionDecision = evaluateExtensionPolicyForExtension(policy, extensionRef);
1093
+ if (extensionDecision.warning) {
1094
+ policyWarnings.push(extensionDecision.warning);
1095
+ }
1096
+ if (!extensionDecision.allowed) {
1097
+ continue;
1098
+ }
1099
+ for (const capability of candidate.manifest.capabilities) {
1100
+ const capabilityDecision = evaluateExtensionPolicyForCapability(policy, extensionRef, capability);
1101
+ if (capabilityDecision.warning) {
1102
+ policyWarnings.push(capabilityDecision.warning);
1103
+ }
1104
+ }
1105
+ effective.push(summarizeCandidate(candidate));
1106
+ }
460
1107
  return {
461
1108
  disabled_by_flag: false,
462
1109
  roots,
@@ -464,7 +1111,8 @@ export async function discoverExtensions(options) {
464
1111
  configured_disabled,
465
1112
  discovered: [...globalScan.diagnostics, ...projectScan.diagnostics],
466
1113
  effective,
467
- warnings: [...globalScan.warnings, ...projectScan.warnings],
1114
+ warnings: [...new Set([...globalScan.warnings, ...projectScan.warnings, ...policyWarnings])],
1115
+ policy: serializedPolicy,
468
1116
  };
469
1117
  }
470
1118
  function formatUnknownError(error) {
@@ -473,6 +1121,31 @@ function formatUnknownError(error) {
473
1121
  }
474
1122
  return String(error);
475
1123
  }
1124
+ async function fingerprintPath(pathToInspect) {
1125
+ try {
1126
+ const stats = await fs.stat(pathToInspect);
1127
+ return `${Math.trunc(stats.mtimeMs)}-${stats.size}`;
1128
+ }
1129
+ catch {
1130
+ return "missing";
1131
+ }
1132
+ }
1133
+ async function resolveExtensionImportHref(extension, options) {
1134
+ const baseUrl = new URL(pathToFileURL(extension.entry_path).href);
1135
+ const shouldCacheBust = options.cache_bust === true || typeof options.reload_token === "string";
1136
+ if (!shouldCacheBust) {
1137
+ return baseUrl.href;
1138
+ }
1139
+ const [entryFingerprint, manifestFingerprint] = await Promise.all([
1140
+ fingerprintPath(extension.entry_path),
1141
+ fingerprintPath(extension.manifest_path),
1142
+ ]);
1143
+ const reloadToken = options.reload_token ?? nextExtensionReloadToken();
1144
+ baseUrl.searchParams.set("pm_ext_reload", reloadToken);
1145
+ baseUrl.searchParams.set("pm_ext_entry", entryFingerprint);
1146
+ baseUrl.searchParams.set("pm_ext_manifest", manifestFingerprint);
1147
+ return baseUrl.href;
1148
+ }
476
1149
  export async function loadExtensions(options) {
477
1150
  const discovery = await discoverExtensions(options);
478
1151
  const loaded = [];
@@ -488,7 +1161,8 @@ export async function loadExtensions(options) {
488
1161
  }
489
1162
  for (const extension of discovery.effective) {
490
1163
  try {
491
- const module = await import(pathToFileURL(extension.entry_path).href);
1164
+ const importHref = await resolveExtensionImportHref(extension, options);
1165
+ const module = await import(importHref);
492
1166
  loaded.push({
493
1167
  ...extension,
494
1168
  module,
@@ -912,7 +1586,34 @@ function assertExtensionCapability(extension, capability, method) {
912
1586
  throw new TypeError(`${method} requires capability '${capability}' in extension manifest capabilities`);
913
1587
  }
914
1588
  }
915
- function createExtensionApi(extension, hooks, commands, parsers, preflight, services, renderers, registrations, activationWarnings) {
1589
+ function createExtensionApi(extension, hooks, commands, parsers, preflight, services, renderers, registrations, activationWarnings, policy) {
1590
+ const extensionRef = {
1591
+ layer: extension.layer,
1592
+ name: extension.name,
1593
+ trusted: extension.trusted === true,
1594
+ provenanceVerified: extension.provenance?.verified === true,
1595
+ sandboxProfile: extension.sandbox_profile,
1596
+ permissions: extension.permissions && typeof extension.permissions === "object"
1597
+ ? {
1598
+ fs_read: extension.permissions.fs_read,
1599
+ fs_write: extension.permissions.fs_write,
1600
+ network: extension.permissions.network,
1601
+ env_read: extension.permissions.env_read,
1602
+ env_write: extension.permissions.env_write,
1603
+ process_spawn: extension.permissions.process_spawn,
1604
+ }
1605
+ : undefined,
1606
+ };
1607
+ const pushPolicyWarning = (warning) => {
1608
+ if (warning) {
1609
+ activationWarnings.push(warning);
1610
+ }
1611
+ };
1612
+ const allowRegistration = (surface, method, capability, details) => {
1613
+ const decision = evaluateExtensionPolicyForRegistration(policy, extensionRef, surface, method, capability, details);
1614
+ pushPolicyWarning(decision.warning);
1615
+ return decision.allowed;
1616
+ };
916
1617
  const registerCommandTrace = (mode, command, expectedSchema, received, hint) => ({
917
1618
  method: "registerCommand",
918
1619
  registration_index: mode === "override" ? commands.overrides.length : commands.handlers.length,
@@ -932,6 +1633,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
932
1633
  const trace = registerCommandTrace("override", normalizedCommand, 'registerCommand("<command>", (context) => unknown)', { command: commandOrDefinition, override }, "Provide a function as the second registerCommand argument.");
933
1634
  throw createRegistrationValidationError(`registerCommand requires an override function when command name is provided (command="${normalizedCommand}", registration_index=${trace.registration_index})`, trace);
934
1635
  }
1636
+ if (!allowRegistration("commands.override", "registerCommand", "commands", { command: normalizedCommand })) {
1637
+ return;
1638
+ }
935
1639
  commands.overrides.push({
936
1640
  layer: extension.layer,
937
1641
  name: extension.name,
@@ -965,6 +1669,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
965
1669
  assertOptionalStringField("registerCommand definition.description", commandOrDefinition.description);
966
1670
  assertOptionalStringField("registerCommand definition.intent", commandOrDefinition.intent);
967
1671
  const action = resolveCommandDefinitionAction(normalizedCommand, commandOrDefinition.action);
1672
+ if (!allowRegistration("commands.handler", "registerCommand", "commands", { command: normalizedCommand, action })) {
1673
+ return;
1674
+ }
968
1675
  const description = commandOrDefinition.description?.trim();
969
1676
  const intent = commandOrDefinition.intent?.trim();
970
1677
  const examples = normalizeOptionalStringArrayField("registerCommand definition.examples", commandOrDefinition.examples);
@@ -1011,6 +1718,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1011
1718
  };
1012
1719
  const registerParser = (command, override) => {
1013
1720
  assertExtensionCapability(extension, "parser", "registerParser");
1721
+ if (!allowRegistration("parser.override", "registerParser", "parser")) {
1722
+ return;
1723
+ }
1014
1724
  const normalizedCommand = normalizeCommandName(assertNonEmptyString("registerParser command", command));
1015
1725
  assertFunctionHandler("registerParser override", override);
1016
1726
  parsers.overrides.push({
@@ -1022,6 +1732,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1022
1732
  };
1023
1733
  const registerPreflight = (override) => {
1024
1734
  assertExtensionCapability(extension, "preflight", "registerPreflight");
1735
+ if (!allowRegistration("preflight.override", "registerPreflight", "preflight")) {
1736
+ return;
1737
+ }
1025
1738
  assertFunctionHandler("registerPreflight override", override);
1026
1739
  preflight.overrides.push({
1027
1740
  layer: extension.layer,
@@ -1035,6 +1748,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1035
1748
  if (!isExtensionServiceName(normalizedService)) {
1036
1749
  throw new TypeError(`registerService service must be one of: ${EXTENSION_SERVICE_NAMES.join(", ")}`);
1037
1750
  }
1751
+ if (!allowRegistration("services.override", "registerService", "services", { service: normalizedService })) {
1752
+ return;
1753
+ }
1038
1754
  assertFunctionHandler("registerService override", override);
1039
1755
  services.overrides.push({
1040
1756
  layer: extension.layer,
@@ -1045,6 +1761,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1045
1761
  };
1046
1762
  const registerRenderer = (format, renderer) => {
1047
1763
  assertExtensionCapability(extension, "renderers", "registerRenderer");
1764
+ if (!allowRegistration("renderers.override", "registerRenderer", "renderers")) {
1765
+ return;
1766
+ }
1048
1767
  if (typeof renderer !== "function") {
1049
1768
  throw new TypeError("registerRenderer requires a renderer function");
1050
1769
  }
@@ -1061,6 +1780,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1061
1780
  };
1062
1781
  const registerFlags = (targetCommand, flags) => {
1063
1782
  assertExtensionCapability(extension, "schema", "registerFlags");
1783
+ if (!allowRegistration("schema.flags", "registerFlags", "schema")) {
1784
+ return;
1785
+ }
1064
1786
  const normalizedTargetCommand = normalizeCommandName(assertNonEmptyString("registerFlags targetCommand", targetCommand));
1065
1787
  validateFlagDefinitions(flags);
1066
1788
  const normalizedFlags = normalizeRegistrationRecordList("registerFlags flags", flags);
@@ -1076,6 +1798,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1076
1798
  };
1077
1799
  const registerItemFields = (fields) => {
1078
1800
  assertExtensionCapability(extension, "schema", "registerItemFields");
1801
+ if (!allowRegistration("schema.itemfields", "registerItemFields", "schema")) {
1802
+ return;
1803
+ }
1079
1804
  validateItemFieldDefinitions(fields);
1080
1805
  const normalizedFields = normalizeRegistrationRecordList("registerItemFields fields", fields);
1081
1806
  if (normalizedFields.length === 0) {
@@ -1089,6 +1814,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1089
1814
  };
1090
1815
  const registerItemTypes = (types) => {
1091
1816
  assertExtensionCapability(extension, "schema", "registerItemTypes");
1817
+ if (!allowRegistration("schema.itemtypes", "registerItemTypes", "schema")) {
1818
+ return;
1819
+ }
1092
1820
  validateItemTypeDefinitions(types);
1093
1821
  const normalizedTypes = normalizeRegistrationRecordList("registerItemTypes types", types);
1094
1822
  if (normalizedTypes.length === 0) {
@@ -1102,6 +1830,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1102
1830
  };
1103
1831
  const registerMigration = (definition) => {
1104
1832
  assertExtensionCapability(extension, "schema", "registerMigration");
1833
+ if (!allowRegistration("schema.migrations", "registerMigration", "schema")) {
1834
+ return;
1835
+ }
1105
1836
  validateMigrationDefinition(definition);
1106
1837
  const runtimeDefinition = normalizeRuntimeRegistrationRecord("registerMigration definition", definition);
1107
1838
  registrations.migrations.push(attachRuntimeDefinition({
@@ -1112,6 +1843,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1112
1843
  };
1113
1844
  const registerImporter = (name, importer) => {
1114
1845
  assertExtensionCapability(extension, "importers", "registerImporter");
1846
+ if (!allowRegistration("importers.importer", "registerImporter", "importers")) {
1847
+ return;
1848
+ }
1115
1849
  const normalizedName = normalizeRegistrationName(assertNonEmptyString("registerImporter name", name));
1116
1850
  assertFunctionHandler("registerImporter importer", importer);
1117
1851
  const commandPath = toRegistrationCommandPath(normalizedName, "import");
@@ -1137,6 +1871,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1137
1871
  };
1138
1872
  const registerExporter = (name, exporter) => {
1139
1873
  assertExtensionCapability(extension, "importers", "registerExporter");
1874
+ if (!allowRegistration("importers.exporter", "registerExporter", "importers")) {
1875
+ return;
1876
+ }
1140
1877
  const normalizedName = normalizeRegistrationName(assertNonEmptyString("registerExporter name", name));
1141
1878
  assertFunctionHandler("registerExporter exporter", exporter);
1142
1879
  const commandPath = toRegistrationCommandPath(normalizedName, "export");
@@ -1162,6 +1899,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1162
1899
  };
1163
1900
  const registerSearchProvider = (provider) => {
1164
1901
  assertExtensionCapability(extension, "search", "registerSearchProvider");
1902
+ if (!allowRegistration("search.provider", "registerSearchProvider", "search")) {
1903
+ return;
1904
+ }
1165
1905
  const runtimeDefinition = normalizeRuntimeRegistrationRecord("registerSearchProvider provider", provider);
1166
1906
  registrations.search_providers.push(attachRuntimeDefinition({
1167
1907
  layer: extension.layer,
@@ -1171,6 +1911,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1171
1911
  };
1172
1912
  const registerVectorStoreAdapter = (adapter) => {
1173
1913
  assertExtensionCapability(extension, "search", "registerVectorStoreAdapter");
1914
+ if (!allowRegistration("search.vectorstore", "registerVectorStoreAdapter", "search")) {
1915
+ return;
1916
+ }
1174
1917
  const runtimeDefinition = normalizeRuntimeRegistrationRecord("registerVectorStoreAdapter adapter", adapter);
1175
1918
  registrations.vector_store_adapters.push(attachRuntimeDefinition({
1176
1919
  layer: extension.layer,
@@ -1180,6 +1923,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1180
1923
  };
1181
1924
  const registerBeforeCommand = (hook) => {
1182
1925
  assertExtensionCapability(extension, "hooks", "api.hooks.beforeCommand");
1926
+ if (!allowRegistration("hooks.beforecommand", "api.hooks.beforeCommand", "hooks")) {
1927
+ return;
1928
+ }
1183
1929
  assertHookHandler("beforeCommand", hook);
1184
1930
  hooks.beforeCommand.push({
1185
1931
  layer: extension.layer,
@@ -1189,6 +1935,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1189
1935
  };
1190
1936
  const registerAfterCommand = (hook) => {
1191
1937
  assertExtensionCapability(extension, "hooks", "api.hooks.afterCommand");
1938
+ if (!allowRegistration("hooks.aftercommand", "api.hooks.afterCommand", "hooks")) {
1939
+ return;
1940
+ }
1192
1941
  assertHookHandler("afterCommand", hook);
1193
1942
  hooks.afterCommand.push({
1194
1943
  layer: extension.layer,
@@ -1198,6 +1947,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1198
1947
  };
1199
1948
  const registerOnWrite = (hook) => {
1200
1949
  assertExtensionCapability(extension, "hooks", "api.hooks.onWrite");
1950
+ if (!allowRegistration("hooks.onwrite", "api.hooks.onWrite", "hooks")) {
1951
+ return;
1952
+ }
1201
1953
  assertHookHandler("onWrite", hook);
1202
1954
  hooks.onWrite.push({
1203
1955
  layer: extension.layer,
@@ -1207,6 +1959,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1207
1959
  };
1208
1960
  const registerOnRead = (hook) => {
1209
1961
  assertExtensionCapability(extension, "hooks", "api.hooks.onRead");
1962
+ if (!allowRegistration("hooks.onread", "api.hooks.onRead", "hooks")) {
1963
+ return;
1964
+ }
1210
1965
  assertHookHandler("onRead", hook);
1211
1966
  hooks.onRead.push({
1212
1967
  layer: extension.layer,
@@ -1216,6 +1971,9 @@ function createExtensionApi(extension, hooks, commands, parsers, preflight, serv
1216
1971
  };
1217
1972
  const registerOnIndex = (hook) => {
1218
1973
  assertExtensionCapability(extension, "hooks", "api.hooks.onIndex");
1974
+ if (!allowRegistration("hooks.onindex", "api.hooks.onIndex", "hooks")) {
1975
+ return;
1976
+ }
1219
1977
  assertHookHandler("onIndex", hook);
1220
1978
  hooks.onIndex.push({
1221
1979
  layer: extension.layer,
@@ -1374,6 +2132,7 @@ function collectServiceCollisionWarnings(services) {
1374
2132
  return warnings;
1375
2133
  }
1376
2134
  export async function activateExtensions(loadResult) {
2135
+ const policy = hydrateExtensionPolicy(loadResult.policy ?? DEFAULT_EXTENSION_POLICY);
1377
2136
  const hooks = createEmptyExtensionHookRegistry();
1378
2137
  const commands = createEmptyExtensionCommandRegistry();
1379
2138
  const parsers = createEmptyExtensionParserRegistry();
@@ -1389,7 +2148,7 @@ export async function activateExtensions(loadResult) {
1389
2148
  continue;
1390
2149
  }
1391
2150
  try {
1392
- await activatable.activate(createExtensionApi(extension, hooks, commands, parsers, preflight, services, renderers, registrations, warnings));
2151
+ await activatable.activate(createExtensionApi(extension, hooks, commands, parsers, preflight, services, renderers, registrations, warnings, policy));
1393
2152
  }
1394
2153
  catch (error) {
1395
2154
  warnings.push(`extension_activate_failed:${extension.layer}:${extension.name}`);