@unbrained/pm-cli 2026.9.6 → 2026.9.8

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 (719) hide show
  1. package/.agents/skills/pm-developer/SKILL.md +12 -4
  2. package/.agents/skills/pm-developer/references/MULTI_AGENT_MERGE.md +19 -10
  3. package/.agents/skills/pm-extensions/SKILL.md +6 -0
  4. package/.agents/skills/pm-sdk/SKILL.md +11 -2
  5. package/.agents/skills/pm-user/SKILL.md +5 -0
  6. package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +95 -0
  7. package/.claude-plugin/marketplace.json +2 -2
  8. package/CHANGELOG.md +218 -182
  9. package/dist/cli/after-command-context-usage.js +1 -48
  10. package/dist/cli/argv-utils.js +1 -40
  11. package/dist/cli/bootstrap-args.js +1 -9
  12. package/dist/cli/bundle-integrity.js +2 -110
  13. package/dist/cli/commander-usage.js +11 -771
  14. package/dist/cli/commands/activity.js +1 -9
  15. package/dist/cli/commands/aggregate.js +1 -5
  16. package/dist/cli/commands/append.js +1 -5
  17. package/dist/cli/commands/claim.js +1 -5
  18. package/dist/cli/commands/close-many.js +1 -5
  19. package/dist/cli/commands/close.js +1 -9
  20. package/dist/cli/commands/comments.js +1 -5
  21. package/dist/cli/commands/config.js +1 -9
  22. package/dist/cli/commands/context.js +1 -9
  23. package/dist/cli/commands/contracts.js +1 -5
  24. package/dist/cli/commands/copy.js +1 -9
  25. package/dist/cli/commands/create.js +1 -9
  26. package/dist/cli/commands/delete.js +1 -9
  27. package/dist/cli/commands/deps.js +1 -9
  28. package/dist/cli/commands/docs.js +1 -9
  29. package/dist/cli/commands/duplicates.js +1 -9
  30. package/dist/cli/commands/eval.js +1 -9
  31. package/dist/cli/commands/extension/describe.js +1 -5
  32. package/dist/cli/commands/extension/scaffold.js +1 -5
  33. package/dist/cli/commands/extension/shared.js +1 -5
  34. package/dist/cli/commands/extension.js +1 -9
  35. package/dist/cli/commands/files.js +1 -9
  36. package/dist/cli/commands/focus.js +1 -9
  37. package/dist/cli/commands/gc.js +1 -9
  38. package/dist/cli/commands/get.js +1 -9
  39. package/dist/cli/commands/graph.js +1 -10
  40. package/dist/cli/commands/health.js +1 -9
  41. package/dist/cli/commands/history-compact.js +1 -10
  42. package/dist/cli/commands/history-redact.js +1 -11
  43. package/dist/cli/commands/history-repair.js +1 -10
  44. package/dist/cli/commands/history.js +1 -5
  45. package/dist/cli/commands/index.js +1 -58
  46. package/dist/cli/commands/init-agent-guidance.js +1 -9
  47. package/dist/cli/commands/init.js +1 -9
  48. package/dist/cli/commands/learnings.js +1 -5
  49. package/dist/cli/commands/lifecycle-transitions.js +1 -5
  50. package/dist/cli/commands/list.js +1 -9
  51. package/dist/cli/commands/merge.js +1 -11
  52. package/dist/cli/commands/next.js +1 -9
  53. package/dist/cli/commands/notes.js +1 -5
  54. package/dist/cli/commands/plan.js +1 -9
  55. package/dist/cli/commands/profile.js +1 -9
  56. package/dist/cli/commands/restore.js +1 -9
  57. package/dist/cli/commands/scheduling-shortcuts.js +1 -9
  58. package/dist/cli/commands/schema.js +1 -10
  59. package/dist/cli/commands/search.js +1 -9
  60. package/dist/cli/commands/stats.js +1 -9
  61. package/dist/cli/commands/telemetry.js +1 -9
  62. package/dist/cli/commands/test-all.js +1 -9
  63. package/dist/cli/commands/test-runs.js +1 -9
  64. package/dist/cli/commands/test.js +1 -9
  65. package/dist/cli/commands/update-many.js +1 -5
  66. package/dist/cli/commands/update.js +1 -9
  67. package/dist/cli/commands/upgrade.js +1 -5
  68. package/dist/cli/commands/validate.js +1 -15
  69. package/dist/cli/context-intent-invocation.js +1 -20
  70. package/dist/cli/description-stdin.js +1 -17
  71. package/dist/cli/error-guidance.js +1 -1353
  72. package/dist/cli/extension-command-help.js +5 -636
  73. package/dist/cli/extension-command-options.js +1 -427
  74. package/dist/cli/extension-recovery.js +1 -126
  75. package/dist/cli/help-content.js +2 -1001
  76. package/dist/cli/help-json-payload.js +4 -570
  77. package/dist/cli/main.d.ts +4 -0
  78. package/dist/cli/main.js +5 -2573
  79. package/dist/cli/migration-gates.js +1 -289
  80. package/dist/cli/public.js +1 -20
  81. package/dist/cli/register-annotations.js +1 -270
  82. package/dist/cli/register-assurance.js +1 -62
  83. package/dist/cli/register-files-lookup.js +1 -56
  84. package/dist/cli/register-history-activity.d.ts +8 -0
  85. package/dist/cli/register-history-activity.js +2 -0
  86. package/dist/cli/register-history-author.js +1 -38
  87. package/dist/cli/register-history-maintenance.d.ts +9 -0
  88. package/dist/cli/register-history-maintenance.js +2 -0
  89. package/dist/cli/register-list-query.js +10 -943
  90. package/dist/cli/register-mutation-options.js +1 -9
  91. package/dist/cli/register-mutation.js +19 -1893
  92. package/dist/cli/register-operations.js +1 -920
  93. package/dist/cli/register-setup.js +1 -716
  94. package/dist/cli/register-structured-mutation.js +1 -196
  95. package/dist/cli/registration-helpers.js +1 -5
  96. package/dist/cli/runtime-compatibility-boundary.js +4 -42
  97. package/dist/cli/schema-registration-helpers.js +1 -73
  98. package/dist/cli/search-refresh.js +1 -13
  99. package/dist/cli/stats-analytics-json.js +1 -68
  100. package/dist/cli/telemetry-flush.js +1 -10
  101. package/dist/cli-bundle/bundle-manifest.json +164 -156
  102. package/dist/cli-bundle/chunks/chunk-3BZZA6A3.js +5 -0
  103. package/dist/cli-bundle/chunks/chunk-AMRDVX36.js +235 -0
  104. package/dist/cli-bundle/chunks/chunk-DAM4CBBR.js +2202 -0
  105. package/dist/cli-bundle/chunks/{chunk-WYRZZ5ZE.js → chunk-DEZPZOQ7.js} +2 -2
  106. package/dist/cli-bundle/chunks/chunk-FLDWYEND.js +77 -0
  107. package/dist/cli-bundle/chunks/{chunk-NPN5I67J.js → chunk-G5ADGQ2D.js} +3 -3
  108. package/dist/cli-bundle/chunks/chunk-JAAEYHUA.js +2 -0
  109. package/dist/cli-bundle/chunks/{chunk-JX4DH4WC.js → chunk-KR3RP4FH.js} +3 -3
  110. package/dist/cli-bundle/chunks/chunk-LEZOUXG6.js +2 -0
  111. package/dist/cli-bundle/chunks/chunk-NSAPNEYH.js +2 -0
  112. package/dist/cli-bundle/chunks/chunk-QOEKOECV.js +29 -0
  113. package/dist/cli-bundle/chunks/chunk-RHSFOMHU.js +2 -0
  114. package/dist/cli-bundle/chunks/chunk-XOD2PCB6.js +20 -0
  115. package/dist/cli-bundle/chunks/{chunk-JT6XF6GE.js → chunk-YVEQ2AMO.js} +4 -4
  116. package/dist/cli-bundle/chunks/register-list-query-KSK3R63M.js +11 -0
  117. package/dist/cli-bundle/chunks/register-mutation-IQ5C7U6O.js +20 -0
  118. package/dist/cli-bundle/chunks/{register-operations-I5W3KMGB.js → register-operations-DWVHR2NC.js} +2 -2
  119. package/dist/cli-bundle/chunks/register-setup-BQ5SVD4B.js +2 -0
  120. package/dist/cli-bundle/focused-chunks/chunk-3WRPWLA7.js +3 -0
  121. package/dist/cli-bundle/focused-chunks/chunk-7MBUII7U.js +2 -0
  122. package/dist/cli-bundle/focused-chunks/chunk-COPBO5HW.js +8 -0
  123. package/dist/cli-bundle/focused-chunks/chunk-DUN4IXYN.js +20 -0
  124. package/dist/cli-bundle/focused-chunks/{chunk-LIJIA4TY.js → chunk-EE5QSM4Z.js} +3 -3
  125. package/dist/cli-bundle/focused-chunks/chunk-HJVARYM3.js +6 -0
  126. package/dist/cli-bundle/focused-chunks/chunk-IAUOUGVR.js +177 -0
  127. package/dist/cli-bundle/focused-chunks/chunk-KOOMJ54F.js +2 -0
  128. package/dist/cli-bundle/focused-chunks/chunk-MZDXX7JY.js +56 -0
  129. package/dist/cli-bundle/focused-chunks/{chunk-M6PBRFSH.js → chunk-OPFHFGDD.js} +2 -2
  130. package/dist/cli-bundle/focused-chunks/chunk-QHNETP77.js +9 -0
  131. package/dist/cli-bundle/focused-chunks/{chunk-3BZEOXLT.js → chunk-RMWES4M6.js} +2 -2
  132. package/dist/cli-bundle/focused-chunks/chunk-RXT4P5QH.js +10 -0
  133. package/dist/cli-bundle/focused-chunks/chunk-SDOL6FM2.js +12 -0
  134. package/dist/cli-bundle/focused-chunks/chunk-SQV4AAUC.js +2 -0
  135. package/dist/cli-bundle/focused-chunks/{chunk-PEFABF7E.js → chunk-WE3IUNIW.js} +3 -3
  136. package/dist/cli-bundle/focused-chunks/chunk-YALOUBWJ.js +2 -0
  137. package/dist/cli-bundle/main.js +20 -20
  138. package/dist/cli-bundle/sdk-authoring.js +1 -1
  139. package/dist/cli-bundle/sdk-contracts.js +2 -2
  140. package/dist/cli-bundle/sdk-core.js +29 -29
  141. package/dist/cli-bundle/sdk-governance.js +1 -1
  142. package/dist/cli-bundle/sdk-graph.js +1 -1
  143. package/dist/cli-bundle/sdk-merge.js +42 -42
  144. package/dist/cli-bundle/sdk-query.js +1 -1
  145. package/dist/cli-bundle/sdk-runtime.js +1 -1
  146. package/dist/cli-bundle/sdk-testing.js +1 -1
  147. package/dist/cli-bundle/sdk.js +11 -11
  148. package/dist/cli.js +1 -104
  149. package/dist/core/checkpoint/checkpoint-gc.js +1 -169
  150. package/dist/core/checkpoint/mutation-checkpoint.js +2 -133
  151. package/dist/core/config/nested-settings.js +1 -534
  152. package/dist/core/config/positional-value.js +1 -109
  153. package/dist/core/diagnostics/remediation.js +1 -506
  154. package/dist/core/extensions/activation-summary-markdown.js +2 -145
  155. package/dist/core/extensions/activation-summary.js +1 -92
  156. package/dist/core/extensions/activation-transaction.js +1 -46
  157. package/dist/core/extensions/capability-usage.js +1 -173
  158. package/dist/core/extensions/command-hook-context.js +1 -13
  159. package/dist/core/extensions/command-metadata-contract.js +1 -22
  160. package/dist/core/extensions/command-visibility-tier.js +1 -29
  161. package/dist/core/extensions/contribution-inventory.js +1 -173
  162. package/dist/core/extensions/exporter-output-contract.js +1 -66
  163. package/dist/core/extensions/extension-candidate.js +1 -18
  164. package/dist/core/extensions/extension-capability-aliases.js +1 -158
  165. package/dist/core/extensions/extension-hook-runtime.js +1 -699
  166. package/dist/core/extensions/extension-policy.js +1 -599
  167. package/dist/core/extensions/extension-registries.js +1 -62
  168. package/dist/core/extensions/extension-runtime-helpers.js +1 -33
  169. package/dist/core/extensions/extension-types.js +1 -102
  170. package/dist/core/extensions/flag-definition-validation.js +1 -52
  171. package/dist/core/extensions/flag-value-types.js +1 -64
  172. package/dist/core/extensions/index.js +1 -526
  173. package/dist/core/extensions/item-field-types.js +1 -39
  174. package/dist/core/extensions/item-fields.js +1 -261
  175. package/dist/core/extensions/loader.js +1 -2223
  176. package/dist/core/extensions/manifest-schema.js +1 -69
  177. package/dist/core/extensions/module-graph-snapshot.js +1 -33
  178. package/dist/core/extensions/preflight-ownership.js +1 -41
  179. package/dist/core/extensions/registration-validation.js +1 -82
  180. package/dist/core/extensions/registration-values.js +1 -209
  181. package/dist/core/extensions/reserved-host-flags.js +1 -36
  182. package/dist/core/extensions/runtime-registrations.js +1 -67
  183. package/dist/core/extensions/service-ownership.js +1 -19
  184. package/dist/core/extensions/version-compat.js +1 -216
  185. package/dist/core/fs/fs-utils.js +2 -158
  186. package/dist/core/fs/host-environment-errors.js +1 -141
  187. package/dist/core/fs/index.js +1 -9
  188. package/dist/core/fs/path-utils.js +1 -17
  189. package/dist/core/governance/content-fields.js +1 -160
  190. package/dist/core/governance/issue-codes.js +1 -151
  191. package/dist/core/governance/metadata-coverage.js +1 -358
  192. package/dist/core/history/drift-cache.js +3 -72
  193. package/dist/core/history/drift-scan.js +1 -342
  194. package/dist/core/history/event-classification.js +1 -109
  195. package/dist/core/history/event-index.js +8 -784
  196. package/dist/core/history/history-compact-bulk.js +1 -104
  197. package/dist/core/history/history-diff.js +1 -158
  198. package/dist/core/history/history-rewrite.d.ts +6 -1
  199. package/dist/core/history/history-rewrite.js +1 -107
  200. package/dist/core/history/history-storage-stats.js +1 -102
  201. package/dist/core/history/history-stream-policy.js +1 -62
  202. package/dist/core/history/history.js +2 -617
  203. package/dist/core/history/identity.js +1 -30
  204. package/dist/core/history/index.js +1 -9
  205. package/dist/core/history/projection.js +1 -196
  206. package/dist/core/history/read.d.ts +9 -0
  207. package/dist/core/history/read.js +1 -57
  208. package/dist/core/history/replay.js +2 -439
  209. package/dist/core/history/version-address.js +1 -32
  210. package/dist/core/history/workspace-history.js +2 -433
  211. package/dist/core/io/body-file.js +1 -62
  212. package/dist/core/io/bulk-ids-input.js +1 -112
  213. package/dist/core/item/actionability.js +1 -327
  214. package/dist/core/item/dependency-reference.js +1 -20
  215. package/dist/core/item/id.js +1 -112
  216. package/dist/core/item/index.js +1 -12
  217. package/dist/core/item/item-format-version.js +1 -78
  218. package/dist/core/item/item-format.js +5 -1473
  219. package/dist/core/item/item-record.js +1 -23
  220. package/dist/core/item/item-type-definition.js +1 -133
  221. package/dist/core/item/parent-reference-policy.js +1 -57
  222. package/dist/core/item/parse.js +2 -603
  223. package/dist/core/item/plan-lifecycle.js +1 -19
  224. package/dist/core/item/priority.js +1 -61
  225. package/dist/core/item/sprint-release-format.js +1 -38
  226. package/dist/core/item/status-filter.js +1 -115
  227. package/dist/core/item/status.js +1 -43
  228. package/dist/core/item/toon-decode.js +1 -29
  229. package/dist/core/item/type-registry.js +1 -715
  230. package/dist/core/item/type-synonyms.js +1 -38
  231. package/dist/core/lock/index.js +1 -9
  232. package/dist/core/lock/lock-gc.js +1 -240
  233. package/dist/core/lock/lock.js +3 -590
  234. package/dist/core/output/mutation-projection.js +1 -217
  235. package/dist/core/output/output-control.js +1 -25
  236. package/dist/core/output/output.js +11 -494
  237. package/dist/core/output/query-summary.js +1 -58
  238. package/dist/core/output/tabular.js +1 -77
  239. package/dist/core/packages/manifest.js +1 -244
  240. package/dist/core/packages/root.js +1 -84
  241. package/dist/core/profile/profile-describe.js +1 -83
  242. package/dist/core/profile/profile-lint.js +1 -415
  243. package/dist/core/profile/profile-plan.js +0 -0
  244. package/dist/core/profile/profile-presets.js +1 -370
  245. package/dist/core/reproducibility/context.js +1 -82
  246. package/dist/core/schema/fields-file.js +2 -288
  247. package/dist/core/schema/item-types-file.js +2 -296
  248. package/dist/core/schema/runtime-field-filters.js +1 -49
  249. package/dist/core/schema/runtime-field-values.js +1 -313
  250. package/dist/core/schema/runtime-schema.js +2 -826
  251. package/dist/core/schema/status-defs-file.js +2 -227
  252. package/dist/core/schema/status-token.js +1 -31
  253. package/dist/core/schema/type-inference.js +1 -95
  254. package/dist/core/schema/type-presets.js +1 -87
  255. package/dist/core/schema/type-workflows.js +1 -104
  256. package/dist/core/search/background-refresh.js +2 -286
  257. package/dist/core/search/bm25.js +1 -146
  258. package/dist/core/search/cache.js +2 -534
  259. package/dist/core/search/corpus.js +1 -311
  260. package/dist/core/search/embedding-batches.js +1 -163
  261. package/dist/core/search/eval.js +1 -225
  262. package/dist/core/search/http-client.js +1 -75
  263. package/dist/core/search/providers.js +1 -242
  264. package/dist/core/search/relevance.js +1 -244
  265. package/dist/core/search/semantic-defaults.js +1 -199
  266. package/dist/core/search/staleness.js +1 -30
  267. package/dist/core/search/vector-stores.js +2 -664
  268. package/dist/core/search/vectorization-metadata.js +1 -81
  269. package/dist/core/sentry/helpers.js +1 -272
  270. package/dist/core/sentry/instrument.js +1 -387
  271. package/dist/core/session/session-state.js +1 -260
  272. package/dist/core/shared/agent-session-context.js +1 -133
  273. package/dist/core/shared/author.js +1 -1172
  274. package/dist/core/shared/command-types.js +1 -4
  275. package/dist/core/shared/conflict-markers.js +1 -31
  276. package/dist/core/shared/constants.js +1 -405
  277. package/dist/core/shared/errors.js +1 -20
  278. package/dist/core/shared/html-entity-decode.js +1 -131
  279. package/dist/core/shared/index.js +1 -12
  280. package/dist/core/shared/item-metadata-contract.js +1 -89
  281. package/dist/core/shared/lazy-module.js +1 -16
  282. package/dist/core/shared/levenshtein.js +1 -61
  283. package/dist/core/shared/memo.js +1 -19
  284. package/dist/core/shared/numeric-parsers.js +1 -20
  285. package/dist/core/shared/option-alias-visibility.js +1 -67
  286. package/dist/core/shared/primitives.js +1 -127
  287. package/dist/core/shared/serial-queue.js +1 -79
  288. package/dist/core/shared/serialization.js +1 -210
  289. package/dist/core/shared/split-comma-list.js +1 -30
  290. package/dist/core/shared/text-normalization.js +1 -38
  291. package/dist/core/shared/time.js +1 -306
  292. package/dist/core/store/index.js +1 -12
  293. package/dist/core/store/item-format-migration.js +1 -125
  294. package/dist/core/store/item-metadata-cache.js +1 -1116
  295. package/dist/core/store/item-metadata-query-index.js +9 -394
  296. package/dist/core/store/item-metadata-read-work.js +1 -43
  297. package/dist/core/store/item-store.js +1 -693
  298. package/dist/core/store/paths.d.ts +1 -1
  299. package/dist/core/store/paths.js +1 -214
  300. package/dist/core/store/settings-read-cache.js +1 -85
  301. package/dist/core/store/settings-validator.js +1 -421
  302. package/dist/core/store/settings.js +3 -1309
  303. package/dist/core/store/tracker-preflight.js +1 -141
  304. package/dist/core/telemetry/consent.js +5 -108
  305. package/dist/core/telemetry/observability.js +1 -246
  306. package/dist/core/telemetry/runtime.js +4 -1988
  307. package/dist/core/test/background-runs.js +4 -945
  308. package/dist/core/test/item-test-run-tracking.js +1 -63
  309. package/dist/core/test/run-selectors.js +1 -120
  310. package/dist/core/validate/estimate-defaults.js +1 -89
  311. package/dist/core/validate/fix-planning.js +1 -189
  312. package/dist/core/validate/linked-artifact-reference.js +1 -36
  313. package/dist/core/validate/missing-by-type.js +1 -34
  314. package/dist/core/validate/missing-link-owners.js +1 -95
  315. package/dist/core/validate/stale-file-classification.js +1 -82
  316. package/dist/mcp/http-server.js +5 -448
  317. package/dist/mcp/legacy-adapter.js +1 -77
  318. package/dist/mcp/runtime-capabilities.js +1 -200
  319. package/dist/mcp/server.js +4 -1233
  320. package/dist/mcp/tool-definitions.js +1 -804
  321. package/dist/sdk/actionability.js +1 -13
  322. package/dist/sdk/agent/closed-domain-contracts.js +1 -161
  323. package/dist/sdk/agent/command-recovery.js +1 -59
  324. package/dist/sdk/agent/command-suggestions.js +1 -66
  325. package/dist/sdk/agent/create-option-policy.js +1 -31
  326. package/dist/sdk/agent/identity-config.js +1 -106
  327. package/dist/sdk/agent/item-addressing.js +1 -207
  328. package/dist/sdk/agent/refusal-closure-census.js +1 -151
  329. package/dist/sdk/agent/refusal-closure.js +1 -133
  330. package/dist/sdk/agent/refusal-corpus-contracts.js +1 -193
  331. package/dist/sdk/agent/refusal-reachability.js +1 -593
  332. package/dist/sdk/agent/subcommand-recovery.js +1 -83
  333. package/dist/sdk/agent/task-transcript-contracts.js +1 -197
  334. package/dist/sdk/agent/tracker-preflight-contracts.js +1 -126
  335. package/dist/sdk/agent-capability-contracts.js +1 -428
  336. package/dist/sdk/agent-session-context.js +1 -9
  337. package/dist/sdk/annotations.js +1 -448
  338. package/dist/sdk/author-attribution.js +1 -468
  339. package/dist/sdk/authoring.js +1 -20
  340. package/dist/sdk/cli-bootstrap.js +1 -838
  341. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +1 -1
  342. package/dist/sdk/cli-contracts/agent-output-contracts.js +4 -476
  343. package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -79
  344. package/dist/sdk/cli-contracts/command-aliases.d.ts +4 -0
  345. package/dist/sdk/cli-contracts/command-aliases.js +1 -107
  346. package/dist/sdk/cli-contracts/command-exit-contracts.js +1 -151
  347. package/dist/sdk/cli-contracts/commander-mutation-options.js +1 -1081
  348. package/dist/sdk/cli-contracts/commander-types.js +1 -159
  349. package/dist/sdk/cli-contracts/completeness.js +1 -393
  350. package/dist/sdk/cli-contracts/enum-contracts.js +1 -222
  351. package/dist/sdk/cli-contracts/flag-contracts.js +1 -1947
  352. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -347
  353. package/dist/sdk/cli-contracts/grammar-contracts.js +1 -986
  354. package/dist/sdk/cli-contracts/registration-helpers.js +7 -990
  355. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +5 -0
  356. package/dist/sdk/cli-contracts/runtime-contracts.js +1 -2090
  357. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +1 -324
  358. package/dist/sdk/cli-contracts/string-lists.js +1 -16
  359. package/dist/sdk/cli-contracts/tool-option-contracts.js +1 -461
  360. package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -2167
  361. package/dist/sdk/cli-contracts/tool-schema.d.ts +3 -1
  362. package/dist/sdk/cli-contracts/tool-schema.js +1 -1664
  363. package/dist/sdk/cli-contracts/typed-action-inputs.js +1 -4
  364. package/dist/sdk/cli-contracts.d.ts +1 -1
  365. package/dist/sdk/cli-contracts.js +1 -17
  366. package/dist/sdk/cli-program.js +1 -49
  367. package/dist/sdk/command-line.d.ts +2 -5
  368. package/dist/sdk/command-line.js +1 -78
  369. package/dist/sdk/comments.js +1 -133
  370. package/dist/sdk/compile-cache.js +1 -73
  371. package/dist/sdk/completion.js +157 -837
  372. package/dist/sdk/compose.js +1 -1090
  373. package/dist/sdk/config.js +1 -1204
  374. package/dist/sdk/context/recency.js +1 -33
  375. package/dist/sdk/context/semantic-session-attribution.js +1 -82
  376. package/dist/sdk/context/usage-egress.js +1 -29
  377. package/dist/sdk/context/usage-ledger.js +3 -185
  378. package/dist/sdk/context-intent-contracts.js +1 -709
  379. package/dist/sdk/context-intent-runtime.js +1 -115
  380. package/dist/sdk/context-packing.js +1 -218
  381. package/dist/sdk/context-relevance.js +1 -503
  382. package/dist/sdk/context-signal-store.js +2 -426
  383. package/dist/sdk/context-usage.js +1 -367
  384. package/dist/sdk/contracts.js +1 -23
  385. package/dist/sdk/core-governance.js +1 -14
  386. package/dist/sdk/core.js +1 -14
  387. package/dist/sdk/corpus-shape.js +1 -401
  388. package/dist/sdk/define.js +1 -220
  389. package/dist/sdk/dependencies.js +1 -718
  390. package/dist/sdk/dependency-flag-validation.js +1 -156
  391. package/dist/sdk/dependency-provenance.js +1 -135
  392. package/dist/sdk/docs.js +1 -14
  393. package/dist/sdk/duplicates.js +1 -40
  394. package/dist/sdk/environment/agent-environment.js +1 -466
  395. package/dist/sdk/environment/host-environment-errors.js +1 -8
  396. package/dist/sdk/environment/project-runtime-compatibility.js +1 -481
  397. package/dist/sdk/environment/source-context.js +1 -28
  398. package/dist/sdk/environment/tracker-preflight.js +1 -9
  399. package/dist/sdk/error-code-catalog.js +1 -153
  400. package/dist/sdk/error-runtime.js +1 -58
  401. package/dist/sdk/errors.js +1 -58
  402. package/dist/sdk/eval.js +1 -190
  403. package/dist/sdk/extension/author-manifest.js +1 -92
  404. package/dist/sdk/extension/bundled-catalog.js +1 -332
  405. package/dist/sdk/extension/concurrency.js +1 -25
  406. package/dist/sdk/extension/copy-scope.d.ts +2 -0
  407. package/dist/sdk/extension/copy-scope.js +2 -0
  408. package/dist/sdk/extension/custom-field-collisions.js +1 -30
  409. package/dist/sdk/extension/describe.js +3 -189
  410. package/dist/sdk/extension/doctor.js +1 -606
  411. package/dist/sdk/extension/install-plan.d.ts +69 -0
  412. package/dist/sdk/extension/install-plan.js +2 -0
  413. package/dist/sdk/extension/install-receipts.d.ts +7 -0
  414. package/dist/sdk/extension/install-receipts.js +2 -0
  415. package/dist/sdk/extension/install-runtime.js +3 -313
  416. package/dist/sdk/extension/install-snapshot.js +1 -66
  417. package/dist/sdk/extension/install-sources.js +5 -1057
  418. package/dist/sdk/extension/managed-state.js +2 -252
  419. package/dist/sdk/extension/migrations.js +2 -174
  420. package/dist/sdk/extension/output-ownership.js +1 -25
  421. package/dist/sdk/extension/runtime-summary.js +1 -18
  422. package/dist/sdk/extension/scaffold.js +4 -2308
  423. package/dist/sdk/extension/shared.js +1 -147
  424. package/dist/sdk/extension/source-resolution.js +1 -65
  425. package/dist/sdk/extension/update-check.js +1 -67
  426. package/dist/sdk/extension-command-context.js +1 -44
  427. package/dist/sdk/extension-contracts.js +1 -73
  428. package/dist/sdk/extension.d.ts +7 -2
  429. package/dist/sdk/extension.js +1 -2196
  430. package/dist/sdk/files.js +1 -535
  431. package/dist/sdk/flag-invocation-contracts.d.ts +0 -5
  432. package/dist/sdk/flag-invocation-contracts.js +1 -344
  433. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -2224
  434. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -2236
  435. package/dist/sdk/generated-error-code-catalog.js +1 -16
  436. package/dist/sdk/governance/assurance-action-contracts.js +1 -4
  437. package/dist/sdk/governance/assurance-action.js +1 -411
  438. package/dist/sdk/governance/assurance-limits.js +1 -10
  439. package/dist/sdk/governance/assurance-mutation-error.js +1 -81
  440. package/dist/sdk/governance/assurance-presets.js +1 -135
  441. package/dist/sdk/governance/assurance-runtime.js +1 -238
  442. package/dist/sdk/governance/assurance.js +4 -1182
  443. package/dist/sdk/governance/boundary-fixtures.js +1 -200
  444. package/dist/sdk/governance/coverage-assurance.js +1 -60
  445. package/dist/sdk/governance/defect-recurrence-coverage.js +1 -188
  446. package/dist/sdk/governance/defect-recurrence-graph.js +1 -120
  447. package/dist/sdk/governance/defect-recurrence-signals.js +1 -24
  448. package/dist/sdk/governance/defect-recurrence.js +1 -623
  449. package/dist/sdk/governance/extension-host-version.js +1 -115
  450. package/dist/sdk/governance/gc.js +1 -368
  451. package/dist/sdk/governance/health.js +1 -2367
  452. package/dist/sdk/governance/hierarchy-validation.js +1 -108
  453. package/dist/sdk/governance/linked-file-report.js +1 -71
  454. package/dist/sdk/governance/merge-receipt-history.js +1 -206
  455. package/dist/sdk/governance/provenance-health.js +1 -282
  456. package/dist/sdk/governance/reindex.js +4 -761
  457. package/dist/sdk/governance/stale-work.js +1 -82
  458. package/dist/sdk/governance/status-role-diagnostics.js +1 -35
  459. package/dist/sdk/governance/storage-integrity.js +1 -280
  460. package/dist/sdk/governance/tracked-runtime-cache.js +1 -106
  461. package/dist/sdk/governance/upgrade.js +2 -327
  462. package/dist/sdk/governance/validate-history-drift.js +1 -81
  463. package/dist/sdk/governance/validate-item-reader.js +1 -17
  464. package/dist/sdk/governance/validate-normalization.js +1 -36
  465. package/dist/sdk/governance/validate.js +1 -2261
  466. package/dist/sdk/governance/workspace-position.js +1 -163
  467. package/dist/sdk/governance.d.ts +1 -0
  468. package/dist/sdk/governance.js +1 -31
  469. package/dist/sdk/graph/adapter.js +1 -181
  470. package/dist/sdk/graph/analytics.js +1 -474
  471. package/dist/sdk/graph/assembly.js +1 -377
  472. package/dist/sdk/graph/cache.js +1 -160
  473. package/dist/sdk/graph/centrality.js +1 -258
  474. package/dist/sdk/graph/conformance.js +1 -130
  475. package/dist/sdk/graph/deadline-scheduling.d.ts +90 -0
  476. package/dist/sdk/graph/deadline-scheduling.js +2 -0
  477. package/dist/sdk/graph/durable-cache.d.ts +1 -1
  478. package/dist/sdk/graph/durable-cache.js +1 -361
  479. package/dist/sdk/graph/governance-contracts.js +1 -34
  480. package/dist/sdk/graph/governance.d.ts +13 -1
  481. package/dist/sdk/graph/governance.js +1 -735
  482. package/dist/sdk/graph/hierarchy-integrity.js +1 -310
  483. package/dist/sdk/graph/index.d.ts +3 -2
  484. package/dist/sdk/graph/index.js +1 -26
  485. package/dist/sdk/graph/lifecycle-coverage.d.ts +32 -0
  486. package/dist/sdk/graph/lifecycle-coverage.js +2 -0
  487. package/dist/sdk/graph/mutation-advisory.js +1 -176
  488. package/dist/sdk/graph/remediation.js +1 -197
  489. package/dist/sdk/graph/run.d.ts +15 -1
  490. package/dist/sdk/graph/run.js +1 -768
  491. package/dist/sdk/graph/scheduling.js +1 -124
  492. package/dist/sdk/graph/traversal.js +1 -284
  493. package/dist/sdk/graph/workspace-relationship-kind-registry.js +1 -27
  494. package/dist/sdk/graph.js +1 -15
  495. package/dist/sdk/guide-topics.js +1 -707
  496. package/dist/sdk/guide.js +1 -279
  497. package/dist/sdk/history/maintenance.d.ts +71 -0
  498. package/dist/sdk/history/maintenance.js +2 -0
  499. package/dist/sdk/history/salvage.js +2 -247
  500. package/dist/sdk/history/subject.d.ts +19 -0
  501. package/dist/sdk/history/subject.js +2 -0
  502. package/dist/sdk/history-analytics.js +1 -282
  503. package/dist/sdk/history-compact.js +1 -527
  504. package/dist/sdk/history-mcp.js +1 -68
  505. package/dist/sdk/history-provenance.js +1 -182
  506. package/dist/sdk/history-read.js +1 -96
  507. package/dist/sdk/history-redact.d.ts +1 -14
  508. package/dist/sdk/history-redact.js +1 -643
  509. package/dist/sdk/history-repair.d.ts +5 -0
  510. package/dist/sdk/history-repair.js +1 -657
  511. package/dist/sdk/improvement-ledger-validation.js +1 -62
  512. package/dist/sdk/improvement-ledger.js +2 -257
  513. package/dist/sdk/index.d.ts +4 -0
  514. package/dist/sdk/index.js +1 -183
  515. package/dist/sdk/init-agent-guidance.js +2 -496
  516. package/dist/sdk/init.js +2 -820
  517. package/dist/sdk/invocation-author.js +1 -39
  518. package/dist/sdk/item-children.js +1 -90
  519. package/dist/sdk/item-metadata-index.js +1 -15
  520. package/dist/sdk/item-schedule.js +1 -27
  521. package/dist/sdk/item-transaction.js +1 -396
  522. package/dist/sdk/learnings.js +1 -29
  523. package/dist/sdk/lifecycle/append.js +2 -53
  524. package/dist/sdk/lifecycle/claim.d.ts +2 -0
  525. package/dist/sdk/lifecycle/claim.js +1 -242
  526. package/dist/sdk/lifecycle/close-many.js +1 -438
  527. package/dist/sdk/lifecycle/close.js +1 -468
  528. package/dist/sdk/lifecycle/copy.js +1 -212
  529. package/dist/sdk/lifecycle/create.js +1 -1949
  530. package/dist/sdk/lifecycle/delete.js +1 -45
  531. package/dist/sdk/lifecycle/event-validation-messages.js +1 -91
  532. package/dist/sdk/lifecycle/focus.js +1 -91
  533. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +1 -24
  534. package/dist/sdk/lifecycle/legacy-none-tokens.js +1 -149
  535. package/dist/sdk/lifecycle/lifecycle-transitions.js +1 -40
  536. package/dist/sdk/lifecycle/mcp-actions.d.ts +2 -0
  537. package/dist/sdk/lifecycle/mcp-actions.js +1 -51
  538. package/dist/sdk/lifecycle/metadata-normalizers.js +1 -46
  539. package/dist/sdk/lifecycle/mutation-command-options.js +1 -9
  540. package/dist/sdk/lifecycle/plan.js +1 -1606
  541. package/dist/sdk/lifecycle/recurrence-parsers.js +1 -147
  542. package/dist/sdk/lifecycle/reopen.js +1 -68
  543. package/dist/sdk/lifecycle/repeatable-metadata-parsers.js +1 -143
  544. package/dist/sdk/lifecycle/restore.d.ts +1 -0
  545. package/dist/sdk/lifecycle/restore.js +1 -330
  546. package/dist/sdk/lifecycle/shared-unset-fields.js +1 -295
  547. package/dist/sdk/lifecycle/update-many.js +1 -861
  548. package/dist/sdk/lifecycle/update.js +1 -2136
  549. package/dist/sdk/lifecycle-completion.js +1 -92
  550. package/dist/sdk/lifecycle-policy.js +1 -120
  551. package/dist/sdk/linked-artifacts.js +1 -530
  552. package/dist/sdk/mcp/apps.js +4 -133
  553. package/dist/sdk/mcp/authorization.js +1 -404
  554. package/dist/sdk/mcp/discovery.js +1 -494
  555. package/dist/sdk/mcp/interactions.js +1 -336
  556. package/dist/sdk/mcp/protocol.d.ts +7 -0
  557. package/dist/sdk/mcp/protocol.js +1 -184
  558. package/dist/sdk/mcp/skills.js +1 -389
  559. package/dist/sdk/mcp/subscriptions.js +1 -211
  560. package/dist/sdk/mcp/tasks.js +2 -430
  561. package/dist/sdk/mcp/transport.js +1 -260
  562. package/dist/sdk/merge/abandoned-receipts.js +1 -46
  563. package/dist/sdk/merge/driver.js +1 -212
  564. package/dist/sdk/merge/index.js +1 -16
  565. package/dist/sdk/merge/install.js +3 -561
  566. package/dist/sdk/merge/receipt-file-boundary.js +1 -46
  567. package/dist/sdk/merge/receipt-operation.js +1 -96
  568. package/dist/sdk/merge/receipt-schema.js +1 -397
  569. package/dist/sdk/merge/receipts.js +4 -525
  570. package/dist/sdk/merge/reconcile.js +1 -352
  571. package/dist/sdk/merge/three-way.js +3 -775
  572. package/dist/sdk/merge.js +1 -10
  573. package/dist/sdk/mutation-events.js +1 -292
  574. package/dist/sdk/mutation-guard.js +1 -216
  575. package/dist/sdk/notes.js +1 -139
  576. package/dist/sdk/output-contracts.js +1 -172
  577. package/dist/sdk/output-projection.js +1 -430
  578. package/dist/sdk/output-token-accounting.js +1 -140
  579. package/dist/sdk/output.js +1 -64
  580. package/dist/sdk/ownership-source.js +1 -21
  581. package/dist/sdk/package-import-adapters.js +1 -442
  582. package/dist/sdk/package-migrations.js +1 -20
  583. package/dist/sdk/package-runtime-options.js +1 -80
  584. package/dist/sdk/pagination.js +1 -162
  585. package/dist/sdk/profile.js +1 -454
  586. package/dist/sdk/provenance.js +1 -258
  587. package/dist/sdk/public-surface.json +1 -1
  588. package/dist/sdk/query/activity.js +1 -401
  589. package/dist/sdk/query/aggregate.d.ts +16 -0
  590. package/dist/sdk/query/aggregate.js +1 -390
  591. package/dist/sdk/query/calendar.js +1 -964
  592. package/dist/sdk/query/complete-list.js +1 -215
  593. package/dist/sdk/query/context.js +1 -1702
  594. package/dist/sdk/query/duplicate-candidates.js +1 -70
  595. package/dist/sdk/query/get.js +1 -450
  596. package/dist/sdk/query/history.js +1 -186
  597. package/dist/sdk/query/item-filter-options.js +1 -9
  598. package/dist/sdk/query/light-metadata.js +1 -18
  599. package/dist/sdk/query/list-filter-shared.js +1 -81
  600. package/dist/sdk/query/list.js +1 -1304
  601. package/dist/sdk/query/multi-value-filters.js +1 -78
  602. package/dist/sdk/query/next.js +1 -513
  603. package/dist/sdk/query/parsers.js +1 -44
  604. package/dist/sdk/query/projection-contracts.js +1 -184
  605. package/dist/sdk/query/search-contracts.js +1 -230
  606. package/dist/sdk/query/search-pagination.js +1 -35
  607. package/dist/sdk/query/search-rendering.js +1 -105
  608. package/dist/sdk/query/search.js +2 -1987
  609. package/dist/sdk/query.js +1 -35
  610. package/dist/sdk/read-output/continuation.js +1 -194
  611. package/dist/sdk/read-output-budget.js +1 -135
  612. package/dist/sdk/read-output-contracts.js +1 -1225
  613. package/dist/sdk/read-output-rows.js +1 -186
  614. package/dist/sdk/read-output-session.js +1 -208
  615. package/dist/sdk/relationship-analytics.js +1 -410
  616. package/dist/sdk/relationship-context.js +1 -284
  617. package/dist/sdk/relationship-history.js +4 -519
  618. package/dist/sdk/relationship-kinds/contract.js +1 -22
  619. package/dist/sdk/relationship-migration.js +1 -77
  620. package/dist/sdk/relationships.js +1 -579
  621. package/dist/sdk/reproducibility/process.js +1 -87
  622. package/dist/sdk/runtime-action-aliases.js +1 -59
  623. package/dist/sdk/runtime-extended-actions.js +1 -177
  624. package/dist/sdk/runtime-input.js +1 -614
  625. package/dist/sdk/runtime-primitives.js +1 -84
  626. package/dist/sdk/runtime-public-contracts.js +1 -4
  627. package/dist/sdk/runtime-stats-options.js +1 -38
  628. package/dist/sdk/runtime.js +1 -1928
  629. package/dist/sdk/scheduling-shortcuts.js +1 -63
  630. package/dist/sdk/schema-migration.js +3 -856
  631. package/dist/sdk/schema.js +1 -1376
  632. package/dist/sdk/search-refresh-worker.js +1 -18
  633. package/dist/sdk/similarity-scoring.js +1 -53
  634. package/dist/sdk/similarity.js +1 -326
  635. package/dist/sdk/start-task-status.js +1 -14
  636. package/dist/sdk/stats.js +1 -270
  637. package/dist/sdk/structured-mutations.js +1 -580
  638. package/dist/sdk/telemetry-flush.js +1 -13
  639. package/dist/sdk/telemetry.js +1 -405
  640. package/dist/sdk/templates.js +2 -292
  641. package/dist/sdk/test/batch.js +2 -509
  642. package/dist/sdk/test/entry.js +1 -70
  643. package/dist/sdk/test/execution.js +6 -2211
  644. package/dist/sdk/test/linked-command-detection.js +1 -546
  645. package/dist/sdk/test/measurements.js +1 -91
  646. package/dist/sdk/test/parsers.js +1 -545
  647. package/dist/sdk/test/runs.js +1 -193
  648. package/dist/sdk/test/trust.js +2 -206
  649. package/dist/sdk/testing.js +4 -1391
  650. package/dist/sdk/traceability/runtime-files-lookup.js +1 -32
  651. package/dist/sdk/traceability/source-traceability.js +1 -292
  652. package/dist/sdk/tracker-root-discovery.js +1 -9
  653. package/dist/sdk/workspace-contracts-cache.js +1 -28
  654. package/dist/sdk/workspace-contracts.js +1 -33
  655. package/dist/sdk/workspace-memory.js +2 -318
  656. package/dist/sdk/workspace-recipe.js +1 -71
  657. package/dist/sdk/workspace-snapshot.js +4 -613
  658. package/dist/sdk/workspace-transaction-gc.js +1 -149
  659. package/dist/sdk/workspace-transaction.js +2 -363
  660. package/dist/sdk/workspace.js +3 -127
  661. package/dist/types/index.js +1 -9
  662. package/dist/types.js +1 -245
  663. package/docs/AGENT_GUIDE.md +1 -1
  664. package/docs/CONTEXT_ALGORITHM_PORTFOLIO.md +167 -0
  665. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +9 -3
  666. package/docs/EXTENSIONS.md +1 -0
  667. package/docs/HISTORY_MAINTENANCE.md +125 -0
  668. package/docs/MCP_2026_07_28_CONFORMANCE.md +20 -10
  669. package/docs/PACKAGE_EVIDENCE.md +92 -0
  670. package/docs/PLANNING_MEASUREMENTS.md +183 -0
  671. package/docs/README.md +3 -0
  672. package/docs/RELEASING.md +1 -0
  673. package/docs/SCRIPTING.md +21 -0
  674. package/docs/TESTING.md +23 -0
  675. package/docs/generated/FLAG_LEXICON_BUDGETS.md +5 -5
  676. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +2 -2
  677. package/marketplace.json +2 -2
  678. package/package.json +1 -1
  679. package/packages/pm-beads/package.json +1 -1
  680. package/packages/pm-calendar/package.json +1 -1
  681. package/packages/pm-command-kit/package.json +1 -1
  682. package/packages/pm-digital-twin/package.json +1 -1
  683. package/packages/pm-governance-audit/package.json +1 -1
  684. package/packages/pm-guide-shell/package.json +1 -1
  685. package/packages/pm-kanban/package.json +1 -1
  686. package/packages/pm-lifecycle-hooks/package.json +1 -1
  687. package/packages/pm-linked-test-adapters/package.json +1 -1
  688. package/packages/pm-search-advanced/package.json +1 -1
  689. package/packages/pm-templates/package.json +1 -1
  690. package/packages/pm-todos/package.json +1 -1
  691. package/packages/pm-vcs/package.json +1 -1
  692. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  693. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  694. package/scripts/finalize-build.mjs +45 -3
  695. package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +0 -9
  696. package/dist/cli-bundle/chunks/chunk-BAX2BYUW.js +0 -2111
  697. package/dist/cli-bundle/chunks/chunk-CKOL5U7J.js +0 -77
  698. package/dist/cli-bundle/chunks/chunk-OF76PSDQ.js +0 -5
  699. package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +0 -232
  700. package/dist/cli-bundle/chunks/chunk-QWRRFYK3.js +0 -2
  701. package/dist/cli-bundle/chunks/chunk-RWWBOWJ5.js +0 -2
  702. package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +0 -40
  703. package/dist/cli-bundle/chunks/chunk-WI2AYPA7.js +0 -2
  704. package/dist/cli-bundle/chunks/register-list-query-5JL52D33.js +0 -11
  705. package/dist/cli-bundle/chunks/register-mutation-ZPG7KMVP.js +0 -20
  706. package/dist/cli-bundle/chunks/register-setup-H4SVAVX7.js +0 -2
  707. package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +0 -2
  708. package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +0 -6
  709. package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +0 -20
  710. package/dist/cli-bundle/focused-chunks/chunk-EZR3GHFE.js +0 -2
  711. package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +0 -56
  712. package/dist/cli-bundle/focused-chunks/chunk-NTPY5GSO.js +0 -3
  713. package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +0 -2
  714. package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +0 -177
  715. package/dist/cli-bundle/focused-chunks/chunk-QTVCLMSZ.js +0 -6
  716. package/dist/cli-bundle/focused-chunks/chunk-QTZRBUYA.js +0 -8
  717. package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +0 -12
  718. package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +0 -10
  719. package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +0 -2
@@ -1,1392 +1,5 @@
1
-
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a09f5e0d-fccc-5a22-a6f3-0a37cbeea3b0")}catch(e){}}();
3
- import { activateExtensions, deactivateExtensions, runAfterCommandHooks, runBeforeCommandHooks, runBeforeMutationHooks, runCommandHandler, runCommandOverride, runOnIndexHooks, runOnReadHooks, runOnWriteHooks, runParserOverride, runPreflightOverride, runRendererOverride, runServiceOverride, } from "../core/extensions/loader.js";
4
- import { createDefaultExtensionGovernancePolicy } from "../core/extensions/extension-types.js";
5
- import { getMigrationRuntimeDefinition, resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, } from "../core/extensions/runtime-registrations.js";
6
- import { collectUsedExtensionCapabilities } from "../core/extensions/capability-usage.js";
7
- import { normalizeKnownExtensionCapability } from "../core/extensions/extension-capability-aliases.js";
8
- import { checkExtensionManifestCompatibility, describeExtensionBlueprint, lintExtensionBlueprint, preflightExtension, } from "./compose.js";
9
- import { describeExtensionActivation } from "../core/extensions/activation-summary.js";
10
- import { lintProjectProfile, } from "../core/profile/profile-lint.js";
11
- import { renderExtensionSurfaceMarkdown } from "../core/extensions/activation-summary-markdown.js";
12
- import { asPropertyRecord, resolveActivatablePropertyRecord, } from "../core/shared/primitives.js";
13
- import { createExtensionCommandSdk } from "./extension-command-context.js";
14
- import { PmClient } from "./runtime.js";
15
- // `describeExtensionActivation` is the `describe` (enumerate-all) verb that
16
- // pairs with the `assert*` (verify-one) and `run*` (invoke-one) helpers below.
17
- // It lives in core (it walks the same registries the loader populates) and is
18
- // surfaced here so package authors get the whole testing surface from the
19
- // `@unbrained/pm-cli/sdk/testing` subpath.
20
- export { describeExtensionActivation, renderExtensionSurfaceMarkdown };
21
- // `composeExtension`'s author-time companions: describe a blueprint's surface map
22
- // and preflight it for capability drift / duplicate / empty-surface footguns
23
- // without activating. Surfaced through the `@unbrained/pm-cli/sdk/testing` subpath
24
- // so a package author gets the full author → describe → preflight → test loop here,
25
- // alongside the assert*/run* helpers; `assertExtensionBlueprint` below is the
26
- // throwing assertion that pairs with the non-throwing `lintExtensionBlueprint`.
27
- export { describeExtensionBlueprint, lintExtensionBlueprint };
28
- function registeredExpectationName(expectation, legacyValue) {
29
- return expectation.name ?? legacyValue ?? "";
30
- }
31
- const HOOK_KIND_TO_REGISTRY_FIELD = {
32
- before_command: "beforeCommand",
33
- before_mutation: "beforeMutation",
34
- after_command: "afterCommand",
35
- on_read: "onRead",
36
- on_write: "onWrite",
37
- on_index: "onIndex",
38
- };
39
- const RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD = {
40
- before_command: "beforeCommand",
41
- after_command: "afterCommand",
42
- on_read: "onRead",
43
- on_write: "onWrite",
44
- on_index: "onIndex",
45
- };
46
- function normalizeSdkIdentifier(value) {
47
- return value.trim().toLowerCase();
48
- }
49
- /**
50
- * Match key for profile-name assertions. Extends {@link normalizeSdkIdentifier}
51
- * with hyphen→underscore folding so the assertion mirrors the runtime profile
52
- * resolution (`pm profile`), which treats `my-flow` and `my_flow` as the same
53
- * archetype. Profiles are the one registration surface that resolves
54
- * hyphen-insensitively (item types resolve by alias, migrations by exact id), so
55
- * — unlike the other `assertRegistered*` helpers — this one folds the separator
56
- * to stay in lockstep with how a profile is actually looked up by name.
57
- */
58
- function normalizeSdkProfileMatchKey(value) {
59
- return normalizeSdkIdentifier(value).replaceAll("-", "_");
60
- }
61
- function extensionNameSuffix(extensionName) {
62
- return extensionName ? ` from extension "${extensionName}"` : "";
63
- }
64
- function sortedUnique(values) {
65
- return [...new Set(values)].sort((left, right) => left.localeCompare(right));
66
- }
67
- function normalizeSdkCommandName(command) {
68
- if (typeof command !== "string") {
69
- // Positional misuse like runRegisteredCommandForTest(activation, "name", opts)
70
- // reaches here with `undefined` at runtime; guide instead of crashing on .trim().
71
- throw new Error('A command name string is required. Pass it via the options object, e.g. runRegisteredCommandForTest(activation, { command: "my command" }) — positional command arguments are not supported.');
72
- }
73
- return command
74
- .trim()
75
- .toLowerCase()
76
- .split(/\s+/)
77
- .filter((part) => part.length > 0)
78
- .join(" ");
79
- }
80
- function formatAvailable(values) {
81
- return values.length > 0 ? values.join(", ") : "(none)";
82
- }
83
- function assertStringSetIncludes(actual, expected, label) {
84
- const actualValues = new Set(actual ?? []);
85
- const missing = sortedUnique(expected.filter((value) => !actualValues.has(value)));
86
- if (missing.length > 0) {
87
- throw new Error(`Expected package manifest ${label} to include ${missing.join(", ")}; available: ${formatAvailable(sortedUnique(actual ?? []))}`);
88
- }
89
- }
90
- function collectFlagLabels(flags) {
91
- const labels = new Set();
92
- for (const flag of flags) {
93
- if (typeof flag.long === "string" && flag.long.trim().length > 0) {
94
- labels.add(flag.long.trim());
95
- }
96
- if (typeof flag.short === "string" && flag.short.trim().length > 0) {
97
- labels.add(flag.short.trim());
98
- }
99
- }
100
- return labels;
101
- }
102
- function readTestExtensionManifest(module) {
103
- const moduleRecord = asPropertyRecord(module);
104
- if (!moduleRecord) {
105
- return {};
106
- }
107
- const manifest = asPropertyRecord(moduleRecord.manifest);
108
- if (manifest) {
109
- return manifest;
110
- }
111
- const defaultExport = asPropertyRecord(moduleRecord.default);
112
- const defaultManifest = asPropertyRecord(defaultExport?.manifest);
113
- if (defaultManifest) {
114
- return defaultManifest;
115
- }
116
- if (defaultExport &&
117
- typeof moduleRecord.default !== "function" &&
118
- ("name" in defaultExport || "capabilities" in defaultExport)) {
119
- return defaultExport;
120
- }
121
- if (typeof module !== "function" &&
122
- ("name" in moduleRecord || "capabilities" in moduleRecord)) {
123
- return moduleRecord;
124
- }
125
- return {};
126
- }
127
- function resolveTestExtensionName(manifest, explicitName) {
128
- return (explicitName ??
129
- (typeof manifest.name === "string" && manifest.name.trim().length > 0
130
- ? manifest.name.trim()
131
- : "test-extension"));
132
- }
133
- function readTestExtensionCapabilities(manifest, options) {
134
- if (Array.isArray(options.capabilities)) {
135
- return [...options.capabilities];
136
- }
137
- if (Array.isArray(manifest.capabilities)) {
138
- return manifest.capabilities.filter((capability) => typeof capability === "string");
139
- }
140
- return [];
141
- }
142
- /**
143
- * Build the synthetic single-extension {@link ExtensionLoadResult} shared by the
144
- * activate/deactivate test helpers, so both stay aligned with the loader's load
145
- * contract as it evolves. The `loaded` entry mirrors the real on-disk shape
146
- * (including `capabilities` and the compatibility fields) while leaving
147
- * filesystem paths empty, since the module is supplied in memory.
148
- */
149
- function buildSingleExtensionLoadResult(module, manifest, identity) {
150
- return {
151
- disabled_by_flag: false,
152
- roots: { global: "", project: "" },
153
- configured_enabled: [],
154
- configured_disabled: [],
155
- discovered: [],
156
- effective: [],
157
- warnings: [],
158
- policy: identity.policy,
159
- failed: [],
160
- loaded: [
161
- {
162
- layer: identity.layer,
163
- directory: "",
164
- manifest_path: "",
165
- name: identity.name,
166
- version: typeof manifest.version === "string" ? manifest.version : "0.0.0",
167
- entry: typeof manifest.entry === "string" ? manifest.entry : "./index.js",
168
- priority: typeof manifest.priority === "number" ? manifest.priority : 0,
169
- entry_path: "",
170
- capabilities: identity.capabilities,
171
- manifest_version: typeof manifest.manifest_version === "number"
172
- ? manifest.manifest_version
173
- : undefined,
174
- pm_min_version: typeof manifest.pm_min_version === "string"
175
- ? manifest.pm_min_version
176
- : undefined,
177
- pm_max_version: typeof manifest.pm_max_version === "string"
178
- ? manifest.pm_max_version
179
- : undefined,
180
- engines: manifest.engines,
181
- trusted: manifest.trusted,
182
- provenance: manifest.provenance,
183
- sandbox_profile: manifest.sandbox_profile,
184
- permissions: manifest.permissions,
185
- activation: manifest.activation,
186
- module,
187
- },
188
- ],
189
- };
190
- }
191
- /**
192
- * Activate one in-memory extension module for package tests.
193
- *
194
- * This uses pm's real registration validation and activation engine while
195
- * avoiding private loader imports, filesystem manifests, or workspace setup.
196
- */
197
- export async function activateExtensionForTest(module, options = {}) {
198
- const manifest = readTestExtensionManifest(module);
199
- return activateExtensions(buildSingleExtensionLoadResult(module, manifest, {
200
- name: resolveTestExtensionName(manifest, options.name),
201
- layer: options.layer ?? "project",
202
- capabilities: readTestExtensionCapabilities(manifest, options),
203
- policy: options.policy ?? createDefaultExtensionGovernancePolicy(),
204
- }));
205
- }
206
- /**
207
- * Deactivate one in-memory extension module for package tests — the teardown
208
- * counterpart to {@link activateExtensionForTest}.
209
- *
210
- * Runs pm's real `deactivateExtensions` engine (including its bounded per-hook
211
- * timeout and best-effort failure capture) over a single synthetic load entry,
212
- * so authors can prove an extension's `deactivate` releases the resources its
213
- * `activate` opened — without importing private loader internals or staging a
214
- * workspace. Resolve `name`/`layer` the same way {@link activateExtensionForTest}
215
- * does so a forwarded `activation` result skips a failed extension correctly.
216
- */
217
- export async function deactivateExtensionForTest(module, options = {}) {
218
- const manifest = readTestExtensionManifest(module);
219
- return deactivateExtensions(buildSingleExtensionLoadResult(module, manifest, {
220
- name: resolveTestExtensionName(manifest, options.name),
221
- layer: options.layer ?? "project",
222
- capabilities: readTestExtensionCapabilities(manifest, {}),
223
- policy: createDefaultExtensionGovernancePolicy(),
224
- }), options.activation, options.deactivateTimeoutMs === undefined
225
- ? {}
226
- : { deactivate_timeout_ms: options.deactivateTimeoutMs });
227
- }
228
- /**
229
- * Invoke a registered extension command handler and return its real
230
- * {@link CommandHandlerResult} so package tests can assert on behavior — not
231
- * just that the command was registered.
232
- *
233
- * This is the "invoke" verb that completes the package-author testing loop:
234
- * `activateExtensionForTest` → `assertRegisteredCommandContract` → **run** →
235
- * `deactivateExtensionForTest`. It runs pm's real handler-dispatch engine
236
- * (`runCommandHandler`) over `activation.commands`, building the
237
- * `CommandHandlerContext` with agent-safe global defaults
238
- * (`{ json: true, quiet: true, noPager: true }`) that callers may override.
239
- *
240
- * Because `registerImporter`/`registerExporter` register their handlers under
241
- * the `"<name> import"` / `"<name> export"` command paths, this same helper
242
- * exercises importer and exporter handlers too.
243
- *
244
- * Throws a descriptive error (listing the available handler command paths) when
245
- * no handler is registered for `command`, since that is a wiring/typo bug in the
246
- * test rather than a behavior under test. When a handler is found, the result is
247
- * returned verbatim: a clean run yields `{ handled: true, result, warnings: [] }`,
248
- * while a handler that throws a non-exit error yields
249
- * `{ handled: false, warnings: [code], errorMessage }` so the failure can be
250
- * asserted. A handler that throws an error carrying a numeric `exitCode`
251
- * propagates the throw, matching runtime semantics.
252
- */
253
- export async function runRegisteredCommandForTest(commands, options) {
254
- const command = normalizeSdkCommandName(options.command);
255
- if (command.length === 0) {
256
- throw new Error("Expected command name must be a non-empty string");
257
- }
258
- const hasHandler = commands.handlers.some((entry) => entry.command === command);
259
- if (!hasHandler) {
260
- const available = sortedUnique(commands.handlers.map((entry) => entry.command));
261
- throw new Error(`Expected a registered command handler for "${command}" to invoke. Available command handlers: ${formatAvailable(available)}`);
262
- }
263
- if (options.sdk !== undefined && options.sdkFactory !== undefined) {
264
- throw new TypeError("runRegisteredCommandForTest accepts sdk or sdkFactory, not both");
265
- }
266
- const pmRoot = options.pmRoot ?? "";
267
- const global = { json: true, quiet: true, noPager: true, ...options.global };
268
- const sdk = options.sdk ??
269
- options.sdkFactory?.({ pmRoot, global }) ??
270
- (pmRoot.length > 0
271
- ? createExtensionCommandSdk(pmRoot, new PmClient({ pmRoot }))
272
- : undefined);
273
- return runCommandHandler(commands, {
274
- command,
275
- args: options.args ? [...options.args] : [],
276
- options: options.options ? { ...options.options } : {},
277
- global,
278
- pm_root: pmRoot,
279
- ...(sdk === undefined ? {} : { sdk }),
280
- });
281
- }
282
- /** Invoke registered transactional mutation guards through the same fail-closed runtime used by real writes. */
283
- export async function runRegisteredMutationGuardForTest(hooks, options) {
284
- if ((hooks.beforeMutation?.length ?? 0) === 0) {
285
- throw new Error("Expected a registered before_mutation hook to invoke");
286
- }
287
- await runBeforeMutationHooks(hooks, options.context);
288
- }
289
- /**
290
- * Fire every registered lifecycle hook of a given `kind` through pm's real hook
291
- * runner and return the warnings array so package tests can assert on behavior —
292
- * not just that a hook was registered.
293
- *
294
- * This is the hook counterpart to {@link runRegisteredCommandForTest}, extending
295
- * the package-author "invoke" verb to the `api.hooks.*` surface. Pass the
296
- * `ExtensionHookRegistry` from `activateExtensionForTest(...).hooks` together with
297
- * the lifecycle `kind` and a synthetic context. Hooks observe their inputs via a
298
- * cloned context snapshot (mutations never leak back to the caller), so the
299
- * observable signal is twofold: any side effects the hook performs, and the
300
- * returned warnings. A clean run returns `[]`; a hook that throws contributes one
301
- * `extension_hook_failed:<layer>:<name>:<hookName>` warning, mirroring runtime
302
- * dispatch which isolates a failing hook without aborting the others.
303
- *
304
- * Throws a descriptive error (listing the hook kinds that do have registrations)
305
- * when no hook of `kind` is registered, since firing a hook the extension never
306
- * registered is a wiring/typo bug in the test rather than a behavior under test.
307
- */
308
- export async function runRegisteredHookForTest(hooks, options) {
309
- const kind = options.kind;
310
- if (kind === "before_mutation") {
311
- throw new Error("Invoke before_mutation hooks with runRegisteredMutationGuardForTest so denials and failures retain fail-closed semantics.");
312
- }
313
- if ((hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[kind]]?.length ?? 0) === 0) {
314
- const populatedKinds = sortedUnique(Object.keys(RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD).filter((candidateKind) => (hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[candidateKind]]
315
- ?.length ?? 0) > 0));
316
- throw new Error(`Expected a registered "${kind}" hook to invoke. Hook kinds with registrations: ${formatAvailable(populatedKinds)}`);
317
- }
318
- switch (options.kind) {
319
- case "before_command":
320
- return runBeforeCommandHooks(hooks, options.context);
321
- case "after_command":
322
- return runAfterCommandHooks(hooks, options.context);
323
- case "on_read":
324
- return runOnReadHooks(hooks, options.context);
325
- case "on_write":
326
- return runOnWriteHooks(hooks, options.context);
327
- case "on_index":
328
- return runOnIndexHooks(hooks, options.context);
329
- }
330
- }
331
- /**
332
- * Invoke a registered parser override through pm's real runner and return the
333
- * resolved {@link ParserOverrideResult}, so package tests can assert the rewritten
334
- * args/options/global an override produces for a command before dispatch.
335
- *
336
- * Guards that a parser override is registered for the (normalized) target command
337
- * before delegating: without a match `runParserOverride` would silently return
338
- * `overridden: false`, hiding a typo'd command name in the test. When a match
339
- * exists the result is returned verbatim — `overridden: true` with the rewritten
340
- * context on success, or `overridden: false` with an
341
- * `extension_parser_override_failed:*` warning when the override throws.
342
- */
343
- export async function runRegisteredParserOverrideForTest(parsers, context) {
344
- const command = normalizeSdkCommandName(context.command);
345
- if (!parsers.overrides.some((entry) => entry.command === command)) {
346
- throw new Error(`Expected a registered parser override for command "${command}" to invoke. Available parser override commands: ${formatAvailable(sortedUnique(parsers.overrides.map((entry) => entry.command)))}`);
347
- }
348
- return runParserOverride(parsers, context);
349
- }
350
- /**
351
- * Invoke the active preflight override through pm's real runner and return the
352
- * resolved {@link PreflightOverrideResult}, so package tests can assert the
353
- * migration/format gate decision an override yields.
354
- *
355
- * Unlike command-scoped overrides, the runtime applies the **last** registered
356
- * preflight override regardless of command, so this helper guards only that at
357
- * least one preflight override is registered before delegating. The result is
358
- * returned verbatim — `overridden: true` with the adjusted decision on success,
359
- * or `overridden: false` with an `extension_preflight_override_failed:*` warning
360
- * when the override throws.
361
- */
362
- export async function runRegisteredPreflightOverrideForTest(preflight, context) {
363
- if (preflight.overrides.length === 0) {
364
- throw new Error("Expected a registered preflight override to invoke, but none are registered.");
365
- }
366
- return runPreflightOverride(preflight, context);
367
- }
368
- /**
369
- * Invoke a registered command-result override through pm's real runner and return
370
- * the resolved {@link CommandOverrideResult}, so package tests can assert how an
371
- * override transforms a command's result payload.
372
- *
373
- * Accepts the same `ExtensionCommandRegistry` exposed as
374
- * `activateExtensionForTest(...).commands` (it carries both handlers and
375
- * overrides). Guards that an override is registered for the (normalized) target
376
- * command before delegating, so a typo'd command name surfaces as a clear error
377
- * rather than a silent `overridden: false`. The result is returned verbatim —
378
- * including the `extension_command_override_async_unsupported:*` warning the
379
- * runtime emits when an override returns a Promise (command overrides are
380
- * synchronous), or an `extension_command_override_failed:*` warning on throw.
381
- */
382
- export async function runRegisteredCommandOverrideForTest(commands, context) {
383
- const command = normalizeSdkCommandName(context.command);
384
- if (!commands.overrides.some((entry) => entry.command === command)) {
385
- throw new Error(`Expected a registered command override for command "${command}" to invoke. Available command override commands: ${formatAvailable(sortedUnique(commands.overrides.map((entry) => entry.command)))}`);
386
- }
387
- return runCommandOverride(commands, context);
388
- }
389
- /**
390
- * Invoke a registered renderer override through pm's real runner and return the
391
- * resolved {@link RendererOverrideResult}, so package tests can assert the custom
392
- * string an override renders for a given output `format`.
393
- *
394
- * Guards that a renderer override is registered for `context.format` before
395
- * delegating, so a wrong format surfaces as a clear error rather than a silent
396
- * `overridden: false`. The result is returned verbatim — `overridden: true` with
397
- * the rendered string on success, or `overridden: false` (with an
398
- * `extension_renderer_invalid_result:*` / `extension_renderer_failed:*` warning
399
- * where applicable) when the override returns a non-string or throws.
400
- */
401
- export async function runRegisteredRendererOverrideForTest(renderers, context) {
402
- if (!renderers.overrides.some((entry) => entry.format === context.format)) {
403
- throw new Error(`Expected a registered renderer override for format "${context.format}" to invoke. Available renderer override formats: ${formatAvailable(sortedUnique(renderers.overrides.map((entry) => entry.format)))}`);
404
- }
405
- return runRendererOverride(renderers, context);
406
- }
407
- /**
408
- * Invoke a registered service override through pm's real runner and return the
409
- * resolved {@link ServiceOverrideResult}, so package tests can assert how an
410
- * override handles an internal service payload (e.g. `output_format`).
411
- *
412
- * Guards that a service override is registered for `context.service` before
413
- * delegating, so a wrong service name surfaces as a clear error rather than a
414
- * silent `handled: false`. The result is returned verbatim — `handled: true` with
415
- * the override's result when it claims the payload, or `handled: false` with the
416
- * original payload (and any `extension_service_override_*` warnings) otherwise.
417
- */
418
- export async function runRegisteredServiceOverrideForTest(services, context) {
419
- if (!services.overrides.some((entry) => entry.service === context.service)) {
420
- throw new Error(`Expected a registered service override for service "${context.service}" to invoke. Available service override services: ${formatAvailable(sortedUnique(services.overrides.map((entry) => entry.service)))}`);
421
- }
422
- return runServiceOverride(services, context);
423
- }
424
- /** Maps each search-provider operation to the runtime-definition keys that may hold its function, in priority order. The host accepts both camelCase and snake_case spellings for the multi-word operations, so this helper mirrors that by trying `embedBatch` before `embed_batch` and `queryExpansion` before `query_expansion`. */
425
- const SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS = {
426
- query: ["query"],
427
- embed: ["embed"],
428
- embedBatch: ["embedBatch", "embed_batch"],
429
- queryExpansion: ["queryExpansion", "query_expansion"],
430
- rerank: ["rerank"],
431
- };
432
- /**
433
- * Invoke an operation of a registered search provider through the same name
434
- * resolution the host uses and return the operation's result, so package tests
435
- * can assert a custom provider's runtime behavior — not just that it registered.
436
- *
437
- * This is the search-provider counterpart to {@link runRegisteredCommandForTest},
438
- * extending the package-author "invoke" verb to the `api.registerSearchProvider`
439
- * surface. Pass the `ExtensionRegistrationRegistry` from
440
- * `activateExtensionForTest(...).registrations`, the registered provider name, the
441
- * `operation` to exercise (`query`, `embed`, `embedBatch`, `queryExpansion`, or
442
- * `rerank`), and a synthetic context. The provider is resolved via
443
- * {@link resolveRegisteredSearchProvider} (case-insensitive, last registration
444
- * wins) and the operation is invoked on its `runtime_definition` — the clone that
445
- * preserves live functions, matching what the runtime dispatches.
446
- *
447
- * Throws a descriptive error listing the available providers when the name is not
448
- * registered, and a separate error when the resolved provider does not implement
449
- * the requested operation, since invoking an absent provider/operation is a
450
- * wiring bug in the test rather than a behavior under test.
451
- */
452
- export async function runRegisteredSearchProviderForTest(registrations, options) {
453
- const registration = resolveRegisteredSearchProvider(registrations, options.provider);
454
- if (!registration) {
455
- const available = sortedUnique(registrations.search_providers.map((entry) => entry.definition.name));
456
- throw new Error(`Expected a registered search provider "${options.provider}" to invoke. Available search providers: ${formatAvailable(available)}`);
457
- }
458
- const runtimeDefinition = registration.runtime_definition;
459
- const operationFn = SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS[options.operation]
460
- .map((key) => runtimeDefinition[key])
461
- .find((value) => typeof value === "function");
462
- if (typeof operationFn !== "function") {
463
- throw new Error(`Registered search provider "${options.provider}" does not implement the "${options.operation}" operation.`);
464
- }
465
- return (await operationFn(options.context));
466
- }
467
- /**
468
- * Invoke an operation of a registered vector store adapter through the same name
469
- * resolution the host uses and return the operation's result, so package tests
470
- * can assert a custom adapter's runtime behavior — not just that it registered.
471
- *
472
- * This is the vector-store counterpart to {@link runRegisteredSearchProviderForTest}.
473
- * Pass the `ExtensionRegistrationRegistry` from
474
- * `activateExtensionForTest(...).registrations`, the registered adapter name, the
475
- * `operation` to exercise (`query`, `upsert`, or `delete`), and a synthetic
476
- * context. The adapter is resolved via {@link resolveRegisteredVectorStoreAdapter}
477
- * (case-insensitive, last registration wins) and the operation is invoked on its
478
- * `runtime_definition` — the clone that preserves live functions, matching what
479
- * the runtime dispatches.
480
- *
481
- * Throws a descriptive error listing the available adapters when the name is not
482
- * registered, and a separate error when the resolved adapter does not implement
483
- * the requested operation.
484
- */
485
- export async function runRegisteredVectorStoreAdapterForTest(registrations, options) {
486
- const registration = resolveRegisteredVectorStoreAdapter(registrations, options.adapter);
487
- if (!registration) {
488
- const available = sortedUnique(registrations.vector_store_adapters.map((entry) => entry.definition.name));
489
- throw new Error(`Expected a registered vector store adapter "${options.adapter}" to invoke. Available vector store adapters: ${formatAvailable(available)}`);
490
- }
491
- const operationFn = registration.runtime_definition[options.operation];
492
- if (typeof operationFn !== "function") {
493
- throw new Error(`Registered vector store adapter "${options.adapter}" does not implement the "${options.operation}" operation.`);
494
- }
495
- return (await operationFn(options.context));
496
- }
497
- /**
498
- * Invoke a registered schema migration's `run` function through the same runtime
499
- * definition the host executes and return its result, so package tests can assert
500
- * a migration's behavior — not just that it registered.
501
- *
502
- * This completes the package-author "invoke" verb across every executable
503
- * registration surface. The migration is resolved by id via
504
- * {@link assertRegisteredMigration} (which throws a descriptive "available
505
- * migrations" error when absent), then `run` is invoked on its
506
- * `runtime_definition` with a context mirroring the one
507
- * `executeRegisteredRuntimeMigrations` builds at runtime: the resolved id,
508
- * `command: "migration"`, the registering extension's layer/name, the supplied
509
- * `pmRoot`, and the migration's normalized status. Unlike the runtime — which
510
- * skips already-applied migrations and folds a throw into a warning — this helper
511
- * always invokes `run` and lets a throw propagate, so authors can assert both the
512
- * success result and failure via rejection.
513
- *
514
- * Throws when the resolved migration declares no `run` function, since invoking a
515
- * runless migration is a wiring bug in the test rather than a behavior under test.
516
- */
517
- export async function runRegisteredMigrationForTest(registrations, options) {
518
- const migration = assertRegisteredMigration(registrations, {
519
- migration: options.migration,
520
- extensionName: options.extensionName,
521
- });
522
- const run = getMigrationRuntimeDefinition(migration).run;
523
- if (typeof run !== "function") {
524
- throw new Error(`Registered migration "${options.migration}" does not implement a run function to invoke.`);
525
- }
526
- const declaredStatus = migration.definition.status;
527
- const context = {
528
- id: migration.definition.id,
529
- command: "migration",
530
- layer: migration.layer,
531
- extension: migration.name,
532
- pm_root: options.pmRoot ?? "",
533
- status: (typeof declaredStatus === "string"
534
- ? declaredStatus.trim().toLowerCase()
535
- : "") || "pending",
536
- };
537
- return await run(context);
538
- }
539
- /**
540
- * Invoke a registered extension importer through pm's real handler-dispatch
541
- * engine and return its {@link CommandHandlerResult}, so package tests can assert
542
- * an importer's behavior — not just that it registered.
543
- *
544
- * This is the importer counterpart to {@link runRegisteredCommandForTest},
545
- * extending the package-author "invoke" verb to the `api.registerImporter`
546
- * surface. Because `registerImporter(name, fn)` wraps `fn` into a command handler
547
- * at the `"<name> import"` path, invoking it through `runRegisteredCommandForTest`
548
- * requires the author to know that naming convention and to remember that an
549
- * importer is reachable as a command at all. This helper closes both gaps: it
550
- * accepts the registered importer name directly, validates via
551
- * {@link assertRegisteredImporter} that it is genuinely a registered importer
552
- * (not merely some command parked at that path), derives the command path, and
553
- * dispatches through the same engine.
554
- *
555
- * The full activation result is required because importer execution spans two
556
- * sub-registries: `registrations.importers` proves the importer exists, while
557
- * `commands` holds the wrapped handler. The result is returned verbatim from the
558
- * command engine: a clean run yields `{ handled: true, result, warnings: [] }`
559
- * (where `result` is the importer's return value), a non-exit throw yields
560
- * `{ handled: false, warnings: [code], errorMessage }`, and an error carrying a
561
- * numeric `exitCode` propagates — matching runtime import semantics.
562
- *
563
- * Throws a descriptive "available importers" error (via `assertRegisteredImporter`)
564
- * when no importer matches, since that is a wiring/typo bug in the test rather
565
- * than a behavior under test.
566
- */
567
- export async function runRegisteredImporterForTest(activation, options) {
568
- const importer = assertRegisteredImporter(activation.registrations, {
569
- importer: options.importer,
570
- extensionName: options.extensionName,
571
- });
572
- return runRegisteredCommandForTest(activation.commands, {
573
- command: `${importer.importer} import`,
574
- args: options.args,
575
- options: options.options,
576
- global: options.global,
577
- pmRoot: options.pmRoot,
578
- });
579
- }
580
- /**
581
- * Invoke a registered extension exporter through pm's real handler-dispatch
582
- * engine and return its {@link CommandHandlerResult}, so package tests can assert
583
- * an exporter's behavior — not just that it registered.
584
- *
585
- * This is the exporter counterpart to {@link runRegisteredImporterForTest} and
586
- * the final surface in the package-author "invoke" verb. Because
587
- * `registerExporter(name, fn)` wraps `fn` into a command handler at the
588
- * `"<name> export"` path, this helper accepts the registered exporter name
589
- * directly, validates via {@link assertRegisteredExporter} that it is genuinely a
590
- * registered exporter, derives the command path, and dispatches through the same
591
- * engine. See {@link runRegisteredImporterForTest} for the full activation
592
- * rationale and return semantics (which apply identically here).
593
- *
594
- * Throws a descriptive "available exporters" error (via `assertRegisteredExporter`)
595
- * when no exporter matches, since that is a wiring/typo bug in the test rather
596
- * than a behavior under test.
597
- */
598
- export async function runRegisteredExporterForTest(activation, options) {
599
- const exporter = assertRegisteredExporter(activation.registrations, {
600
- exporter: options.exporter,
601
- extensionName: options.extensionName,
602
- });
603
- return runRegisteredCommandForTest(activation.commands, {
604
- command: `${exporter.exporter} export`,
605
- args: options.args,
606
- options: options.options,
607
- global: options.global,
608
- pmRoot: options.pmRoot,
609
- });
610
- }
611
- /**
612
- * Assert that an {@link ExtensionDeactivationResult} reports the expected clean
613
- * teardown counts, throwing a descriptive error otherwise. Defaults assert the
614
- * single-extension happy path — exactly one extension deactivated and none
615
- * failed. Returns the result so assertions can chain.
616
- */
617
- export function assertExtensionDeactivated(result, expectation = {}) {
618
- const expectedDeactivated = expectation.deactivated ?? 1;
619
- if (result.deactivated !== expectedDeactivated) {
620
- throw new Error(`Expected ${expectedDeactivated} extension${expectedDeactivated === 1 ? "" : "s"} to deactivate cleanly, ` +
621
- `but ${result.deactivated} did.`);
622
- }
623
- const expectedFailed = expectation.failed ?? 0;
624
- if (result.failed.length !== expectedFailed) {
625
- const detail = result.failed
626
- .map((failure) => `${failure.layer}:${failure.name} (${failure.error})`)
627
- .join(", ");
628
- throw new Error(`Expected ${expectedFailed} teardown failure${expectedFailed === 1 ? "" : "s"}, ` +
629
- `but observed ${result.failed.length}${detail.length > 0 ? `: ${detail}` : ""}.`);
630
- }
631
- return result;
632
- }
633
- /** Assert that a normalized package manifest advertises expected package resources. Pair with `readPmPackageManifest(packageRoot)` in package tests. */
634
- export function assertPackageManifest(manifest, expectation) {
635
- if (expectation.packageName !== undefined &&
636
- manifest.package_name !== expectation.packageName) {
637
- throw new Error(`Expected package manifest package_name to be "${expectation.packageName}"; received "${manifest.package_name ?? "(none)"}"`);
638
- }
639
- if (expectation.aliases !== undefined) {
640
- assertStringSetIncludes(manifest.aliases, expectation.aliases, "aliases");
641
- }
642
- if (expectation.resources !== undefined) {
643
- for (const [kind, expectedPaths] of Object.entries(expectation.resources)) {
644
- assertStringSetIncludes(manifest.resources[kind], expectedPaths, `pm.${kind}`);
645
- }
646
- }
647
- return manifest;
648
- }
649
- /** Assert that an activated extension registration registry contains a command contract with the expected public metadata. */
650
- export function assertRegisteredCommandContract(registrations, expectation) {
651
- const expectedCommand = normalizeSdkCommandName(registeredExpectationName(expectation, expectation.command));
652
- if (expectedCommand.length === 0) {
653
- throw new Error("Expected command name must be a non-empty string");
654
- }
655
- const commandCandidates = registrations.commands.filter((entry) => entry.command === expectedCommand);
656
- const command = expectation.extensionName
657
- ? commandCandidates.find((entry) => entry.name === expectation.extensionName)
658
- : commandCandidates[0];
659
- if (!command) {
660
- const available = registrations.commands
661
- .map((entry) => entry.command)
662
- .sort((left, right) => left.localeCompare(right));
663
- const extensionSuffix = expectation.extensionName
664
- ? ` from extension "${expectation.extensionName}"`
665
- : "";
666
- throw new Error(`Expected extension command "${expectedCommand}"${extensionSuffix} to be registered. Available commands: ${formatAvailable(available)}`);
667
- }
668
- if (expectation.action !== undefined &&
669
- command.action !== expectation.action) {
670
- throw new Error(`Expected extension command "${expectedCommand}" action "${expectation.action}", received "${command.action}"`);
671
- }
672
- if (expectation.arguments !== undefined) {
673
- const actualArguments = (command.arguments ?? []).map((argument) => argument.name);
674
- const missingArguments = expectation.arguments.filter((argument) => !actualArguments.includes(argument));
675
- if (missingArguments.length > 0) {
676
- throw new Error(`Expected extension command "${expectedCommand}" arguments ${formatAvailable(expectation.arguments)}; missing ${formatAvailable(missingArguments)}; available ${formatAvailable(actualArguments)}`);
677
- }
678
- }
679
- const flags = registrations.flags
680
- .filter((entry) => entry.target_command === expectedCommand &&
681
- (expectation.extensionName === undefined ||
682
- entry.name === expectation.extensionName))
683
- .flatMap((entry) => entry.flags);
684
- if (expectation.flags !== undefined) {
685
- const actualFlagLabels = collectFlagLabels(flags);
686
- const missingFlags = expectation.flags.filter((flag) => !actualFlagLabels.has(flag));
687
- if (missingFlags.length > 0) {
688
- throw new Error(`Expected extension command "${expectedCommand}" flags ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left, right) => left.localeCompare(right)))}`);
689
- }
690
- }
691
- return { command, flags };
692
- }
693
- /** Assert that an activated extension registration registry contains flags injected into an existing command through `api.registerFlags(...)`. */
694
- export function assertRegisteredFlags(registrations, expectation) {
695
- const expectedCommand = normalizeSdkCommandName(registeredExpectationName(expectation, expectation.targetCommand));
696
- if (expectedCommand.length === 0) {
697
- throw new Error("Expected target command name must be a non-empty string");
698
- }
699
- const candidates = registrations.flags.filter((entry) => entry.target_command === expectedCommand);
700
- let registration;
701
- if (expectation.extensionName) {
702
- registration = candidates.find((entry) => entry.name === expectation.extensionName);
703
- }
704
- else if (candidates.length === 1) {
705
- registration = candidates[0];
706
- }
707
- else if (candidates.length > 1) {
708
- const availableExtensions = sortedUnique(candidates.map((entry) => entry.name));
709
- throw new Error(`Expected flags for target command "${expectedCommand}" matched multiple extensions: ${formatAvailable(availableExtensions)}. Specify extensionName to choose one registration.`);
710
- }
711
- if (!registration) {
712
- const available = sortedUnique(registrations.flags.map((entry) => entry.target_command));
713
- const availableExtensions = sortedUnique(candidates.map((entry) => entry.name));
714
- throw new Error(`Expected flags for target command "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available flag target commands: ${formatAvailable(available)}; matching extensions: ${formatAvailable(availableExtensions)}`);
715
- }
716
- if (expectation.flags !== undefined) {
717
- const actualFlagLabels = collectFlagLabels(registration.flags);
718
- const missingFlags = expectation.flags.filter((flag) => !actualFlagLabels.has(flag));
719
- if (missingFlags.length > 0) {
720
- throw new Error(`Expected flags for target command "${expectedCommand}" to include ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left, right) => left.localeCompare(right)))}`);
721
- }
722
- }
723
- return registration;
724
- }
725
- /**
726
- * Assert that an activated extension hook registry contains a hook of the
727
- * expected lifecycle kind (optionally scoped to a specific extension).
728
- *
729
- * Hooks are surfaced via `ExtensionActivationResult.hooks`, not the command
730
- * registration registry, so this helper accepts an `ExtensionHookRegistry`.
731
- */
732
- export function assertRegisteredHook(hooks, expectation) {
733
- const field = HOOK_KIND_TO_REGISTRY_FIELD[expectation.kind];
734
- const candidates = (hooks[field] ?? []);
735
- const hook = expectation.extensionName
736
- ? candidates.find((entry) => entry.name === expectation.extensionName)
737
- : candidates[0];
738
- if (!hook) {
739
- const available = sortedUnique(candidates.map((entry) => entry.name));
740
- throw new Error(`Expected a "${expectation.kind}" hook${extensionNameSuffix(expectation.extensionName)} to be registered. Available "${expectation.kind}" hooks: ${formatAvailable(available)}`);
741
- }
742
- return hook;
743
- }
744
- /** Assert that an activated extension registration registry contains a search provider with the expected name (optionally scoped to a specific extension). */
745
- export function assertRegisteredSearchProvider(registrations, expectation) {
746
- const expectedProvider = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.provider));
747
- if (expectedProvider.length === 0) {
748
- throw new Error("Expected search provider name must be a non-empty string");
749
- }
750
- const candidates = registrations.search_providers.filter((entry) => normalizeSdkIdentifier(entry.definition.name) === expectedProvider);
751
- const provider = expectation.extensionName
752
- ? candidates.find((entry) => entry.name === expectation.extensionName)
753
- : candidates[0];
754
- if (!provider) {
755
- const available = sortedUnique(registrations.search_providers.map((entry) => entry.definition.name));
756
- throw new Error(`Expected search provider "${expectedProvider}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available search providers: ${formatAvailable(available)}`);
757
- }
758
- return provider;
759
- }
760
- /** Assert that an activated extension registration registry contains a vector store adapter with the expected name (optionally scoped to a specific extension). */
761
- export function assertRegisteredVectorStoreAdapter(registrations, expectation) {
762
- const expectedAdapter = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.adapter));
763
- if (expectedAdapter.length === 0) {
764
- throw new Error("Expected vector store adapter name must be a non-empty string");
765
- }
766
- const candidates = registrations.vector_store_adapters.filter((entry) => normalizeSdkIdentifier(entry.definition.name) === expectedAdapter);
767
- const adapter = expectation.extensionName
768
- ? candidates.find((entry) => entry.name === expectation.extensionName)
769
- : candidates[0];
770
- if (!adapter) {
771
- const available = sortedUnique(registrations.vector_store_adapters.map((entry) => entry.definition.name));
772
- throw new Error(`Expected vector store adapter "${expectedAdapter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available vector store adapters: ${formatAvailable(available)}`);
773
- }
774
- return adapter;
775
- }
776
- /** Assert that an activated extension registration registry contains an importer for the expected format/name (optionally scoped to a specific extension). */
777
- export function assertRegisteredImporter(registrations, expectation) {
778
- const expectedImporter = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.importer));
779
- if (expectedImporter.length === 0) {
780
- throw new Error("Expected importer name must be a non-empty string");
781
- }
782
- const candidates = registrations.importers.filter((entry) => normalizeSdkIdentifier(entry.importer) === expectedImporter);
783
- const importer = expectation.extensionName
784
- ? candidates.find((entry) => entry.name === expectation.extensionName)
785
- : candidates[0];
786
- if (!importer) {
787
- const available = sortedUnique(registrations.importers.map((entry) => entry.importer));
788
- throw new Error(`Expected importer "${expectedImporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available importers: ${formatAvailable(available)}`);
789
- }
790
- return importer;
791
- }
792
- /** Assert that an activated extension registration registry contains an exporter for the expected format/name (optionally scoped to a specific extension). */
793
- export function assertRegisteredExporter(registrations, expectation) {
794
- const expectedExporter = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.exporter));
795
- if (expectedExporter.length === 0) {
796
- throw new Error("Expected exporter name must be a non-empty string");
797
- }
798
- const candidates = registrations.exporters.filter((entry) => normalizeSdkIdentifier(entry.exporter) === expectedExporter);
799
- const exporter = expectation.extensionName
800
- ? candidates.find((entry) => entry.name === expectation.extensionName)
801
- : candidates[0];
802
- if (!exporter) {
803
- const available = sortedUnique(registrations.exporters.map((entry) => entry.exporter));
804
- throw new Error(`Expected exporter "${expectedExporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available exporters: ${formatAvailable(available)}`);
805
- }
806
- return exporter;
807
- }
808
- /** Assert that an activated extension registration registry contains a custom item field definition (optionally scoped to a specific extension). */
809
- export function assertRegisteredItemField(registrations, expectation) {
810
- const expectedField = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.field));
811
- if (expectedField.length === 0) {
812
- throw new Error("Expected item field name must be a non-empty string");
813
- }
814
- const candidates = registrations.item_fields
815
- .filter((entry) => expectation.extensionName === undefined ||
816
- entry.name === expectation.extensionName)
817
- .map((registration) => ({
818
- registration,
819
- field: registration.fields.find((field) => normalizeSdkIdentifier(field.name) === expectedField),
820
- }))
821
- .filter((entry) => entry.field !== undefined);
822
- const match = candidates.find((entry) => expectation.type === undefined || entry.field.type === expectation.type);
823
- if (!match) {
824
- const available = sortedUnique(registrations.item_fields.flatMap((entry) => entry.fields.map((field) => `${field.name}:${field.type}`)));
825
- throw new Error(`Expected item field "${expectedField}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item fields: ${formatAvailable(available)}`);
826
- }
827
- return match;
828
- }
829
- /** Assert that an activated extension registration registry contains a custom item type definition (optionally scoped to a specific extension). */
830
- export function assertRegisteredItemType(registrations, expectation) {
831
- const expectedType = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.itemType));
832
- if (expectedType.length === 0) {
833
- throw new Error("Expected item type name must be a non-empty string");
834
- }
835
- const candidates = registrations.item_types
836
- .filter((entry) => expectation.extensionName === undefined ||
837
- entry.name === expectation.extensionName)
838
- .map((registration) => ({
839
- registration,
840
- itemType: registration.types.find((itemType) => normalizeSdkIdentifier(itemType.name) === expectedType),
841
- }))
842
- .filter((entry) => entry.itemType !== undefined);
843
- const match = candidates.find((entry) => expectation.folder === undefined ||
844
- entry.itemType.folder === expectation.folder);
845
- if (!match) {
846
- const available = sortedUnique(registrations.item_types.flatMap((entry) => entry.types.map((itemType) => `${itemType.name}:${itemType.folder}`)));
847
- throw new Error(`Expected item type "${expectedType}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item types: ${formatAvailable(available)}`);
848
- }
849
- return match;
850
- }
851
- /**
852
- * Assert that an activated extension registration registry contains a project
853
- * profile registered via `api.registerProfile(profile)` (optionally scoped to a
854
- * specific extension).
855
- *
856
- * This is the test-time counterpart to the declarative `defineProjectProfile`
857
- * authoring helper and the `pm profile list/show/apply` runtime surface: it
858
- * proves a package's archetype reached the registry so a downstream consumer can
859
- * resolve and apply it by name. Like the other declarative-surface assertions
860
- * (item types, item fields), a profile is verified by presence — it is staged,
861
- * not executed — so there is no `runRegisteredProfileForTest` counterpart.
862
- */
863
- export function assertRegisteredProfile(registrations, expectation) {
864
- const expectedProfile = normalizeSdkProfileMatchKey(registeredExpectationName(expectation, expectation.profile));
865
- if (expectedProfile.length === 0) {
866
- throw new Error("Expected profile name must be a non-empty string");
867
- }
868
- const match = registrations.profiles
869
- .filter((entry) => expectation.extensionName === undefined ||
870
- entry.name === expectation.extensionName)
871
- .find((entry) => normalizeSdkProfileMatchKey(entry.profile.name) === expectedProfile);
872
- if (!match) {
873
- const available = sortedUnique(registrations.profiles.map((entry) => entry.profile.name));
874
- throw new Error(`Expected profile "${expectedProfile}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available profiles: ${formatAvailable(available)}`);
875
- }
876
- return { registration: match, profile: match.profile };
877
- }
878
- /**
879
- * Assert that an activated extension command registry contains a command
880
- * override registered via `api.registerCommand(command, override)` (optionally
881
- * scoped to a specific extension).
882
- *
883
- * Command overrides are surfaced via `ExtensionActivationResult.commands`, not
884
- * the command registration registry, so this helper accepts an
885
- * `ExtensionCommandRegistry`.
886
- */
887
- export function assertRegisteredCommandOverride(commands, expectation) {
888
- const expectedCommand = normalizeSdkCommandName(registeredExpectationName(expectation, expectation.command));
889
- if (expectedCommand.length === 0) {
890
- throw new Error("Expected command name must be a non-empty string");
891
- }
892
- const candidates = commands.overrides.filter((entry) => entry.command === expectedCommand);
893
- const override = expectation.extensionName
894
- ? candidates.find((entry) => entry.name === expectation.extensionName)
895
- : candidates[0];
896
- if (!override) {
897
- const available = sortedUnique(commands.overrides.map((entry) => entry.command));
898
- throw new Error(`Expected command override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available command overrides: ${formatAvailable(available)}`);
899
- }
900
- return override;
901
- }
902
- /**
903
- * Assert that an activated extension parser registry contains a parser override
904
- * registered via `api.registerParser(command, override)` (optionally scoped to
905
- * a specific extension).
906
- *
907
- * Parser overrides are surfaced via `ExtensionActivationResult.parsers`, not the
908
- * command registration registry, so this helper accepts an
909
- * `ExtensionParserRegistry`.
910
- */
911
- export function assertRegisteredParserOverride(parsers, expectation) {
912
- const expectedCommand = normalizeSdkCommandName(registeredExpectationName(expectation, expectation.command));
913
- if (expectedCommand.length === 0) {
914
- throw new Error("Expected command name must be a non-empty string");
915
- }
916
- const candidates = parsers.overrides.filter((entry) => entry.command === expectedCommand);
917
- const override = expectation.extensionName
918
- ? candidates.find((entry) => entry.name === expectation.extensionName)
919
- : candidates[0];
920
- if (!override) {
921
- const available = sortedUnique(parsers.overrides.map((entry) => entry.command));
922
- throw new Error(`Expected parser override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available parser overrides: ${formatAvailable(available)}`);
923
- }
924
- return override;
925
- }
926
- /**
927
- * Assert that an activated extension preflight registry contains a preflight
928
- * override registered via `api.registerPreflight(override)` (optionally scoped
929
- * to a specific extension).
930
- *
931
- * Preflight overrides are global (they run for every command and carry no
932
- * `command` field), so the expectation only accepts an optional
933
- * `extensionName`. Preflight overrides are surfaced via
934
- * `ExtensionActivationResult.preflight`.
935
- */
936
- export function assertRegisteredPreflightOverride(preflight, expectation = {}) {
937
- const override = expectation.extensionName
938
- ? preflight.overrides.find((entry) => entry.name === expectation.extensionName)
939
- : preflight.overrides[0];
940
- if (!override) {
941
- const available = sortedUnique(preflight.overrides.map((entry) => entry.name));
942
- throw new Error(`Expected a preflight override${extensionNameSuffix(expectation.extensionName)} to be registered. Available preflight overrides: ${formatAvailable(available)}`);
943
- }
944
- return override;
945
- }
946
- /**
947
- * Assert that an activated extension renderer registry contains a renderer
948
- * override registered via `api.registerRenderer(format, renderer)` for the
949
- * expected output format (optionally scoped to a specific extension).
950
- *
951
- * Renderer overrides are surfaced via `ExtensionActivationResult.renderers`.
952
- */
953
- export function assertRegisteredRendererOverride(renderers, expectation) {
954
- const expectedFormat = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.format));
955
- if (expectedFormat.length === 0) {
956
- throw new Error("Expected renderer format must be a non-empty string");
957
- }
958
- const candidates = renderers.overrides.filter((entry) => entry.format === expectedFormat);
959
- const override = expectation.extensionName
960
- ? candidates.find((entry) => entry.name === expectation.extensionName)
961
- : candidates[0];
962
- if (!override) {
963
- const available = sortedUnique(renderers.overrides.map((entry) => entry.format));
964
- throw new Error(`Expected renderer override "${expectedFormat}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available renderer overrides: ${formatAvailable(available)}`);
965
- }
966
- return override;
967
- }
968
- /**
969
- * Assert that an activated extension service registry contains a service override
970
- * registered via `api.registerService(service, override)` for the expected
971
- * service name (optionally scoped to a specific extension).
972
- *
973
- * Service overrides are surfaced via `ExtensionActivationResult.services`, not the
974
- * command registration registry, so this helper accepts an
975
- * `ExtensionServiceRegistry`.
976
- */
977
- export function assertRegisteredServiceOverride(services, expectation) {
978
- const expectedService = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.service));
979
- if (expectedService.length === 0) {
980
- throw new Error("Expected service name must be a non-empty string");
981
- }
982
- const candidates = services.overrides.filter((entry) => normalizeSdkIdentifier(entry.service) === expectedService);
983
- const override = expectation.extensionName
984
- ? candidates.find((entry) => entry.name === expectation.extensionName)
985
- : candidates[0];
986
- if (!override) {
987
- const available = sortedUnique(services.overrides.map((entry) => entry.service));
988
- throw new Error(`Expected service override "${expectedService}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available service overrides: ${formatAvailable(available)}`);
989
- }
990
- return override;
991
- }
992
- /**
993
- * Assert that an activated extension registration registry contains a schema
994
- * migration registered via `api.registerMigration(definition)` with the expected
995
- * id (optionally scoped to a specific extension and asserting the `mandatory`
996
- * governance flag, where an unset flag is treated as non-mandatory).
997
- *
998
- * Migrations are surfaced via `ExtensionActivationResult.registrations.migrations`.
999
- */
1000
- export function assertRegisteredMigration(registrations, expectation) {
1001
- const expectedMigration = normalizeSdkIdentifier(registeredExpectationName(expectation, expectation.migration));
1002
- if (expectedMigration.length === 0) {
1003
- throw new Error("Expected migration id must be a non-empty string");
1004
- }
1005
- const candidates = registrations.migrations.filter((entry) => (expectation.extensionName === undefined ||
1006
- entry.name === expectation.extensionName) &&
1007
- typeof entry.definition.id === "string" &&
1008
- normalizeSdkIdentifier(entry.definition.id) === expectedMigration);
1009
- const match = candidates.find((entry) => expectation.mandatory === undefined ||
1010
- (entry.definition.mandatory ?? false) === expectation.mandatory);
1011
- if (match) {
1012
- return match;
1013
- }
1014
- // The id (and any extension scope) matched but the `mandatory` flag did not:
1015
- // report the mismatch directly rather than the misleading "not registered"
1016
- // listing. Reaching here with a candidate implies `expectation.mandatory` was
1017
- // set, since an unset flag matches any candidate above.
1018
- const idMatch = candidates[0];
1019
- if (idMatch !== undefined) {
1020
- throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to have ` +
1021
- `mandatory=${expectation.mandatory}, but it is mandatory=${idMatch.definition.mandatory ?? false}.`);
1022
- }
1023
- const available = sortedUnique(registrations.migrations.map((entry) => {
1024
- const id = typeof entry.definition.id === "string" &&
1025
- entry.definition.id.trim().length > 0
1026
- ? entry.definition.id.trim()
1027
- : "(unnamed)";
1028
- return `${id}:${entry.definition.mandatory === true}`;
1029
- }));
1030
- throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available migrations: ${formatAvailable(available)}`);
1031
- }
1032
- /**
1033
- * Assert that an activated extension declares no capability it never uses
1034
- * (least privilege). Pass the same capabilities as `manifest.capabilities` via
1035
- * `expectation.declared`; the helper computes what the extension actually
1036
- * registered against and throws when any declared capability is unused.
1037
- *
1038
- * This is the package-test counterpart of the advisory
1039
- * `extension_capability_unused` warning `pm package doctor` emits, letting an
1040
- * author catch an over-broad manifest at `npm test` time. Returns the
1041
- * declared/used/unused breakdown for further assertions.
1042
- */
1043
- export function assertExtensionCapabilityUsage(activation, expectation) {
1044
- const toKnownCapabilitySet = (capabilities, field) => {
1045
- const known = new Set();
1046
- for (const capability of capabilities) {
1047
- const normalized = normalizeKnownExtensionCapability(capability);
1048
- if (normalized === null) {
1049
- throw new Error(`Expected ${field} capability "${capability}" to be a known extension capability. ` +
1050
- "Use canonical capability names (see manifest.capabilities).");
1051
- }
1052
- known.add(normalized);
1053
- }
1054
- return known;
1055
- };
1056
- const declared = [
1057
- ...toKnownCapabilitySet(expectation.declared, "declared"),
1058
- ].sort((left, right) => left.localeCompare(right));
1059
- const allowUnused = toKnownCapabilitySet(expectation.allowUnused ?? [], "allowUnused");
1060
- const used = collectUsedExtensionCapabilities(activation, {
1061
- extensionName: expectation.extensionName,
1062
- });
1063
- const usedSet = new Set(used);
1064
- const unused = declared.filter((capability) => !usedSet.has(capability) && !allowUnused.has(capability));
1065
- if (unused.length > 0) {
1066
- const scopeSuffix = extensionNameSuffix(expectation.extensionName);
1067
- throw new Error(`Expected every declared capability${scopeSuffix} to be exercised, but [${unused.join(", ")}] ${unused.length === 1 ? "is" : "are"} declared yet never registered against. ` +
1068
- `Remove ${unused.length === 1 ? "it" : "them"} from manifest.capabilities for least privilege, ` +
1069
- "or pass allowUnused for capabilities registered only behind a runtime flag.");
1070
- }
1071
- return { declared, used, unused };
1072
- }
1073
- /**
1074
- * Preflight a declarative {@link ExtensionBlueprint} in a test, throwing if it has
1075
- * any `error`-severity issue (today: capability the blueprint exercises but the
1076
- * declared set omits, which would fail activation with `extension_capability_missing`).
1077
- *
1078
- * The throwing counterpart to {@link lintExtensionBlueprint} and the `assert*`
1079
- * family member for declarative authoring: drop `assertExtensionBlueprint(blueprint)`
1080
- * into a package's `node:test`/Vitest suite to fail CI before the blueprint is
1081
- * ever activated. The full {@link ExtensionBlueprintLintResult} is returned on
1082
- * success so a test can still inspect advisory warnings (unused capability,
1083
- * duplicate command, empty surface) without failing on them.
1084
- */
1085
- export function assertExtensionBlueprint(blueprint, options = {}) {
1086
- const result = lintExtensionBlueprint(blueprint, options);
1087
- if (!result.ok) {
1088
- const errors = result.findings.filter((finding) => finding.severity === "error");
1089
- throw new Error(`Extension blueprint failed preflight with ${errors.length} ${errors.length === 1 ? "error" : "errors"}:\n` +
1090
- errors
1091
- .map((finding) => ` - [${finding.code}] ${finding.message}`)
1092
- .join("\n"));
1093
- }
1094
- return result;
1095
- }
1096
- /**
1097
- * Assert a {@link ProjectProfileDefinition} is internally consistent in a test,
1098
- * throwing on any `error`-severity finding (and on `warning`-severity findings
1099
- * too when `strict` is set). The author-time counterpart to `defineProjectProfile`
1100
- * and the project-profile analogue of {@link assertExtensionBlueprint}: drop
1101
- * `assertProjectProfile(myProfile)` into a package's `node:test`/Vitest suite to
1102
- * fail CI before the profile is ever registered via `api.registerProfile` or
1103
- * applied via `pm profile apply`. The full {@link ProjectProfileLintReport} is
1104
- * returned on success so a test can still inspect advisory warnings without
1105
- * failing on them.
1106
- */
1107
- export function assertProjectProfile(profile, options = {}) {
1108
- const report = lintProjectProfile(profile);
1109
- const blocking = report.findings.filter((finding) => finding.severity === "error" ||
1110
- (options.strict === true && finding.severity === "warning"));
1111
- if (blocking.length > 0) {
1112
- throw new Error(`Project profile "${report.profile}" failed lint with ${blocking.length} ${blocking.length === 1 ? "issue" : "issues"}:\n` +
1113
- blocking
1114
- .map((finding) => ` - ${finding.severity} [${finding.code}] ${finding.message}`)
1115
- .join("\n"));
1116
- }
1117
- return report;
1118
- }
1119
- /**
1120
- * Strictly assert a manifest's `capabilities` equal the least-privilege set a
1121
- * declarative {@link ExtensionBlueprint} exercises, throwing on any drift in
1122
- * either direction.
1123
- *
1124
- * This is the strict bookend to the lenient {@link assertExtensionBlueprint}:
1125
- * where that helper fails only on an *undeclared* capability (the `error`-severity
1126
- * `extension_capability_missing` activation crash) and merely *warns* on an unused
1127
- * one, this assertion fails on **both** — an undeclared capability (`missing`) and
1128
- * a declared-but-unused one (`unused`) — so a hand-maintained `manifest.json` stays
1129
- * exactly the set the blueprint requires. Drop it into a package's
1130
- * `node:test`/Vitest suite to guard against capability drift in CI, the natural
1131
- * companion to {@link ../sdk/compose.js#synthesizeExtensionManifest} (assert what
1132
- * you would otherwise generate). Only `capabilities` are reconciled because that is
1133
- * the one manifest field a blueprint determines.
1134
- *
1135
- * The reconciliation is returned on success so a test can still inspect advisory
1136
- * warnings (duplicate command, empty surface) without failing on them.
1137
- */
1138
- export function assertExtensionManifestMatchesBlueprint(manifest, blueprint) {
1139
- // Coerce a malformed/absent capabilities field to an explicit empty declared set
1140
- // so an untyped `.js` caller gets a deterministic `missing` list rather than the
1141
- // lint silently falling back to the blueprint's in-module manifest mirror.
1142
- const declaredCapabilities = Array.isArray(manifest.capabilities)
1143
- ? manifest.capabilities
1144
- : [];
1145
- const result = lintExtensionBlueprint(blueprint, { declaredCapabilities });
1146
- // `declared` is guaranteed non-null here: we always hand lint an array, so it
1147
- // never takes its "no declared set" branch. The cast drops the unreachable null.
1148
- const declared = result.declared;
1149
- const declaredSet = new Set(declared);
1150
- const usedSet = new Set(result.used);
1151
- const missing = result.used.filter((capability) => !declaredSet.has(capability));
1152
- const unused = declared.filter((capability) => !usedSet.has(capability));
1153
- if (missing.length > 0 || unused.length > 0) {
1154
- const parts = [];
1155
- if (missing.length > 0) {
1156
- parts.push(`missing [${missing.join(", ")}] (the blueprint registers ${missing.length === 1 ? "a surface" : "surfaces"} requiring ${missing.length === 1 ? "it" : "them"}; activation throws extension_capability_missing)`);
1157
- }
1158
- if (unused.length > 0) {
1159
- parts.push(`unused [${unused.join(", ")}] (declared but no surface exercises ${unused.length === 1 ? "it" : "them"}; drop for least privilege)`);
1160
- }
1161
- throw new Error(`Manifest capabilities do not match the blueprint: ${parts.join("; ")}. ` +
1162
- "Set capabilities to synthesizeExtensionManifest(blueprint, identity).capabilities to stay in sync.");
1163
- }
1164
- return {
1165
- used: result.used,
1166
- declared,
1167
- missing,
1168
- unused,
1169
- findings: result.findings,
1170
- };
1171
- }
1172
- /**
1173
- * Assert an extension manifest's declared version bounds permit it to load on a
1174
- * target pm CLI version, throwing when a bound blocks the load.
1175
- *
1176
- * This is the throwing CI/test counterpart to
1177
- * {@link ../sdk/compose.js#checkExtensionManifestCompatibility}: it runs the same
1178
- * author-time version-bound analysis and fails on any blocking incompatibility — a
1179
- * malformed bound (`*_invalid`), a `pm_min_version` the target is below
1180
- * (`pm_min_version_unmet`), or a `block`-mode `pm_max_version` the target exceeds
1181
- * (`pm_max_version_exceeded`). Advisory `warning` findings (`*_unchecked`, or a
1182
- * `warn`-mode `pm_max_version_exceeded_warn`) never throw, because the loader
1183
- * would still load the extension. Pin it to the pm version a package commits to
1184
- * supporting so a too-tight or malformed bound fails the package's own suite, not
1185
- * a user's install. The full compatibility result is returned on success so a test
1186
- * can still inspect advisory warnings.
1187
- */
1188
- export function assertExtensionManifestCompatible(manifest, target) {
1189
- const result = checkExtensionManifestCompatibility(manifest, target);
1190
- const blocking = result.findings.filter((finding) => finding.severity === "error");
1191
- if (blocking.length > 0) {
1192
- throw new Error(`Extension manifest is not compatible with pm ${result.pmVersion}: ${blocking
1193
- .map((finding) => finding.message)
1194
- .join("; ")}`);
1195
- }
1196
- return result;
1197
- }
1198
- /**
1199
- * Preflight a declarative {@link ExtensionBlueprint} through every author-time
1200
- * check in one test, throwing if any stage produced an `error`-severity finding.
1201
- *
1202
- * This is the throwing CI/test bookend over
1203
- * {@link ../sdk/compose.js#preflightExtension} — the single guard that replaces
1204
- * chaining {@link assertExtensionBlueprint}, {@link assertExtensionManifestMatchesBlueprint},
1205
- * and {@link assertExtensionManifestCompatible} in a package's
1206
- * `node:test`/Vitest suite. It runs the same consolidated analysis (always lints
1207
- * the blueprint; synthesizes the manifest when `options.identity` is given; checks
1208
- * version bounds when `options.target` is given) and throws one error listing every
1209
- * blocking finding tagged by its `source:code`. Advisory `warning` findings (an
1210
- * unused capability, a duplicate command, an `*_unchecked` bound) never throw,
1211
- * matching the underlying stages. The full {@link ExtensionPreflightReport} is
1212
- * returned on success so a test can still inspect those warnings, the synthesized
1213
- * manifest, and the derived capability set without failing on them.
1214
- */
1215
- export function assertExtensionPreflight(blueprint, options = {}) {
1216
- const report = preflightExtension(blueprint, options);
1217
- if (!report.ok) {
1218
- const errors = report.findings.filter((finding) => finding.severity === "error");
1219
- throw new Error(`Extension failed preflight with ${errors.length} ${errors.length === 1 ? "error" : "errors"}:\n` +
1220
- errors
1221
- .map((finding) => ` - [${finding.source}:${finding.code}] ${finding.message}`)
1222
- .join("\n"));
1223
- }
1224
- return report;
1225
- }
1226
- /** Reject obviously wrong harness input (e.g. an options object passed as the module) instead of silently producing an empty activation. Mirrors the loader's activatable-extension shapes: an `activate` function on the module or on its default export. */
1227
- function assertTestModuleHasActivateExport(module) {
1228
- if (resolveActivatablePropertyRecord(module)) {
1229
- return;
1230
- }
1231
- throw new Error("createExtensionTestHarness received a module with no activate export. Pass the extension module (with an activate(api) function on the module or its default export) as the first argument, and options second.");
1232
- }
1233
- /**
1234
- * Activate one in-memory extension module and return a fluent
1235
- * {@link ExtensionTestHarness} that binds every SDK testing helper to the right
1236
- * sub-registry of the resulting activation.
1237
- *
1238
- * This is the ergonomic capstone over the standalone helpers: instead of
1239
- * `activateExtensionForTest(module)` followed by repeatedly threading
1240
- * `activation.registrations` / `activation.commands` / `activation.hooks` into
1241
- * each `assertRegistered*` / `runRegistered*` call, an author writes
1242
- * `const ext = await createExtensionTestHarness(module, { capabilities });` and
1243
- * then `ext.assertCommandContract(...)`, `await ext.runCommand(...)`,
1244
- * `await ext.deactivate()` — never needing to know pm's internal registry layout.
1245
- *
1246
- * `name` and `layer` are resolved exactly as {@link activateExtensionForTest}
1247
- * resolves them, and captured so {@link ExtensionTestHarness.deactivate} forwards
1248
- * a matching skip-key. Activation runs pm's real validation/activation engine, so
1249
- * every bound helper exercises real wiring and dispatch — not mocks.
1250
- *
1251
- * Fails fast: if the module does not activate cleanly (e.g. a registration is
1252
- * dropped because the manifest omits the required capability), this throws a
1253
- * descriptive error listing each failure instead of returning a harness whose
1254
- * registries are empty — which would otherwise surface later as the confusing
1255
- * `available: (none)` assertion error this helper exists to prevent. Tests that
1256
- * deliberately exercise a *failed* activation should call
1257
- * {@link activateExtensionForTest} directly and inspect `activation.failed`.
1258
- */
1259
- export async function createExtensionTestHarness(module, options = {}) {
1260
- assertTestModuleHasActivateExport(module);
1261
- const manifest = readTestExtensionManifest(module);
1262
- const name = resolveTestExtensionName(manifest, options.name);
1263
- const layer = options.layer ?? "project";
1264
- const activation = await activateExtensionForTest(module, options);
1265
- if (activation.failed.length > 0) {
1266
- const detail = activation.failed
1267
- .map((failure) => {
1268
- const missingCapability = failure.trace?.missing_capability;
1269
- const reason = missingCapability
1270
- ? `${failure.error}; missing capability "${missingCapability}"`
1271
- : failure.error;
1272
- return `${failure.layer}:${failure.name} (${reason})`;
1273
- })
1274
- .join(", ");
1275
- throw new Error(`createExtensionTestHarness could not activate the extension cleanly: ${detail}. ` +
1276
- "Declare the required capability in the manifest (or pass it via options.capabilities), " +
1277
- "or use activateExtensionForTest directly to inspect a failed activation.");
1278
- }
1279
- return {
1280
- module,
1281
- name,
1282
- layer,
1283
- activation,
1284
- activationSummary(describeOptions) {
1285
- return describeExtensionActivation(activation, describeOptions);
1286
- },
1287
- renderMarkdown(markdownOptions = {}) {
1288
- const { extensionName, ...renderOptions } = markdownOptions ?? {};
1289
- return renderExtensionSurfaceMarkdown(describeExtensionActivation(activation, { extensionName }), renderOptions);
1290
- },
1291
- assertCommandContract(expectation) {
1292
- return assertRegisteredCommandContract(activation.registrations, expectation);
1293
- },
1294
- assertFlags(expectation) {
1295
- return assertRegisteredFlags(activation.registrations, expectation);
1296
- },
1297
- assertItemField(expectation) {
1298
- return assertRegisteredItemField(activation.registrations, expectation);
1299
- },
1300
- assertItemType(expectation) {
1301
- return assertRegisteredItemType(activation.registrations, expectation);
1302
- },
1303
- assertProfile(expectation) {
1304
- return assertRegisteredProfile(activation.registrations, expectation);
1305
- },
1306
- assertHook(expectation) {
1307
- return assertRegisteredHook(activation.hooks, expectation);
1308
- },
1309
- assertCommandOverride(expectation) {
1310
- return assertRegisteredCommandOverride(activation.commands, expectation);
1311
- },
1312
- assertParserOverride(expectation) {
1313
- return assertRegisteredParserOverride(activation.parsers, expectation);
1314
- },
1315
- assertPreflightOverride(expectation) {
1316
- return assertRegisteredPreflightOverride(activation.preflight, expectation);
1317
- },
1318
- assertRendererOverride(expectation) {
1319
- return assertRegisteredRendererOverride(activation.renderers, expectation);
1320
- },
1321
- assertServiceOverride(expectation) {
1322
- return assertRegisteredServiceOverride(activation.services, expectation);
1323
- },
1324
- assertSearchProvider(expectation) {
1325
- return assertRegisteredSearchProvider(activation.registrations, expectation);
1326
- },
1327
- assertVectorStoreAdapter(expectation) {
1328
- return assertRegisteredVectorStoreAdapter(activation.registrations, expectation);
1329
- },
1330
- assertImporter(expectation) {
1331
- return assertRegisteredImporter(activation.registrations, expectation);
1332
- },
1333
- assertExporter(expectation) {
1334
- return assertRegisteredExporter(activation.registrations, expectation);
1335
- },
1336
- assertMigration(expectation) {
1337
- return assertRegisteredMigration(activation.registrations, expectation);
1338
- },
1339
- assertCapabilityUsage(expectation) {
1340
- return assertExtensionCapabilityUsage(activation, expectation);
1341
- },
1342
- runCommand(runOptions) {
1343
- return runRegisteredCommandForTest(activation.commands, runOptions);
1344
- },
1345
- runHook(runOptions) {
1346
- return runRegisteredHookForTest(activation.hooks, runOptions);
1347
- },
1348
- runMutationGuard(runOptions) {
1349
- return runRegisteredMutationGuardForTest(activation.hooks, runOptions);
1350
- },
1351
- runCommandOverride(context) {
1352
- return runRegisteredCommandOverrideForTest(activation.commands, context);
1353
- },
1354
- runParserOverride(context) {
1355
- return runRegisteredParserOverrideForTest(activation.parsers, context);
1356
- },
1357
- runPreflightOverride(context) {
1358
- return runRegisteredPreflightOverrideForTest(activation.preflight, context);
1359
- },
1360
- runRendererOverride(context) {
1361
- return runRegisteredRendererOverrideForTest(activation.renderers, context);
1362
- },
1363
- runServiceOverride(context) {
1364
- return runRegisteredServiceOverrideForTest(activation.services, context);
1365
- },
1366
- runSearchProvider(runOptions) {
1367
- return runRegisteredSearchProviderForTest(activation.registrations, runOptions);
1368
- },
1369
- runVectorStoreAdapter(runOptions) {
1370
- return runRegisteredVectorStoreAdapterForTest(activation.registrations, runOptions);
1371
- },
1372
- runMigration(runOptions) {
1373
- return runRegisteredMigrationForTest(activation.registrations, runOptions);
1374
- },
1375
- runImporter(runOptions) {
1376
- return runRegisteredImporterForTest(activation, runOptions);
1377
- },
1378
- runExporter(runOptions) {
1379
- return runRegisteredExporterForTest(activation, runOptions);
1380
- },
1381
- deactivate(deactivateOptions = {}) {
1382
- return deactivateExtensionForTest(module, {
1383
- name,
1384
- layer,
1385
- activation,
1386
- deactivateTimeoutMs: deactivateOptions.deactivateTimeoutMs,
1387
- });
1388
- },
1389
- };
1390
- }
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fa9e95e5-7782-509d-96af-093a47daaed7")}catch(e2){}})();import{activateExtensions,deactivateExtensions,runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride}from"../core/extensions/loader.js";import{createDefaultExtensionGovernancePolicy}from"../core/extensions/extension-types.js";import{getMigrationRuntimeDefinition,resolveRegisteredSearchProvider,resolveRegisteredVectorStoreAdapter}from"../core/extensions/runtime-registrations.js";import{collectUsedExtensionCapabilities}from"../core/extensions/capability-usage.js";import{normalizeKnownExtensionCapability}from"../core/extensions/extension-capability-aliases.js";import{checkExtensionManifestCompatibility,describeExtensionBlueprint,lintExtensionBlueprint,preflightExtension}from"./compose.js";import{describeExtensionActivation}from"../core/extensions/activation-summary.js";import{lintProjectProfile}from"../core/profile/profile-lint.js";import{renderExtensionSurfaceMarkdown}from"../core/extensions/activation-summary-markdown.js";import{asPropertyRecord,resolveActivatablePropertyRecord}from"../core/shared/primitives.js";import{createExtensionCommandSdk}from"./extension-command-context.js";import{PmClient}from"./runtime.js";function registeredExpectationName(expectation,legacyValue){return expectation.name??legacyValue??""}const HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",before_mutation:"beforeMutation",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};const RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};function normalizeSdkIdentifier(value){return value.trim().toLowerCase()}function normalizeSdkProfileMatchKey(value){return normalizeSdkIdentifier(value).replaceAll("-","_")}function extensionNameSuffix(extensionName){return extensionName?` from extension "${extensionName}"`:""}function sortedUnique(values){return[...new Set(values)].sort((left,right)=>left.localeCompare(right))}function normalizeSdkCommandName(command){if(typeof command!=="string"){throw new Error('A command name string is required. Pass it via the options object, e.g. runRegisteredCommandForTest(activation, { command: "my command" }) \u2014 positional command arguments are not supported.')}return command.trim().toLowerCase().split(/\s+/).filter(part=>part.length>0).join(" ")}function formatAvailable(values){return values.length>0?values.join(", "):"(none)"}function assertStringSetIncludes(actual,expected,label){const actualValues=new Set(actual??[]);const missing=sortedUnique(expected.filter(value=>!actualValues.has(value)));if(missing.length>0){throw new Error(`Expected package manifest ${label} to include ${missing.join(", ")}; available: ${formatAvailable(sortedUnique(actual??[]))}`)}}function collectFlagLabels(flags){const labels=new Set;for(const flag of flags){if(typeof flag.long==="string"&&flag.long.trim().length>0){labels.add(flag.long.trim())}if(typeof flag.short==="string"&&flag.short.trim().length>0){labels.add(flag.short.trim())}}return labels}function readTestExtensionManifest(module){const moduleRecord=asPropertyRecord(module);if(!moduleRecord){return{}}const manifest=asPropertyRecord(moduleRecord.manifest);if(manifest){return manifest}const defaultExport=asPropertyRecord(moduleRecord.default);const defaultManifest=asPropertyRecord(defaultExport?.manifest);if(defaultManifest){return defaultManifest}if(defaultExport&&typeof moduleRecord.default!=="function"&&("name"in defaultExport||"capabilities"in defaultExport)){return defaultExport}if(typeof module!=="function"&&("name"in moduleRecord||"capabilities"in moduleRecord)){return moduleRecord}return{}}function resolveTestExtensionName(manifest,explicitName){return explicitName??(typeof manifest.name==="string"&&manifest.name.trim().length>0?manifest.name.trim():"test-extension")}function readTestExtensionCapabilities(manifest,options){if(Array.isArray(options.capabilities)){return[...options.capabilities]}if(Array.isArray(manifest.capabilities)){return manifest.capabilities.filter(capability=>typeof capability==="string")}return[]}function buildSingleExtensionLoadResult(module,manifest,identity){return{disabled_by_flag:false,roots:{global:"",project:""},configured_enabled:[],configured_disabled:[],discovered:[],effective:[],warnings:[],policy:identity.policy,failed:[],loaded:[{layer:identity.layer,directory:"",manifest_path:"",name:identity.name,version:typeof manifest.version==="string"?manifest.version:"0.0.0",entry:typeof manifest.entry==="string"?manifest.entry:"./index.js",priority:typeof manifest.priority==="number"?manifest.priority:0,entry_path:"",capabilities:identity.capabilities,manifest_version:typeof manifest.manifest_version==="number"?manifest.manifest_version:void 0,pm_min_version:typeof manifest.pm_min_version==="string"?manifest.pm_min_version:void 0,pm_max_version:typeof manifest.pm_max_version==="string"?manifest.pm_max_version:void 0,engines:manifest.engines,trusted:manifest.trusted,provenance:manifest.provenance,sandbox_profile:manifest.sandbox_profile,permissions:manifest.permissions,activation:manifest.activation,module}]}}async function activateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return activateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,options),policy:options.policy??createDefaultExtensionGovernancePolicy()}))}async function deactivateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return deactivateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,{}),policy:createDefaultExtensionGovernancePolicy()}),options.activation,options.deactivateTimeoutMs===void 0?{}:{deactivate_timeout_ms:options.deactivateTimeoutMs})}async function runRegisteredCommandForTest(commands,options){const command=normalizeSdkCommandName(options.command);if(command.length===0){throw new Error("Expected command name must be a non-empty string")}const hasHandler=commands.handlers.some(entry=>entry.command===command);if(!hasHandler){const available=sortedUnique(commands.handlers.map(entry=>entry.command));throw new Error(`Expected a registered command handler for "${command}" to invoke. Available command handlers: ${formatAvailable(available)}`)}if(options.sdk!==void 0&&options.sdkFactory!==void 0){throw new TypeError("runRegisteredCommandForTest accepts sdk or sdkFactory, not both")}const pmRoot=options.pmRoot??"";const global2={json:true,quiet:true,noPager:true,...options.global};const sdk=options.sdk??options.sdkFactory?.({pmRoot,global:global2})??(pmRoot.length>0?createExtensionCommandSdk(pmRoot,new PmClient({pmRoot})):void 0);return runCommandHandler(commands,{command,args:options.args?[...options.args]:[],options:options.options?{...options.options}:{},global:global2,pm_root:pmRoot,...sdk===void 0?{}:{sdk}})}async function runRegisteredMutationGuardForTest(hooks,options){if((hooks.beforeMutation?.length??0)===0){throw new Error("Expected a registered before_mutation hook to invoke")}await runBeforeMutationHooks(hooks,options.context)}async function runRegisteredHookForTest(hooks,options){const kind=options.kind;if(kind==="before_mutation"){throw new Error("Invoke before_mutation hooks with runRegisteredMutationGuardForTest so denials and failures retain fail-closed semantics.")}if((hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[kind]]?.length??0)===0){const populatedKinds=sortedUnique(Object.keys(RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD).filter(candidateKind=>(hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[candidateKind]]?.length??0)>0));throw new Error(`Expected a registered "${kind}" hook to invoke. Hook kinds with registrations: ${formatAvailable(populatedKinds)}`)}switch(options.kind){case"before_command":return runBeforeCommandHooks(hooks,options.context);case"after_command":return runAfterCommandHooks(hooks,options.context);case"on_read":return runOnReadHooks(hooks,options.context);case"on_write":return runOnWriteHooks(hooks,options.context);case"on_index":return runOnIndexHooks(hooks,options.context)}}async function runRegisteredParserOverrideForTest(parsers,context){const command=normalizeSdkCommandName(context.command);if(!parsers.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered parser override for command "${command}" to invoke. Available parser override commands: ${formatAvailable(sortedUnique(parsers.overrides.map(entry=>entry.command)))}`)}return runParserOverride(parsers,context)}async function runRegisteredPreflightOverrideForTest(preflight,context){if(preflight.overrides.length===0){throw new Error("Expected a registered preflight override to invoke, but none are registered.")}return runPreflightOverride(preflight,context)}async function runRegisteredCommandOverrideForTest(commands,context){const command=normalizeSdkCommandName(context.command);if(!commands.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered command override for command "${command}" to invoke. Available command override commands: ${formatAvailable(sortedUnique(commands.overrides.map(entry=>entry.command)))}`)}return runCommandOverride(commands,context)}async function runRegisteredRendererOverrideForTest(renderers,context){if(!renderers.overrides.some(entry=>entry.format===context.format)){throw new Error(`Expected a registered renderer override for format "${context.format}" to invoke. Available renderer override formats: ${formatAvailable(sortedUnique(renderers.overrides.map(entry=>entry.format)))}`)}return runRendererOverride(renderers,context)}async function runRegisteredServiceOverrideForTest(services,context){if(!services.overrides.some(entry=>entry.service===context.service)){throw new Error(`Expected a registered service override for service "${context.service}" to invoke. Available service override services: ${formatAvailable(sortedUnique(services.overrides.map(entry=>entry.service)))}`)}return runServiceOverride(services,context)}const SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS={query:["query"],embed:["embed"],embedBatch:["embedBatch","embed_batch"],queryExpansion:["queryExpansion","query_expansion"],rerank:["rerank"]};async function runRegisteredSearchProviderForTest(registrations,options){const registration=resolveRegisteredSearchProvider(registrations,options.provider);if(!registration){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected a registered search provider "${options.provider}" to invoke. Available search providers: ${formatAvailable(available)}`)}const runtimeDefinition=registration.runtime_definition;const operationFn=SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS[options.operation].map(key=>runtimeDefinition[key]).find(value=>typeof value==="function");if(typeof operationFn!=="function"){throw new Error(`Registered search provider "${options.provider}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredVectorStoreAdapterForTest(registrations,options){const registration=resolveRegisteredVectorStoreAdapter(registrations,options.adapter);if(!registration){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected a registered vector store adapter "${options.adapter}" to invoke. Available vector store adapters: ${formatAvailable(available)}`)}const operationFn=registration.runtime_definition[options.operation];if(typeof operationFn!=="function"){throw new Error(`Registered vector store adapter "${options.adapter}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredMigrationForTest(registrations,options){const migration=assertRegisteredMigration(registrations,{migration:options.migration,extensionName:options.extensionName});const run=getMigrationRuntimeDefinition(migration).run;if(typeof run!=="function"){throw new Error(`Registered migration "${options.migration}" does not implement a run function to invoke.`)}const declaredStatus=migration.definition.status;const context={id:migration.definition.id,command:"migration",layer:migration.layer,extension:migration.name,pm_root:options.pmRoot??"",status:(typeof declaredStatus==="string"?declaredStatus.trim().toLowerCase():"")||"pending"};return await run(context)}async function runRegisteredImporterForTest(activation,options){const importer=assertRegisteredImporter(activation.registrations,{importer:options.importer,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${importer.importer} import`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}async function runRegisteredExporterForTest(activation,options){const exporter=assertRegisteredExporter(activation.registrations,{exporter:options.exporter,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${exporter.exporter} export`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}function assertExtensionDeactivated(result,expectation={}){const expectedDeactivated=expectation.deactivated??1;if(result.deactivated!==expectedDeactivated){throw new Error(`Expected ${expectedDeactivated} extension${expectedDeactivated===1?"":"s"} to deactivate cleanly, but ${result.deactivated} did.`)}const expectedFailed=expectation.failed??0;if(result.failed.length!==expectedFailed){const detail=result.failed.map(failure=>`${failure.layer}:${failure.name} (${failure.error})`).join(", ");throw new Error(`Expected ${expectedFailed} teardown failure${expectedFailed===1?"":"s"}, but observed ${result.failed.length}${detail.length>0?`: ${detail}`:""}.`)}return result}function assertPackageManifest(manifest,expectation){if(expectation.packageName!==void 0&&manifest.package_name!==expectation.packageName){throw new Error(`Expected package manifest package_name to be "${expectation.packageName}"; received "${manifest.package_name??"(none)"}"`)}if(expectation.aliases!==void 0){assertStringSetIncludes(manifest.aliases,expectation.aliases,"aliases")}if(expectation.resources!==void 0){for(const[kind,expectedPaths]of Object.entries(expectation.resources)){assertStringSetIncludes(manifest.resources[kind],expectedPaths,`pm.${kind}`)}}return manifest}function assertRegisteredCommandContract(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const commandCandidates=registrations.commands.filter(entry=>entry.command===expectedCommand);const command=expectation.extensionName?commandCandidates.find(entry=>entry.name===expectation.extensionName):commandCandidates[0];if(!command){const available=registrations.commands.map(entry=>entry.command).sort((left,right)=>left.localeCompare(right));const extensionSuffix=expectation.extensionName?` from extension "${expectation.extensionName}"`:"";throw new Error(`Expected extension command "${expectedCommand}"${extensionSuffix} to be registered. Available commands: ${formatAvailable(available)}`)}if(expectation.action!==void 0&&command.action!==expectation.action){throw new Error(`Expected extension command "${expectedCommand}" action "${expectation.action}", received "${command.action}"`)}if(expectation.arguments!==void 0){const actualArguments=(command.arguments??[]).map(argument=>argument.name);const missingArguments=expectation.arguments.filter(argument=>!actualArguments.includes(argument));if(missingArguments.length>0){throw new Error(`Expected extension command "${expectedCommand}" arguments ${formatAvailable(expectation.arguments)}; missing ${formatAvailable(missingArguments)}; available ${formatAvailable(actualArguments)}`)}}const flags=registrations.flags.filter(entry=>entry.target_command===expectedCommand&&(expectation.extensionName===void 0||entry.name===expectation.extensionName)).flatMap(entry=>entry.flags);if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected extension command "${expectedCommand}" flags ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return{command,flags}}function assertRegisteredFlags(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.targetCommand));if(expectedCommand.length===0){throw new Error("Expected target command name must be a non-empty string")}const candidates=registrations.flags.filter(entry=>entry.target_command===expectedCommand);let registration;if(expectation.extensionName){registration=candidates.find(entry=>entry.name===expectation.extensionName)}else if(candidates.length===1){registration=candidates[0]}else if(candidates.length>1){const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}" matched multiple extensions: ${formatAvailable(availableExtensions)}. Specify extensionName to choose one registration.`)}if(!registration){const available=sortedUnique(registrations.flags.map(entry=>entry.target_command));const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available flag target commands: ${formatAvailable(available)}; matching extensions: ${formatAvailable(availableExtensions)}`)}if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(registration.flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected flags for target command "${expectedCommand}" to include ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return registration}function assertRegisteredHook(hooks,expectation){const field=HOOK_KIND_TO_REGISTRY_FIELD[expectation.kind];const candidates=hooks[field]??[];const hook=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!hook){const available=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected a "${expectation.kind}" hook${extensionNameSuffix(expectation.extensionName)} to be registered. Available "${expectation.kind}" hooks: ${formatAvailable(available)}`)}return hook}function assertRegisteredSearchProvider(registrations,expectation){const expectedProvider=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.provider));if(expectedProvider.length===0){throw new Error("Expected search provider name must be a non-empty string")}const candidates=registrations.search_providers.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedProvider);const provider=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!provider){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected search provider "${expectedProvider}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available search providers: ${formatAvailable(available)}`)}return provider}function assertRegisteredVectorStoreAdapter(registrations,expectation){const expectedAdapter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.adapter));if(expectedAdapter.length===0){throw new Error("Expected vector store adapter name must be a non-empty string")}const candidates=registrations.vector_store_adapters.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedAdapter);const adapter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!adapter){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected vector store adapter "${expectedAdapter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available vector store adapters: ${formatAvailable(available)}`)}return adapter}function assertRegisteredImporter(registrations,expectation){const expectedImporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.importer));if(expectedImporter.length===0){throw new Error("Expected importer name must be a non-empty string")}const candidates=registrations.importers.filter(entry=>normalizeSdkIdentifier(entry.importer)===expectedImporter);const importer=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!importer){const available=sortedUnique(registrations.importers.map(entry=>entry.importer));throw new Error(`Expected importer "${expectedImporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available importers: ${formatAvailable(available)}`)}return importer}function assertRegisteredExporter(registrations,expectation){const expectedExporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.exporter));if(expectedExporter.length===0){throw new Error("Expected exporter name must be a non-empty string")}const candidates=registrations.exporters.filter(entry=>normalizeSdkIdentifier(entry.exporter)===expectedExporter);const exporter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!exporter){const available=sortedUnique(registrations.exporters.map(entry=>entry.exporter));throw new Error(`Expected exporter "${expectedExporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available exporters: ${formatAvailable(available)}`)}return exporter}function assertRegisteredItemField(registrations,expectation){const expectedField=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.field));if(expectedField.length===0){throw new Error("Expected item field name must be a non-empty string")}const candidates=registrations.item_fields.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,field:registration.fields.find(field=>normalizeSdkIdentifier(field.name)===expectedField)})).filter(entry=>entry.field!==void 0);const match=candidates.find(entry=>expectation.type===void 0||entry.field.type===expectation.type);if(!match){const available=sortedUnique(registrations.item_fields.flatMap(entry=>entry.fields.map(field=>`${field.name}:${field.type}`)));throw new Error(`Expected item field "${expectedField}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item fields: ${formatAvailable(available)}`)}return match}function assertRegisteredItemType(registrations,expectation){const expectedType=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.itemType));if(expectedType.length===0){throw new Error("Expected item type name must be a non-empty string")}const candidates=registrations.item_types.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,itemType:registration.types.find(itemType=>normalizeSdkIdentifier(itemType.name)===expectedType)})).filter(entry=>entry.itemType!==void 0);const match=candidates.find(entry=>expectation.folder===void 0||entry.itemType.folder===expectation.folder);if(!match){const available=sortedUnique(registrations.item_types.flatMap(entry=>entry.types.map(itemType=>`${itemType.name}:${itemType.folder}`)));throw new Error(`Expected item type "${expectedType}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item types: ${formatAvailable(available)}`)}return match}function assertRegisteredProfile(registrations,expectation){const expectedProfile=normalizeSdkProfileMatchKey(registeredExpectationName(expectation,expectation.profile));if(expectedProfile.length===0){throw new Error("Expected profile name must be a non-empty string")}const match=registrations.profiles.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).find(entry=>normalizeSdkProfileMatchKey(entry.profile.name)===expectedProfile);if(!match){const available=sortedUnique(registrations.profiles.map(entry=>entry.profile.name));throw new Error(`Expected profile "${expectedProfile}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available profiles: ${formatAvailable(available)}`)}return{registration:match,profile:match.profile}}function assertRegisteredCommandOverride(commands,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=commands.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(commands.overrides.map(entry=>entry.command));throw new Error(`Expected command override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available command overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredParserOverride(parsers,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=parsers.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(parsers.overrides.map(entry=>entry.command));throw new Error(`Expected parser override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available parser overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredPreflightOverride(preflight,expectation={}){const override=expectation.extensionName?preflight.overrides.find(entry=>entry.name===expectation.extensionName):preflight.overrides[0];if(!override){const available=sortedUnique(preflight.overrides.map(entry=>entry.name));throw new Error(`Expected a preflight override${extensionNameSuffix(expectation.extensionName)} to be registered. Available preflight overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredRendererOverride(renderers,expectation){const expectedFormat=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.format));if(expectedFormat.length===0){throw new Error("Expected renderer format must be a non-empty string")}const candidates=renderers.overrides.filter(entry=>entry.format===expectedFormat);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(renderers.overrides.map(entry=>entry.format));throw new Error(`Expected renderer override "${expectedFormat}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available renderer overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredServiceOverride(services,expectation){const expectedService=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.service));if(expectedService.length===0){throw new Error("Expected service name must be a non-empty string")}const candidates=services.overrides.filter(entry=>normalizeSdkIdentifier(entry.service)===expectedService);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(services.overrides.map(entry=>entry.service));throw new Error(`Expected service override "${expectedService}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available service overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredMigration(registrations,expectation){const expectedMigration=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.migration));if(expectedMigration.length===0){throw new Error("Expected migration id must be a non-empty string")}const candidates=registrations.migrations.filter(entry=>(expectation.extensionName===void 0||entry.name===expectation.extensionName)&&typeof entry.definition.id==="string"&&normalizeSdkIdentifier(entry.definition.id)===expectedMigration);const match=candidates.find(entry=>expectation.mandatory===void 0||(entry.definition.mandatory??false)===expectation.mandatory);if(match){return match}const idMatch=candidates[0];if(idMatch!==void 0){throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to have mandatory=${expectation.mandatory}, but it is mandatory=${idMatch.definition.mandatory??false}.`)}const available=sortedUnique(registrations.migrations.map(entry=>{const id=typeof entry.definition.id==="string"&&entry.definition.id.trim().length>0?entry.definition.id.trim():"(unnamed)";return`${id}:${entry.definition.mandatory===true}`}));throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available migrations: ${formatAvailable(available)}`)}function assertExtensionCapabilityUsage(activation,expectation){const toKnownCapabilitySet=(capabilities,field)=>{const known=new Set;for(const capability of capabilities){const normalized=normalizeKnownExtensionCapability(capability);if(normalized===null){throw new Error(`Expected ${field} capability "${capability}" to be a known extension capability. Use canonical capability names (see manifest.capabilities).`)}known.add(normalized)}return known};const declared=[...toKnownCapabilitySet(expectation.declared,"declared")].sort((left,right)=>left.localeCompare(right));const allowUnused=toKnownCapabilitySet(expectation.allowUnused??[],"allowUnused");const used=collectUsedExtensionCapabilities(activation,{extensionName:expectation.extensionName});const usedSet=new Set(used);const unused=declared.filter(capability=>!usedSet.has(capability)&&!allowUnused.has(capability));if(unused.length>0){const scopeSuffix=extensionNameSuffix(expectation.extensionName);throw new Error(`Expected every declared capability${scopeSuffix} to be exercised, but [${unused.join(", ")}] ${unused.length===1?"is":"are"} declared yet never registered against. Remove ${unused.length===1?"it":"them"} from manifest.capabilities for least privilege, or pass allowUnused for capabilities registered only behind a runtime flag.`)}return{declared,used,unused}}function assertExtensionBlueprint(blueprint,options={}){const result=lintExtensionBlueprint(blueprint,options);if(!result.ok){const errors=result.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension blueprint failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
2
+ `+errors.map(finding=>` - [${finding.code}] ${finding.message}`).join("\n"))}return result}function assertProjectProfile(profile,options={}){const report=lintProjectProfile(profile);const blocking=report.findings.filter(finding=>finding.severity==="error"||options.strict===true&&finding.severity==="warning");if(blocking.length>0){throw new Error(`Project profile "${report.profile}" failed lint with ${blocking.length} ${blocking.length===1?"issue":"issues"}:
3
+ `+blocking.map(finding=>` - ${finding.severity} [${finding.code}] ${finding.message}`).join("\n"))}return report}function assertExtensionManifestMatchesBlueprint(manifest,blueprint){const declaredCapabilities=Array.isArray(manifest.capabilities)?manifest.capabilities:[];const result=lintExtensionBlueprint(blueprint,{declaredCapabilities});const declared=result.declared;const declaredSet=new Set(declared);const usedSet=new Set(result.used);const missing=result.used.filter(capability=>!declaredSet.has(capability));const unused=declared.filter(capability=>!usedSet.has(capability));if(missing.length>0||unused.length>0){const parts=[];if(missing.length>0){parts.push(`missing [${missing.join(", ")}] (the blueprint registers ${missing.length===1?"a surface":"surfaces"} requiring ${missing.length===1?"it":"them"}; activation throws extension_capability_missing)`)}if(unused.length>0){parts.push(`unused [${unused.join(", ")}] (declared but no surface exercises ${unused.length===1?"it":"them"}; drop for least privilege)`)}throw new Error(`Manifest capabilities do not match the blueprint: ${parts.join("; ")}. Set capabilities to synthesizeExtensionManifest(blueprint, identity).capabilities to stay in sync.`)}return{used:result.used,declared,missing,unused,findings:result.findings}}function assertExtensionManifestCompatible(manifest,target){const result=checkExtensionManifestCompatibility(manifest,target);const blocking=result.findings.filter(finding=>finding.severity==="error");if(blocking.length>0){throw new Error(`Extension manifest is not compatible with pm ${result.pmVersion}: ${blocking.map(finding=>finding.message).join("; ")}`)}return result}function assertExtensionPreflight(blueprint,options={}){const report=preflightExtension(blueprint,options);if(!report.ok){const errors=report.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
4
+ `+errors.map(finding=>` - [${finding.source}:${finding.code}] ${finding.message}`).join("\n"))}return report}function assertTestModuleHasActivateExport(module){if(resolveActivatablePropertyRecord(module)){return}throw new Error("createExtensionTestHarness received a module with no activate export. Pass the extension module (with an activate(api) function on the module or its default export) as the first argument, and options second.")}async function createExtensionTestHarness(module,options={}){assertTestModuleHasActivateExport(module);const manifest=readTestExtensionManifest(module);const name=resolveTestExtensionName(manifest,options.name);const layer=options.layer??"project";const activation=await activateExtensionForTest(module,options);if(activation.failed.length>0){const detail=activation.failed.map(failure=>{const missingCapability=failure.trace?.missing_capability;const reason=missingCapability?`${failure.error}; missing capability "${missingCapability}"`:failure.error;return`${failure.layer}:${failure.name} (${reason})`}).join(", ");throw new Error(`createExtensionTestHarness could not activate the extension cleanly: ${detail}. Declare the required capability in the manifest (or pass it via options.capabilities), or use activateExtensionForTest directly to inspect a failed activation.`)}return{module,name,layer,activation,activationSummary(describeOptions){return describeExtensionActivation(activation,describeOptions)},renderMarkdown(markdownOptions={}){const{extensionName,...renderOptions}=markdownOptions??{};return renderExtensionSurfaceMarkdown(describeExtensionActivation(activation,{extensionName}),renderOptions)},assertCommandContract(expectation){return assertRegisteredCommandContract(activation.registrations,expectation)},assertFlags(expectation){return assertRegisteredFlags(activation.registrations,expectation)},assertItemField(expectation){return assertRegisteredItemField(activation.registrations,expectation)},assertItemType(expectation){return assertRegisteredItemType(activation.registrations,expectation)},assertProfile(expectation){return assertRegisteredProfile(activation.registrations,expectation)},assertHook(expectation){return assertRegisteredHook(activation.hooks,expectation)},assertCommandOverride(expectation){return assertRegisteredCommandOverride(activation.commands,expectation)},assertParserOverride(expectation){return assertRegisteredParserOverride(activation.parsers,expectation)},assertPreflightOverride(expectation){return assertRegisteredPreflightOverride(activation.preflight,expectation)},assertRendererOverride(expectation){return assertRegisteredRendererOverride(activation.renderers,expectation)},assertServiceOverride(expectation){return assertRegisteredServiceOverride(activation.services,expectation)},assertSearchProvider(expectation){return assertRegisteredSearchProvider(activation.registrations,expectation)},assertVectorStoreAdapter(expectation){return assertRegisteredVectorStoreAdapter(activation.registrations,expectation)},assertImporter(expectation){return assertRegisteredImporter(activation.registrations,expectation)},assertExporter(expectation){return assertRegisteredExporter(activation.registrations,expectation)},assertMigration(expectation){return assertRegisteredMigration(activation.registrations,expectation)},assertCapabilityUsage(expectation){return assertExtensionCapabilityUsage(activation,expectation)},runCommand(runOptions){return runRegisteredCommandForTest(activation.commands,runOptions)},runHook(runOptions){return runRegisteredHookForTest(activation.hooks,runOptions)},runMutationGuard(runOptions){return runRegisteredMutationGuardForTest(activation.hooks,runOptions)},runCommandOverride(context){return runRegisteredCommandOverrideForTest(activation.commands,context)},runParserOverride(context){return runRegisteredParserOverrideForTest(activation.parsers,context)},runPreflightOverride(context){return runRegisteredPreflightOverrideForTest(activation.preflight,context)},runRendererOverride(context){return runRegisteredRendererOverrideForTest(activation.renderers,context)},runServiceOverride(context){return runRegisteredServiceOverrideForTest(activation.services,context)},runSearchProvider(runOptions){return runRegisteredSearchProviderForTest(activation.registrations,runOptions)},runVectorStoreAdapter(runOptions){return runRegisteredVectorStoreAdapterForTest(activation.registrations,runOptions)},runMigration(runOptions){return runRegisteredMigrationForTest(activation.registrations,runOptions)},runImporter(runOptions){return runRegisteredImporterForTest(activation,runOptions)},runExporter(runOptions){return runRegisteredExporterForTest(activation,runOptions)},deactivate(deactivateOptions={}){return deactivateExtensionForTest(module,{name,layer,activation,deactivateTimeoutMs:deactivateOptions.deactivateTimeoutMs})}}}export{activateExtensionForTest,assertExtensionBlueprint,assertExtensionCapabilityUsage,assertExtensionDeactivated,assertExtensionManifestCompatible,assertExtensionManifestMatchesBlueprint,assertExtensionPreflight,assertPackageManifest,assertProjectProfile,assertRegisteredCommandContract,assertRegisteredCommandOverride,assertRegisteredExporter,assertRegisteredFlags,assertRegisteredHook,assertRegisteredImporter,assertRegisteredItemField,assertRegisteredItemType,assertRegisteredMigration,assertRegisteredParserOverride,assertRegisteredPreflightOverride,assertRegisteredProfile,assertRegisteredRendererOverride,assertRegisteredSearchProvider,assertRegisteredServiceOverride,assertRegisteredVectorStoreAdapter,createExtensionTestHarness,deactivateExtensionForTest,describeExtensionActivation,describeExtensionBlueprint,lintExtensionBlueprint,renderExtensionSurfaceMarkdown,runRegisteredCommandForTest,runRegisteredCommandOverrideForTest,runRegisteredExporterForTest,runRegisteredHookForTest,runRegisteredImporterForTest,runRegisteredMigrationForTest,runRegisteredMutationGuardForTest,runRegisteredParserOverrideForTest,runRegisteredPreflightOverrideForTest,runRegisteredRendererOverrideForTest,runRegisteredSearchProviderForTest,runRegisteredServiceOverrideForTest,runRegisteredVectorStoreAdapterForTest};
1391
5
  //# sourceMappingURL=testing.js.map
1392
- //# debugId=a09f5e0d-fccc-5a22-a6f3-0a37cbeea3b0