@pie-players/pie-assessment-toolkit 0.3.73 → 0.3.75

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 (274) hide show
  1. package/README.md +469 -318
  2. package/dist/attempt/AssessmentSession.d.ts +1 -5
  3. package/dist/attempt/AssessmentSession.js +0 -29
  4. package/dist/attempt/TestSession.d.ts +1 -20
  5. package/dist/attempt/TestSession.js +8 -99
  6. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  7. package/dist/components/ItemScope.custom-element.js +1 -0
  8. package/dist/components/ItemToolBar.custom-element.d.ts +1 -0
  9. package/dist/components/ItemToolBar.custom-element.js +1 -1
  10. package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
  11. package/dist/components/PieAssessmentToolkit.custom-element.js +13 -13
  12. package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
  13. package/dist/components/SectionToolBar.custom-element.js +1 -1
  14. package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
  15. package/dist/components/chunks/ItemToolBar-97hx60z4.js +2 -0
  16. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
  17. package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
  18. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
  19. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  20. package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
  21. package/dist/components/item-scope-element.d.ts +2 -0
  22. package/dist/components/item-scope-element.js +2 -0
  23. package/dist/context/assessment-toolkit-context.d.ts +32 -6
  24. package/dist/index.d.ts +23 -48
  25. package/dist/index.js +12 -37
  26. package/dist/policy/core/PolicySource.d.ts +1 -1
  27. package/dist/policy/core/ToolPolicyEngine.d.ts +85 -34
  28. package/dist/policy/core/ToolPolicyEngine.js +148 -46
  29. package/dist/policy/core/compose-decision.d.ts +28 -7
  30. package/dist/policy/core/compose-decision.js +75 -54
  31. package/dist/policy/core/decision-types.d.ts +65 -34
  32. package/dist/policy/core/feature-decision.d.ts +16 -9
  33. package/dist/policy/core/feature-decision.js +14 -8
  34. package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
  35. package/dist/policy/core/pnp-policy-inputs.js +28 -56
  36. package/dist/policy/core/provenance.d.ts +9 -4
  37. package/dist/policy/core/provenance.js +1 -1
  38. package/dist/policy/engine.d.ts +7 -23
  39. package/dist/policy/engine.js +5 -20
  40. package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
  41. package/dist/policy/sources/PnpPolicySource.js +117 -118
  42. package/dist/runtime/SectionControllerBinding.d.ts +279 -0
  43. package/dist/runtime/SectionControllerBinding.js +450 -0
  44. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  45. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  46. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  47. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  48. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  49. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  50. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
  51. package/dist/runtime/adapter/subscriber-fanout.js +1 -2
  52. package/dist/runtime/catalog-context.d.ts +19 -0
  53. package/dist/runtime/catalog-context.js +28 -0
  54. package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
  55. package/dist/runtime/composition-emit-scheduler.js +9 -15
  56. package/dist/runtime/content-language.d.ts +32 -0
  57. package/dist/runtime/content-language.js +46 -0
  58. package/dist/runtime/content-region.d.ts +17 -0
  59. package/dist/runtime/content-region.js +29 -0
  60. package/dist/runtime/content-registration.d.ts +13 -0
  61. package/dist/runtime/content-registration.js +25 -0
  62. package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
  63. package/dist/runtime/core/SectionEngineCore.js +2 -2
  64. package/dist/runtime/core/cohort.d.ts +1 -1
  65. package/dist/runtime/core/cohort.js +1 -1
  66. package/dist/runtime/core/engine-input.d.ts +18 -30
  67. package/dist/runtime/core/engine-input.js +11 -12
  68. package/dist/runtime/core/engine-output.d.ts +16 -40
  69. package/dist/runtime/core/engine-output.js +9 -21
  70. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  71. package/dist/runtime/core/engine-readiness.js +8 -41
  72. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  73. package/dist/runtime/core/engine-resolver.js +18 -43
  74. package/dist/runtime/core/engine-state.d.ts +28 -36
  75. package/dist/runtime/core/engine-state.js +18 -21
  76. package/dist/runtime/core/engine-transition.d.ts +14 -19
  77. package/dist/runtime/core/engine-transition.js +71 -51
  78. package/dist/runtime/engine.d.ts +16 -19
  79. package/dist/runtime/engine.js +12 -19
  80. package/dist/runtime/page-warnings.d.ts +13 -0
  81. package/dist/runtime/page-warnings.js +22 -0
  82. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  83. package/dist/runtime/policy-input-forwarding.js +25 -0
  84. package/dist/runtime/registration-events.d.ts +14 -0
  85. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  86. package/dist/runtime/runtime-event-claim.js +15 -0
  87. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  88. package/dist/runtime/shell-event-bridge.js +127 -0
  89. package/dist/runtime/shell-registration.d.ts +73 -0
  90. package/dist/runtime/shell-registration.js +95 -0
  91. package/dist/runtime/shell-scope.d.ts +68 -0
  92. package/dist/runtime/shell-scope.js +188 -0
  93. package/dist/runtime/tool-host-contract.d.ts +28 -10
  94. package/dist/runtime/tool-host-contract.js +31 -10
  95. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  96. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  97. package/dist/services/ElementToolStateStore.d.ts +14 -10
  98. package/dist/services/ElementToolStateStore.js +37 -13
  99. package/dist/services/HighlightCoordinator.d.ts +22 -28
  100. package/dist/services/HighlightCoordinator.js +431 -327
  101. package/dist/services/RangeSerializer.d.ts +10 -1
  102. package/dist/services/RangeSerializer.js +87 -14
  103. package/dist/services/TTSService.d.ts +130 -69
  104. package/dist/services/TTSService.js +713 -570
  105. package/dist/services/ToolCoordinator.d.ts +43 -66
  106. package/dist/services/ToolCoordinator.js +164 -208
  107. package/dist/services/ToolRegistry.d.ts +78 -54
  108. package/dist/services/ToolRegistry.js +107 -105
  109. package/dist/services/ToolkitCoordinator.d.ts +261 -175
  110. package/dist/services/ToolkitCoordinator.js +667 -433
  111. package/dist/services/audio-handoff.d.ts +6 -7
  112. package/dist/services/audio-handoff.js +20 -14
  113. package/dist/services/catalog-owner.d.ts +11 -1
  114. package/dist/services/catalog-owner.js +25 -0
  115. package/dist/services/create-tools-config.d.ts +2 -2
  116. package/dist/services/framework-error-bus.d.ts +3 -6
  117. package/dist/services/framework-error-bus.js +3 -6
  118. package/dist/services/framework-error.d.ts +30 -8
  119. package/dist/services/framework-error.js +9 -7
  120. package/dist/services/interfaces.d.ts +158 -129
  121. package/dist/services/section-controller-types.d.ts +15 -0
  122. package/dist/services/section-session-assignment.d.ts +21 -0
  123. package/dist/services/section-session-assignment.js +82 -0
  124. package/dist/services/spoken-audio-cards.d.ts +0 -2
  125. package/dist/services/spoken-audio-cards.js +1 -3
  126. package/dist/services/tool-config-defaults.d.ts +3 -3
  127. package/dist/services/tool-config-defaults.js +3 -3
  128. package/dist/services/tool-config-validation.d.ts +14 -4
  129. package/dist/services/tool-config-validation.js +63 -71
  130. package/dist/services/tool-context.d.ts +0 -8
  131. package/dist/services/tool-context.js +29 -25
  132. package/dist/services/tool-icons.d.ts +4 -3
  133. package/dist/services/tool-icons.js +4 -3
  134. package/dist/services/tool-instance-id.d.ts +0 -1
  135. package/dist/services/tool-instance-id.js +0 -6
  136. package/dist/services/tool-providers/TTSToolProvider.d.ts +41 -80
  137. package/dist/services/tool-providers/TTSToolProvider.js +81 -40
  138. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
  139. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +32 -28
  140. package/dist/services/tool-providers/ToolProviderRegistry.js +121 -107
  141. package/dist/services/tool-providers/index.d.ts +0 -9
  142. package/dist/services/tool-providers/index.js +2 -4
  143. package/dist/services/tool-request.d.ts +27 -12
  144. package/dist/services/tool-request.js +24 -14
  145. package/dist/services/toolbar-decided-tools.d.ts +15 -0
  146. package/dist/services/toolbar-decided-tools.js +28 -0
  147. package/dist/services/tools-config-normalizer.d.ts +37 -23
  148. package/dist/services/tools-config-normalizer.js +2 -10
  149. package/dist/services/tts/browser-provider.d.ts +38 -2
  150. package/dist/services/tts/browser-provider.js +276 -140
  151. package/dist/services/tts/catalog-span-alignment.d.ts +2 -17
  152. package/dist/services/tts/catalog-span-alignment.js +17 -142
  153. package/dist/services/tts/flat-tree.d.ts +71 -0
  154. package/dist/services/tts/flat-tree.js +269 -0
  155. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  156. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  157. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  158. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  159. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  160. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  161. package/dist/services/tts/generated-speech/types.d.ts +0 -12
  162. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
  163. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +38 -5
  164. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
  165. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  166. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  167. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  168. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  169. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  170. package/dist/services/tts/math-alignment/index.d.ts +0 -5
  171. package/dist/services/tts/math-alignment/index.js +5 -25
  172. package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
  173. package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
  174. package/dist/services/tts/math-alignment/types.d.ts +4 -2
  175. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  176. package/dist/services/tts/math-aware-text-processing.js +74 -18
  177. package/dist/services/tts/math-control-names.d.ts +21 -0
  178. package/dist/services/tts/math-control-names.js +142 -0
  179. package/dist/services/tts/math-speech.d.ts +16 -1
  180. package/dist/services/tts/math-speech.js +55 -19
  181. package/dist/services/tts/provider-options.d.ts +41 -0
  182. package/dist/services/tts/provider-options.js +1 -0
  183. package/dist/services/tts/sre-engine.d.ts +7 -0
  184. package/dist/services/tts/sre-engine.js +11 -0
  185. package/dist/services/tts/sre-locales.d.ts +14 -0
  186. package/dist/services/tts/sre-locales.js +57 -0
  187. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  188. package/dist/services/tts/ssml/spoken-text.js +29 -0
  189. package/dist/services/tts/start-failure.d.ts +11 -0
  190. package/dist/services/tts/start-failure.js +19 -0
  191. package/dist/services/tts/text-processing.d.ts +9 -1
  192. package/dist/services/tts/text-processing.js +46 -34
  193. package/dist/services/tts/text-segmentation.d.ts +7 -0
  194. package/dist/services/tts/text-segmentation.js +47 -0
  195. package/dist/services/tts-runtime-config.d.ts +44 -18
  196. package/dist/services/tts-runtime-config.js +22 -93
  197. package/dist/tools/content-capability-resolution.d.ts +7 -9
  198. package/dist/tools/content-capability-resolution.js +8 -45
  199. package/dist/tools/registration.d.ts +58 -0
  200. package/dist/tools/registration.js +54 -0
  201. package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
  202. package/dist/tools/tool-surface-host.d.ts +6 -10
  203. package/dist/tools/tool-surface-host.js +44 -42
  204. package/dist/tools/tool-tag-map.d.ts +5 -15
  205. package/dist/tools/tool-tag-map.js +6 -20
  206. package/dist/utils/structural-equality.d.ts +11 -0
  207. package/dist/utils/structural-equality.js +47 -0
  208. package/package.json +24 -59
  209. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  210. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  211. package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
  212. package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
  213. package/dist/context/runtime-context-consumer.d.ts +0 -13
  214. package/dist/context/runtime-context-consumer.js +0 -51
  215. package/dist/item-loader.d.ts +0 -44
  216. package/dist/item-loader.js +0 -61
  217. package/dist/policy/core/engine-context.d.ts +0 -29
  218. package/dist/policy/core/engine-context.js +0 -27
  219. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  220. package/dist/policy/core/policy-source-tag.js +0 -28
  221. package/dist/policy/internal.d.ts +0 -25
  222. package/dist/policy/internal.js +0 -25
  223. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  224. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  225. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  226. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  227. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  228. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  229. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  230. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  231. package/dist/runtime/engine-context.d.ts +0 -39
  232. package/dist/runtime/engine-context.js +0 -31
  233. package/dist/runtime/internal.d.ts +0 -43
  234. package/dist/runtime/internal.js +0 -45
  235. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -80
  236. package/dist/runtime/section-runtime-engine-host-context.js +0 -95
  237. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  238. package/dist/runtime/stage-emit-gate.js +0 -82
  239. package/dist/services/I18nService.d.ts +0 -47
  240. package/dist/services/I18nService.js +0 -84
  241. package/dist/services/ThemeProvider.d.ts +0 -95
  242. package/dist/services/ThemeProvider.js +0 -334
  243. package/dist/services/ToolConfigResolver.d.ts +0 -161
  244. package/dist/services/ToolConfigResolver.js +0 -229
  245. package/dist/services/catalog-media.d.ts +0 -56
  246. package/dist/services/catalog-media.js +0 -190
  247. package/dist/services/createDefaultToolRegistry.d.ts +0 -53
  248. package/dist/services/createDefaultToolRegistry.js +0 -45
  249. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  250. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  251. package/dist/services/pnp-standard-features.d.ts +0 -238
  252. package/dist/services/pnp-standard-features.js +0 -338
  253. package/dist/services/tool-providers/CortexToolProvider.d.ts +0 -13
  254. package/dist/services/tool-providers/CortexToolProvider.js +0 -32
  255. package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
  256. package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
  257. package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
  258. package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
  259. package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
  260. package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
  261. package/dist/services/tts/visual-line-ranges.d.ts +0 -18
  262. package/dist/services/tts/visual-line-ranges.js +0 -226
  263. package/dist/tools/client.d.ts +0 -12
  264. package/dist/tools/client.js +0 -14
  265. package/dist/tools/index.d.ts +0 -8
  266. package/dist/tools/index.js +0 -10
  267. package/dist/tools/internal.d.ts +0 -43
  268. package/dist/tools/internal.js +0 -41
  269. package/dist/tools/response-discovery.d.ts +0 -61
  270. package/dist/tools/response-discovery.js +0 -113
  271. package/dist/tools/types.d.ts +0 -337
  272. package/dist/tools/types.js +0 -12
  273. package/dist/tools/variant-resolver.d.ts +0 -41
  274. package/dist/tools/variant-resolver.js +0 -207
@@ -5,7 +5,8 @@
5
5
  * and button/instance creation. Supports dynamic registration and override by integrators.
6
6
  */
7
7
  import { dynamicMessageKey } from "@pie-players/pie-players-shared/i18n/provider";
8
- import { normalizeToolAlias } from "./tools-config-normalizer.js";
8
+ import { resolveToolTag, } from "../tools/tool-tag-map.js";
9
+ import { PENDING_INPUT_WARNING_DELAY_MS, warnOncePerDocument, } from "../runtime/page-warnings.js";
9
10
  /**
10
11
  * A registration's display name in the interface locale.
11
12
  *
@@ -13,16 +14,12 @@ import { normalizeToolAlias } from "./tools-config-normalizer.js";
13
14
  * falls back to `name` rather than rendering the key, so a catalog gap degrades
14
15
  * to English instead of to `tools.something.name` on a toolbar button.
15
16
  *
16
- * Both display resolvers live here so the toolbars, the settings panels and the
17
- * PNP debugger cannot each invent their own precedence.
17
+ * It lives here so the toolbars, the settings panels and the PNP debugger
18
+ * cannot each invent their own precedence.
18
19
  */
19
20
  export function resolveToolRegistrationName(registration, i18n) {
20
21
  return resolveKeyedString(registration.name, registration.nameKey, i18n);
21
22
  }
22
- /** A registration's description in the interface locale. See the name resolver. */
23
- export function resolveToolRegistrationDescription(registration, i18n) {
24
- return resolveKeyedString(registration.description, registration.descriptionKey, i18n);
25
- }
26
23
  function resolveKeyedString(source, key, i18n) {
27
24
  if (!key || !i18n)
28
25
  return source;
@@ -50,7 +47,7 @@ function assertNonEmptyString(value, fieldName) {
50
47
  }
51
48
  // Defence-in-depth: reject obvious XSS payloads in tool-registered icon
52
49
  // markup at registration time. Runtime rendering still runs each icon
53
- // through DOMPurify (see `ToolIcon.svelte`), but surfacing the problem
50
+ // through DOMPurify (`sanitizeSvgIcon` in `ItemToolBar.svelte`), but surfacing the problem
54
51
  // early produces a clearer error for tool authors than "the icon silently
55
52
  // disappeared after sanitization".
56
53
  const SCRIPTABLE_ICON_PATTERNS = [
@@ -151,11 +148,6 @@ function assertToolRegistrationShape(registration) {
151
148
  registration.singletonScope !== "section") {
152
149
  throw new Error(`Invalid tool registration "${registration.toolId}": selection-gateway tools must declare singletonScope "section".`);
153
150
  }
154
- if (registration.pnpSupportIds !== undefined &&
155
- (!Array.isArray(registration.pnpSupportIds) ||
156
- registration.pnpSupportIds.some((pnpId) => typeof pnpId !== "string" || pnpId.trim().length === 0))) {
157
- throw new Error(`Invalid tool registration "${registration.toolId}": "pnpSupportIds" must be an array of non-empty strings.`);
158
- }
159
151
  if (registration.activation !== "region" &&
160
152
  typeof registration.isVisibleInContext !== "function") {
161
153
  throw new Error(`Invalid tool registration "${registration.toolId}": "isVisibleInContext" must be a function.`);
@@ -174,12 +166,6 @@ function assertToolRegistrationShape(registration) {
174
166
  typeof registration.requiresAuthoredContent.resolve !== "function") {
175
167
  throw new Error(`Invalid tool registration "${registration.toolId}": "requiresAuthoredContent" must be an object with a "resolve" function.`);
176
168
  }
177
- if (!registration.pnpSupportIds?.length) {
178
- // A content dependency's second job is keeping the capability out of a
179
- // wholesale grant, and a host filters that by support id. Declaring one
180
- // with no id to filter on would silently drop that guarantee.
181
- throw new Error(`Invalid tool registration "${registration.toolId}": "requiresAuthoredContent" requires at least one entry in "pnpSupportIds", which is what a host filters a default grant list on.`);
182
- }
183
169
  }
184
170
  if (registration.renderToolbar !== undefined) {
185
171
  if (typeof registration.renderToolbar !== "function") {
@@ -197,10 +183,12 @@ function assertToolRegistrationShape(registration) {
197
183
  */
198
184
  export class ToolRegistry {
199
185
  tools = new Map();
200
- pnpIndex = new Map(); // pnpSupportId → Set<toolId>
201
186
  componentOverrides = {};
187
+ watchedUndefinedToolElements = new Set();
202
188
  moduleLoaders = new Map();
203
189
  loadedToolModules = new Set();
190
+ warnedLoaderReplacements = new Set();
191
+ warnedUnregisteredAllowedTools = new Set();
204
192
  moduleLoadPromises = new Map();
205
193
  changeListeners = new Set();
206
194
  emitChange(event) {
@@ -228,13 +216,13 @@ export class ToolRegistry {
228
216
  };
229
217
  }
230
218
  /**
231
- * Normalize a single tool alias to canonical toolId.
219
+ * Normalize a single tool id (trims surrounding whitespace).
232
220
  */
233
221
  normalizeToolId(toolId) {
234
- return normalizeToolAlias(toolId);
222
+ return toolId.trim();
235
223
  }
236
224
  /**
237
- * Normalize a list of tool aliases to canonical toolIds.
225
+ * Normalize a list of tool ids (trims surrounding whitespace).
238
226
  */
239
227
  normalizeToolIds(toolIds) {
240
228
  return toolIds.map((toolId) => this.normalizeToolId(toolId));
@@ -251,15 +239,6 @@ export class ToolRegistry {
251
239
  throw new Error(`Tool '${registration.toolId}' is already registered`);
252
240
  }
253
241
  this.tools.set(registration.toolId, registration);
254
- // Index PNP support IDs
255
- if (registration.pnpSupportIds) {
256
- for (const pnpId of registration.pnpSupportIds) {
257
- if (!this.pnpIndex.has(pnpId)) {
258
- this.pnpIndex.set(pnpId, new Set());
259
- }
260
- this.pnpIndex.get(pnpId).add(registration.toolId);
261
- }
262
- }
263
242
  this.emitChange({ kind: "register", toolIds: [registration.toolId] });
264
243
  }
265
244
  /**
@@ -272,24 +251,7 @@ export class ToolRegistry {
272
251
  if (!this.tools.has(registration.toolId)) {
273
252
  throw new Error(`Cannot override non-existent tool '${registration.toolId}'`);
274
253
  }
275
- // Remove old PNP index entries
276
- const oldReg = this.tools.get(registration.toolId);
277
- if (oldReg.pnpSupportIds) {
278
- for (const pnpId of oldReg.pnpSupportIds) {
279
- this.pnpIndex.get(pnpId)?.delete(registration.toolId);
280
- }
281
- }
282
- // Add new registration
283
254
  this.tools.set(registration.toolId, registration);
284
- // Re-index PNP support IDs
285
- if (registration.pnpSupportIds) {
286
- for (const pnpId of registration.pnpSupportIds) {
287
- if (!this.pnpIndex.has(pnpId)) {
288
- this.pnpIndex.set(pnpId, new Set());
289
- }
290
- this.pnpIndex.get(pnpId).add(registration.toolId);
291
- }
292
- }
293
255
  this.emitChange({ kind: "override", toolIds: [registration.toolId] });
294
256
  }
295
257
  /**
@@ -298,15 +260,8 @@ export class ToolRegistry {
298
260
  * @param toolId - Tool ID to remove
299
261
  */
300
262
  unregister(toolId) {
301
- const reg = this.tools.get(toolId);
302
- if (!reg)
263
+ if (!this.tools.has(toolId))
303
264
  return;
304
- // Remove PNP index entries
305
- if (reg.pnpSupportIds) {
306
- for (const pnpId of reg.pnpSupportIds) {
307
- this.pnpIndex.get(pnpId)?.delete(toolId);
308
- }
309
- }
310
265
  this.tools.delete(toolId);
311
266
  this.emitChange({ kind: "unregister", toolIds: [toolId] });
312
267
  }
@@ -344,15 +299,6 @@ export class ToolRegistry {
344
299
  getAllTools() {
345
300
  return Array.from(this.tools.values());
346
301
  }
347
- /**
348
- * Find tool IDs that support a given PNP support ID
349
- *
350
- * @param pnpSupportId - PNP support ID (e.g., 'calculator')
351
- * @returns Set of tool IDs that support this PNP ID
352
- */
353
- getToolsByPNPSupport(pnpSupportId) {
354
- return this.pnpIndex.get(pnpSupportId) || new Set();
355
- }
356
302
  /**
357
303
  * Get tools that support a specific level
358
304
  *
@@ -389,21 +335,18 @@ export class ToolRegistry {
389
335
  tool.surfaces?.includes(surface));
390
336
  }
391
337
  /**
392
- * Support ids belonging to capabilities that need authored content.
338
+ * Support ids, which are tool ids, of the capabilities that need authored
339
+ * content.
393
340
  *
394
- * What a host filters a default grant list on, in place of the compile-time
395
- * exclusion array this replaced: granting one of these wholesale grants an
396
- * accommodation to learners with no documented need for it.
341
+ * What a host filters a default grant list on: granting one of these
342
+ * wholesale grants an accommodation to learners with no documented need
343
+ * for it.
397
344
  */
398
345
  getContentDependentSupportIds() {
399
- const ids = new Set();
400
- for (const tool of this.getAllTools()) {
401
- if (!tool.requiresAuthoredContent)
402
- continue;
403
- for (const supportId of tool.pnpSupportIds || [])
404
- ids.add(supportId);
405
- }
406
- return [...ids].sort();
346
+ return this.getAllTools()
347
+ .filter((tool) => tool.requiresAuthoredContent)
348
+ .map((tool) => tool.toolId)
349
+ .sort();
407
350
  }
408
351
  /**
409
352
  * Filter tool IDs by activation type.
@@ -414,7 +357,7 @@ export class ToolRegistry {
414
357
  /**
415
358
  * Filter tools by visibility in a given context
416
359
  *
417
- * Pass 2 of the two-pass model: Given a list of allowed tool IDs (from Pass 1),
360
+ * Pass 2 of the three-pass model: Given a list of allowed tool IDs (from Pass 1),
418
361
  * ask each tool if it's relevant in this context.
419
362
  *
420
363
  * @param allowedToolIds - Tool IDs that passed Pass 1 (orchestrator approval)
@@ -426,7 +369,11 @@ export class ToolRegistry {
426
369
  for (const toolId of allowedToolIds) {
427
370
  const tool = this.get(toolId);
428
371
  if (!tool) {
429
- console.warn(`Tool '${toolId}' is allowed but not registered`);
372
+ // Toolbars filter on every render; one warning per id carries it.
373
+ if (!this.warnedUnregisteredAllowedTools.has(toolId)) {
374
+ this.warnedUnregisteredAllowedTools.add(toolId);
375
+ console.warn(`Tool '${toolId}' is allowed but not registered`);
376
+ }
430
377
  continue;
431
378
  }
432
379
  // Check if tool supports this level
@@ -480,14 +427,13 @@ export class ToolRegistry {
480
427
  * Get tool metadata for building UIs
481
428
  * Useful for building PNP configuration interfaces
482
429
  *
483
- * @returns Array of tool metadata (id, name, description, pnpSupportIds)
430
+ * @returns Array of tool metadata (id, name, description, levels, activation)
484
431
  */
485
432
  getToolMetadata() {
486
433
  return this.getAllTools().map((tool) => ({
487
434
  toolId: tool.toolId,
488
435
  name: tool.name,
489
436
  description: tool.description,
490
- pnpSupportIds: tool.pnpSupportIds || [],
491
437
  supportedLevels: tool.supportedLevels,
492
438
  activation: tool.activation || "toolbar-toggle",
493
439
  singletonScope: tool.singletonScope || null,
@@ -496,25 +442,6 @@ export class ToolRegistry {
496
442
  contentDependencyDescription: tool.requiresAuthoredContent?.description ?? null,
497
443
  }));
498
444
  }
499
- /**
500
- * Generate PNP support IDs from enabled tools
501
- * Useful for creating PNP profiles
502
- *
503
- * @param enabledToolIds - Tool IDs to enable
504
- * @returns Array of unique PNP support IDs
505
- */
506
- generatePNPSupportsFromTools(enabledToolIds) {
507
- const pnpSupports = new Set();
508
- for (const toolId of enabledToolIds) {
509
- const tool = this.get(toolId);
510
- if (tool?.pnpSupportIds) {
511
- for (const pnpId of tool.pnpSupportIds) {
512
- pnpSupports.add(pnpId);
513
- }
514
- }
515
- }
516
- return Array.from(pnpSupports);
517
- }
518
445
  /**
519
446
  * Clear all registrations (useful for testing)
520
447
  */
@@ -523,7 +450,6 @@ export class ToolRegistry {
523
450
  if (toolIds.length === 0)
524
451
  return;
525
452
  this.tools.clear();
526
- this.pnpIndex.clear();
527
453
  this.emitChange({ kind: "clear", toolIds });
528
454
  }
529
455
  /**
@@ -538,6 +464,8 @@ export class ToolRegistry {
538
464
  /**
539
465
  * Register lazy module loaders by toolId.
540
466
  * Toolbars call ensureToolModuleLoaded(toolId) before instance creation.
467
+ * A tool whose module has loaded, or is loading, keeps its loader: its
468
+ * elements are already defined, so a replacement could never take effect.
541
469
  */
542
470
  setToolModuleLoaders(loaders) {
543
471
  const entries = Object.entries(loaders).filter((entry) => entry[1] !== undefined);
@@ -551,6 +479,11 @@ export class ToolRegistry {
551
479
  for (const [toolId, loader] of entries) {
552
480
  if (this.moduleLoaders.get(toolId) === loader)
553
481
  continue;
482
+ if (this.loadedToolModules.has(toolId) ||
483
+ this.moduleLoadPromises.has(toolId)) {
484
+ this.warnLoaderReplacedAfterLoad(toolId);
485
+ continue;
486
+ }
554
487
  this.moduleLoaders.set(toolId, loader);
555
488
  changedToolIds.push(toolId);
556
489
  }
@@ -558,6 +491,12 @@ export class ToolRegistry {
558
491
  this.emitChange({ kind: "module-loaders", toolIds: changedToolIds });
559
492
  }
560
493
  }
494
+ warnLoaderReplacedAfterLoad(toolId) {
495
+ if (this.warnedLoaderReplacements.has(toolId))
496
+ return;
497
+ this.warnedLoaderReplacements.add(toolId);
498
+ console.warn(`[ToolRegistry] Ignored a new module loader for "${toolId}": its module already loaded, so the elements it defines stay in place. Register loaders before the tool first renders.`);
499
+ }
561
500
  /**
562
501
  * Ensure tool module side-effects are loaded exactly once.
563
502
  * Safe to call repeatedly; concurrent callers share the same promise.
@@ -571,11 +510,14 @@ export class ToolRegistry {
571
510
  return;
572
511
  }
573
512
  const loader = this.moduleLoaders.get(toolId);
574
- if (!loader)
513
+ if (!loader) {
514
+ this.watchUndefinedToolElement(toolId);
575
515
  return;
516
+ }
576
517
  const loadPromise = (async () => {
577
518
  await loader();
578
519
  this.loadedToolModules.add(toolId);
520
+ this.warnIfLoadedToolElementUndefined(toolId);
579
521
  })();
580
522
  this.moduleLoadPromises.set(toolId, loadPromise);
581
523
  try {
@@ -586,10 +528,70 @@ export class ToolRegistry {
586
528
  }
587
529
  }
588
530
  /**
589
- * Ensure a set of tool modules are loaded.
531
+ * The undefined element tag a tool would render, or `null` when the tool is
532
+ * built by a component factory, its element is defined, or there is no DOM.
533
+ */
534
+ undefinedToolElementTag(toolId) {
535
+ if (typeof customElements === "undefined" || typeof document === "undefined") {
536
+ return null;
537
+ }
538
+ const overrides = this.componentOverrides;
539
+ if (overrides.toolComponentFactories?.[toolId])
540
+ return null;
541
+ let tagName;
542
+ try {
543
+ tagName = resolveToolTag(toolId, overrides);
544
+ }
545
+ catch {
546
+ // Element creation reports the missing tag mapping.
547
+ return null;
548
+ }
549
+ return customElements.get(tagName) ? null : tagName;
550
+ }
551
+ /**
552
+ * Warn when a tool's module loader finished without defining the element the
553
+ * tag map points the tool at, as when a host remaps the tag but keeps a
554
+ * loader that defines the packaged element. The tool otherwise renders blank.
555
+ */
556
+ warnIfLoadedToolElementUndefined(toolId) {
557
+ const tagName = this.undefinedToolElementTag(toolId);
558
+ if (!tagName)
559
+ return;
560
+ warnOncePerDocument(document, `loadedToolElementUndefined.${toolId}.${tagName}`, `[ToolRegistry] Tool "${toolId}" renders <${tagName}>, but its module loader finished without defining it, so the tool renders blank. The tag map and the loader disagree: map "${toolId}" to the tag its loader defines, or register a loader that defines <${tagName}>. Reported once per page.`);
561
+ }
562
+ /**
563
+ * Warn when a tool with no module loader still has no element definition
564
+ * after the pending-input delay. A registry built without loaders otherwise
565
+ * renders the tool as an unknown element with no error.
566
+ */
567
+ watchUndefinedToolElement(toolId) {
568
+ if (this.watchedUndefinedToolElements.has(toolId))
569
+ return;
570
+ const tagName = this.undefinedToolElementTag(toolId);
571
+ if (!tagName)
572
+ return;
573
+ this.watchedUndefinedToolElements.add(toolId);
574
+ const doc = document;
575
+ const timer = setTimeout(() => {
576
+ if (customElements.get(tagName))
577
+ return;
578
+ warnOncePerDocument(doc, `undefinedToolElement.${toolId}`, `[ToolRegistry] Tool "${toolId}" renders <${tagName}>, which is still undefined after ${PENDING_INPUT_WARNING_DELAY_MS / 1000} s, and its registry has no module loader for it. Pass toolModuleLoaders to createPackagedToolRegistry (DEFAULT_TOOL_MODULE_LOADERS from @pie-players/pie-default-tool-loaders loads the stock tools), register one with setToolModuleLoaders, or import the tool's package before it renders. Reported once per page.`);
579
+ }, PENDING_INPUT_WARNING_DELAY_MS);
580
+ void customElements.whenDefined(tagName).then(() => clearTimeout(timer));
581
+ }
582
+ /**
583
+ * Load a set of tool modules, each to completion, and resolve with the error
584
+ * of every tool whose module failed, by tool id. One failure leaves the other
585
+ * tools loaded and usable. A failed tool is retried on the next call.
590
586
  */
591
587
  async ensureToolModulesLoaded(toolIds) {
592
- await Promise.all(toolIds.map((toolId) => this.ensureToolModuleLoaded(toolId)));
588
+ const results = await Promise.allSettled(toolIds.map((toolId) => this.ensureToolModuleLoaded(toolId)));
589
+ const failures = new Map();
590
+ results.forEach((result, index) => {
591
+ if (result.status === "rejected")
592
+ failures.set(toolIds[index], result.reason);
593
+ });
594
+ return failures;
593
595
  }
594
596
  /**
595
597
  * Whether a tool module has already been loaded.