@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
@@ -12,7 +12,7 @@
12
12
  * `tests/policy/compose-decision.test.ts` lock the orchestration in.
13
13
  */
14
14
  import { normalizeToolList, } from "../../services/tools-config-normalizer.js";
15
- import { ToolPolicyProvenanceBuilder } from "./provenance.js";
15
+ import { ToolPolicyProvenanceBuilder, } from "./provenance.js";
16
16
  export function composeDecision(inputs) {
17
17
  const { request, tools, pnpPolicy, customSources, contextId } = inputs;
18
18
  const builder = new ToolPolicyProvenanceBuilder(contextId);
@@ -103,23 +103,16 @@ export function composeDecision(inputs) {
103
103
  });
104
104
  // Step 5 — PNP/profile policy gates.
105
105
  const diagnostics = [];
106
- const sourcesByTool = new Map();
107
- for (const toolId of candidates) {
108
- sourcesByTool.set(toolId, ["placement"]);
109
- }
110
- // Snapshot the post-host candidate set so step 5b can distinguish
111
- // "host removed a required tool" (requiredToolBlocked fires)
112
- // from "profile policy's own precedence removed a required tool" (fix
113
- // per M8 PR 1 R1 S1 — multi-rule conflict on the same tool must
114
- // not blame the host).
106
+ // Snapshot the post-host candidate set so step 5b reports
107
+ // requiredToolBlocked only for a mandated tool the host removed.
115
108
  const postHostCandidates = new Set(candidates);
116
109
  let pnpPolicyResult = null;
117
110
  if (pnpPolicy.enforcement === "on" &&
118
111
  pnpPolicy.source &&
119
- (pnpPolicy.assessment || pnpPolicy.currentItemRef)) {
112
+ (pnpPolicy.assessment || pnpPolicy.item)) {
120
113
  pnpPolicyResult = pnpPolicy.source.apply({
121
114
  assessment: pnpPolicy.assessment,
122
- currentItemRef: pnpPolicy.currentItemRef,
115
+ item: pnpPolicy.item,
123
116
  });
124
117
  if (pnpPolicyResult.sources.assessment) {
125
118
  builder.addSource("assessment", pnpPolicyResult.sources.assessment);
@@ -141,29 +134,19 @@ export function composeDecision(inputs) {
141
134
  value: decision.value,
142
135
  });
143
136
  }
144
- // A support id no registration claims resolves to itself and then matches
145
- // nothing placed, so the capability is absent with no trace of why — the
146
- // failure a host sending its own vocabulary instead of an AfA/QTI feature
147
- // id actually hits. Reported per decision rather than at config time
148
- // because the ids arrive with the profile, not with the tools config.
149
- for (const supportId of pnpPolicyResult.unmappedSupportIds) {
150
- diagnostics.push({
151
- code: "tool-policy.unknownSupportId",
152
- level: request.level,
153
- toolId: supportId,
154
- message: `No registered tool claims PNP support id "${supportId}"; it was carried through as a feature id and matched nothing. Expected an AfA 3.0 / QTI 3.0 access feature id.`,
155
- source: "pnp.pnp-support",
156
- });
137
+ // An id no tool is registered under matches nothing placed, so whatever it
138
+ // names is absent with no trace of why. Reported per decision because the
139
+ // ids arrive with the profile and settings, after the tools config.
140
+ for (const [supportId, origins] of pnpPolicyResult.unmappedSupportIds) {
141
+ diagnostics.push(unknownSupportIdDiagnostic(supportId, origins, request.level));
142
+ }
143
+ for (const [toolId, rule] of pnpPolicyResult.blockedOverrides) {
144
+ diagnostics.push(overrideBlockedDiagnostic(toolId, rule, request.level));
157
145
  }
158
146
  // 5a — remove PNP/profile-blocked tools from the candidate set.
159
147
  candidates = candidates.filter((toolId) => {
160
148
  return !pnpPolicyResult.blockedToolIds.has(toolId);
161
149
  });
162
- for (const toolId of Array.from(sourcesByTool.keys())) {
163
- if (pnpPolicyResult.blockedToolIds.has(toolId)) {
164
- sourcesByTool.delete(toolId);
165
- }
166
- }
167
150
  // 5b — surface requiredToolBlocked diagnostics ONLY for
168
151
  // PNP/profile-mandated tools that the host removed (i.e., the tool was
169
152
  // NOT in `postHostCandidates`). Mandated tools removed by
@@ -211,15 +194,6 @@ export function composeDecision(inputs) {
211
194
  value: details,
212
195
  });
213
196
  }
214
- // 5c — attach PNP policy source tags to surviving entries.
215
- for (const toolId of candidates) {
216
- const flag = pnpPolicyResult.perToolFlags.get(toolId);
217
- if (flag) {
218
- const tags = sourcesByTool.get(toolId) ?? [];
219
- tags.push(`pnp.${flag.rule}`);
220
- sourcesByTool.set(toolId, tags);
221
- }
222
- }
223
197
  }
224
198
  // Step 6 — Custom PolicySources.
225
199
  for (let i = 0; i < customSources.length; i++) {
@@ -238,7 +212,7 @@ export function composeDecision(inputs) {
238
212
  level: request.level,
239
213
  toolId,
240
214
  message: `Custom source "${source.id}" tried to add tool "${toolId}" that was not in candidates; ignored.`,
241
- source: `custom.${source.id}`,
215
+ details: { customSourceId: source.id },
242
216
  });
243
217
  refinedSet.delete(toolId);
244
218
  }
@@ -250,7 +224,7 @@ export function composeDecision(inputs) {
250
224
  // this auto-log, the provenance trail for a silently-removed
251
225
  // tool would show only the prior `enable` decisions and the
252
226
  // final-state reconciliation would have no evidence to mark it
253
- // `blocked` (M8 PR 1 R2 S1 / M1 case B).
227
+ // `blocked`.
254
228
  const explicitBlocks = new Set();
255
229
  if (result.decisions) {
256
230
  for (const event of result.decisions) {
@@ -284,18 +258,27 @@ export function composeDecision(inputs) {
284
258
  value: { customSourceId: source.id },
285
259
  });
286
260
  }
287
- // Annotate surviving entries with `custom.<id>` so hosts can see
288
- // which custom source kept them in.
289
- for (const toolId of candidates) {
290
- const tags = sourcesByTool.get(toolId) ?? ["placement"];
291
- tags.push(`custom.${source.id}`);
292
- sourcesByTool.set(toolId, tags);
293
- }
294
- // And drop attribution for IDs the source removed.
295
- for (const toolId of before) {
296
- if (!refinedSet.has(toolId)) {
297
- sourcesByTool.delete(toolId);
298
- }
261
+ }
262
+ // Step 7 — item settings a shared toolbar does not apply. Placement is the
263
+ // host's section-wide choice, so the tool stays; the diagnostic says where to
264
+ // place it for the item setting to hold.
265
+ for (const toolId of candidates) {
266
+ for (const item of inputs.unappliedItems ?? []) {
267
+ const settings = ["restrictedTools", "requiredTools"].filter((key) => item.settings[key]?.includes(toolId) === true);
268
+ if (settings.length === 0)
269
+ continue;
270
+ const details = {
271
+ itemId: item.id,
272
+ settings,
273
+ toolbarLevel: request.scope.level,
274
+ };
275
+ diagnostics.push({
276
+ code: "tool-policy.itemSettingNotApplied",
277
+ level: request.level,
278
+ toolId,
279
+ message: `Item "${item.id}" names "${toolId}" in ${settings.join(" and ")}, but "${toolId}" is on the ${request.scope.level}-level toolbar, which item settings do not reach. Place "${toolId}" at item level to enforce the setting per item.`,
280
+ details,
281
+ });
299
282
  }
300
283
  }
301
284
  const survivingIds = new Set(candidates);
@@ -306,7 +289,6 @@ export function composeDecision(inputs) {
306
289
  required: flag?.required ?? false,
307
290
  alwaysAvailable: flag?.alwaysAvailable ?? false,
308
291
  settings: flag?.settings,
309
- sources: sourcesByTool.get(toolId) ?? ["placement"],
310
292
  };
311
293
  });
312
294
  builder.reconcileFinalStates(survivingIds);
@@ -346,3 +328,42 @@ function detectHostRemovalGate(toolId, args) {
346
328
  // pipeline has a bug; surface a best-effort rather than throwing.
347
329
  return { hostRule: "placement-missing", hostValue: args.placement };
348
330
  }
331
+ /** Where each PNP/profile rule's ids are authored. */
332
+ const PNP_RULE_FIELDS = {
333
+ "district-block": "settings.districtPolicy.blockedTools",
334
+ "test-admin-override": "settings.testAdministration.toolOverrides",
335
+ "item-restriction": "item settings.restrictedTools",
336
+ "pnp-prohibited": "personalNeedsProfile.prohibitedSupports",
337
+ "item-requirement": "item settings.requiredTools",
338
+ "district-requirement": "settings.districtPolicy.requiredTools",
339
+ "pnp-support": "personalNeedsProfile.supports",
340
+ };
341
+ /**
342
+ * A `tool-policy.overrideBlocked` diagnostic for a tool a `true` test
343
+ * administration override grants and `rule` withdraws. `level` is absent on a
344
+ * feature decision, which has no toolbar level.
345
+ */
346
+ export function overrideBlockedDiagnostic(toolId, rule, level) {
347
+ return {
348
+ code: "tool-policy.overrideBlocked",
349
+ ...(level ? { level } : {}),
350
+ toolId,
351
+ message: `settings.testAdministration.toolOverrides grants "${toolId}", but ${PNP_RULE_FIELDS[rule]} withdraws it and outranks a granting override.`,
352
+ details: { rule },
353
+ };
354
+ }
355
+ /**
356
+ * A `tool-policy.unknownSupportId` diagnostic for an id the registry lacks,
357
+ * attributed to the highest-precedence rule naming it. `level` is absent on a
358
+ * feature decision, which has no toolbar level.
359
+ */
360
+ export function unknownSupportIdDiagnostic(supportId, origins, level) {
361
+ const fields = origins.map((rule) => PNP_RULE_FIELDS[rule]).join(", ");
362
+ return {
363
+ code: "tool-policy.unknownSupportId",
364
+ ...(level ? { level } : {}),
365
+ toolId: supportId,
366
+ message: `No tool is registered under "${supportId}", named in ${fields}, so it matches nothing. These lists name tools by tool id.`,
367
+ details: { origins: [...origins] },
368
+ };
369
+ }
@@ -11,16 +11,15 @@
11
11
  */
12
12
  import type { ToolPlacementLevel } from "../../services/tools-config-normalizer.js";
13
13
  import type { ToolContext, ToolLevel } from "../../services/tool-context.js";
14
- import type { PolicySourceTag } from "./policy-source-tag.js";
15
- import type { ToolPolicyProvenance } from "./provenance.js";
14
+ import type { PnpPolicySourceRule, ToolPolicyProvenance } from "./provenance.js";
16
15
  /**
17
16
  * Scope identifier for a `decide(...)` request. The engine uses
18
17
  * `level` to pick which placement bucket to read; `scopeId` is opaque
19
18
  * to the engine (it is round-tripped into provenance for human-readable
20
19
  * trails) and is typically the section-id, item-id, or passage-id of
21
20
  * the surface asking the question. `contentKind` mirrors the
22
- * `ToolbarContext.scope.contentKind` field already in use by
23
- * `<pie-item-toolbar>` Pass-2 visibility.
21
+ * `ToolbarContext.scope.contentKind` field `<pie-item-toolbar>` reads for
22
+ * content relevance.
24
23
  */
25
24
  export interface ToolScope {
26
25
  level: ToolLevel;
@@ -41,29 +40,44 @@ export interface ToolPolicyDecisionRequest {
41
40
  level: ToolPlacementLevel;
42
41
  scope: ToolScope;
43
42
  /**
44
- * Optional Pass-2 context. The engine itself does NOT call
45
- * `isVisibleInContext(...)`; that gate is intentionally applied
46
- * at the toolbar boundary (Pass-2 = relevance, Pass-1 = the
47
- * engine's job). The context is forwarded to custom
48
- * `PolicySource` instances so they can refine the candidate set
49
- * if they need to.
43
+ * Optional tool context. The engine does not call
44
+ * `isVisibleInContext(...)`: content relevance is applied at the toolbar
45
+ * boundary. The context is forwarded to custom `PolicySource` instances so
46
+ * they can refine the candidate set.
50
47
  */
51
48
  context?: ToolContext;
52
49
  }
53
50
  /**
54
- * Diagnostic codes the engine may emit alongside a decision. The
55
- * canonical case in M8 is `tool-policy.requiredToolBlocked` — fired
56
- * when a host policy gate (`policy.blocked`, missing-from-`placement`,
57
- * provider veto) removed a tool that profile policy `requiredTools` /
58
- * `district.requiredTools` mandates.
51
+ * Diagnostic codes the engine may emit alongside a decision.
52
+ * `tool-policy.requiredToolBlocked` fires when a host policy gate
53
+ * (`policy.blocked`, missing-from-`placement`, provider veto) removed a tool
54
+ * that item or district `requiredTools` mandates.
59
55
  *
60
56
  * `tool-policy.placementMissing` fires when a custom `PolicySource`
61
57
  * references a tool ID that is not present in `tools.placement[level]`
62
- * for the resolved level. The host-side `ToolConfigDiagnostic` channel
58
+ * for the resolved level. `details` is {@link PlacementMissingDetails}.
59
+ * The host-side `ToolConfigDiagnostic` channel
63
60
  * (already used by `tool-config-validation.ts`) covers config-time
64
61
  * misconfiguration; this channel covers per-decision conflicts.
62
+ *
63
+ * `tool-policy.unknownSupportId` fires for each id a profile, district
64
+ * policy, test administration or item names that no tool is registered under.
65
+ * `details` is {@link UnknownSupportIdDetails}.
66
+ *
67
+ * `tool-policy.itemSettingNotApplied` fires on a section-, assessment- or
68
+ * passage-level decision for each tool on that toolbar a mounted item's
69
+ * `restrictedTools` or `requiredTools` names, once per tool and item: item
70
+ * settings govern only the item's own toolbar. `details` is
71
+ * {@link ItemSettingNotAppliedDetails}.
72
+ *
73
+ * `tool-policy.overrideBlocked` fires for each tool a `true`
74
+ * `settings.testAdministration.toolOverrides` entry grants that an item's
75
+ * `restrictedTools` or the profile's `prohibitedSupports` withdraws instead.
76
+ * `details` is {@link OverrideBlockedDetails}.
77
+ *
78
+ * The toolkit coordinator logs each diagnostic once per code, tool and item.
65
79
  */
66
- export type ToolPolicyDiagnosticCode = "tool-policy.requiredToolBlocked" | "tool-policy.placementMissing" | "tool-policy.unknownSupportId";
80
+ export type ToolPolicyDiagnosticCode = "tool-policy.requiredToolBlocked" | "tool-policy.placementMissing" | "tool-policy.unknownSupportId" | "tool-policy.itemSettingNotApplied" | "tool-policy.overrideBlocked";
67
81
  /**
68
82
  * Which host gate removed a profile-mandated tool. Surfaced inside
69
83
  * `ToolPolicyDiagnostic.details` for `tool-policy.requiredToolBlocked`
@@ -94,29 +108,53 @@ export interface RequiredToolBlockedDetails extends Record<string, unknown> {
94
108
  /** The host configuration value that triggered the gate (best-effort, may be omitted). */
95
109
  hostValue?: unknown;
96
110
  }
111
+ /** Payload of a `tool-policy.unknownSupportId` diagnostic. */
112
+ export interface UnknownSupportIdDetails extends Record<string, unknown> {
113
+ /** The rules whose lists name the id, in precedence order. */
114
+ origins: PnpPolicySourceRule[];
115
+ }
116
+ /** Payload of a `tool-policy.placementMissing` diagnostic. */
117
+ export interface PlacementMissingDetails extends Record<string, unknown> {
118
+ /** The custom `PolicySource` that named the tool. */
119
+ customSourceId: string;
120
+ }
121
+ /** Payload of a `tool-policy.overrideBlocked` diagnostic. */
122
+ export interface OverrideBlockedDetails extends Record<string, unknown> {
123
+ /** The rule that withdrew the granted tool. */
124
+ rule: "item-restriction" | "pnp-prohibited";
125
+ }
126
+ /** Payload of a `tool-policy.itemSettingNotApplied` diagnostic. */
127
+ export interface ItemSettingNotAppliedDetails extends Record<string, unknown> {
128
+ /** Canonical id of the item whose setting names the tool. */
129
+ itemId: string;
130
+ /** The item settings that name it. */
131
+ settings: Array<"restrictedTools" | "requiredTools">;
132
+ /** The level of the toolbar the tool is on. */
133
+ toolbarLevel: ToolLevel;
134
+ }
97
135
  export interface ToolPolicyDiagnostic {
98
136
  code: ToolPolicyDiagnosticCode;
99
- level: ToolPlacementLevel;
137
+ /** The toolbar level decided; absent on a feature decision. */
138
+ level?: ToolPlacementLevel;
100
139
  toolId: string;
101
140
  message: string;
102
- source?: PolicySourceTag;
103
141
  details?: Record<string, unknown>;
104
142
  }
105
143
  export interface ToolPolicyEntry {
106
144
  toolId: string;
107
145
  /**
108
146
  * `true` when PNP/profile policy mandates this tool (item or district
109
- * `requiredTools`, or PNP support without prohibition). Advisory
110
- * mandates that the host blocked do *not* surface here — they
111
- * appear in `diagnostics` instead. Hosts that need to know about
112
- * those should listen on the diagnostic channel.
147
+ * `requiredTools`). Advisory mandates that the host blocked do *not*
148
+ * surface here — they appear in `diagnostics` instead. Hosts that need
149
+ * to know about those should listen on the diagnostic channel.
113
150
  */
114
151
  required: boolean;
115
152
  /**
116
- * `true` for PNP `supports` — UI-level signal that the host
117
- * cannot toggle this tool off in user preferences. Does NOT
118
- * override host blocks (they would have removed the entry
119
- * before this flag is read).
153
+ * `true` for a tool the student's PNP `supports` or a
154
+ * `testAdministration.toolOverrides` entry grants — UI-level signal that
155
+ * the host cannot toggle this tool off in user preferences. Does NOT
156
+ * override host blocks (they would have removed the entry before this
157
+ * flag is read).
120
158
  */
121
159
  alwaysAvailable: boolean;
122
160
  /**
@@ -126,13 +164,6 @@ export interface ToolPolicyEntry {
126
164
  * the assessment entity from above the engine.
127
165
  */
128
166
  settings?: unknown;
129
- /**
130
- * Every contributor that helped this entry survive the
131
- * composition pipeline. Always non-empty — at minimum
132
- * `["placement"]` for any entry that came from
133
- * `tools.placement[level]`.
134
- */
135
- sources: PolicySourceTag[];
136
167
  }
137
168
  export interface ToolPolicyDecision {
138
169
  visibleTools: ToolPolicyEntry[];
@@ -21,10 +21,10 @@
21
21
  * reasons, and only the second is a defect.
22
22
  */
23
23
  import type { PnpPolicyResult } from "../sources/PnpPolicySource.js";
24
- import type { PnpPolicySourceRule } from "./policy-source-tag.js";
25
- import type { ToolPolicyResolutionDecision, ToolPolicySourceType } from "./provenance.js";
24
+ import type { ToolPolicyDiagnostic } from "./decision-types.js";
25
+ import type { PnpPolicySourceRule, ToolPolicyResolutionDecision, ToolPolicySourceType } from "./provenance.js";
26
26
  /**
27
- * A feature verdict comes from one of the six PNP precedence levels, or from a
27
+ * A feature verdict comes from one of the eight PNP precedence levels, or from a
28
28
  * host gate that never reaches them: `tools.policy.blocked` and a non-empty
29
29
  * `tools.policy.allowed` are absolute for the id they name, exactly as they are
30
30
  * on the placement-scoped path.
@@ -34,7 +34,7 @@ export interface FeaturePolicyDecision {
34
34
  /** The PNP/AfA support id that was evaluated (e.g. `"signLanguage"`). */
35
35
  featureId: string;
36
36
  /**
37
- * `true` only when policy explicitly granted the feature at one of the six
37
+ * `true` only when policy explicitly granted the feature at one of the eight
38
38
  * precedence levels. A feature nobody configured is **not** available —
39
39
  * accommodations require a documented need, so silence means no.
40
40
  */
@@ -42,7 +42,7 @@ export interface FeaturePolicyDecision {
42
42
  action: ToolPolicyResolutionDecision["action"];
43
43
  /** Which precedence rule produced the verdict. */
44
44
  rule: FeaturePolicyRule;
45
- precedence: 0 | 1 | 2 | 3 | 4 | 5 | 6;
45
+ precedence: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
46
46
  sourceType: ToolPolicySourceType;
47
47
  /** Human-readable explanation, suitable for a policy debugger. */
48
48
  reason: string;
@@ -51,9 +51,10 @@ export interface FeaturePolicyDecision {
51
51
  *
52
52
  * `false` makes a *denial* a wiring gap rather than a verdict: no profile,
53
53
  * district policy or test administration could be consulted, because there was
54
- * no assessment to read them from. It is not itself a denial — an item ref
55
- * carrying `requiredTools` mandates a feature at precedence 4 with no
56
- * assessment bound — so read it alongside `granted` rather than instead of it.
54
+ * no assessment to read them from. It is not itself a denial — an item's
55
+ * registered settings carrying `requiredTools` mandate a feature at precedence
56
+ * 6 with no assessment bound — so read it alongside `granted` rather than
57
+ * instead of it.
57
58
  *
58
59
  * Granting is unaffected either way: an unbound host with no item mandate still
59
60
  * gets `granted: false`, since an accommodation requires a documented need and
@@ -66,7 +67,7 @@ export interface FeaturePolicyDecision {
66
67
  assessmentBound: boolean;
67
68
  /**
68
69
  * `true` when the grant is a mandate (item or district `requiredTools`)
69
- * rather than a student-profile support.
70
+ * rather than a student-profile support or a test-administration override.
70
71
  */
71
72
  required: boolean;
72
73
  /**
@@ -75,6 +76,12 @@ export interface FeaturePolicyDecision {
75
76
  * presentation would hang on; no vocabulary is defined yet.
76
77
  */
77
78
  parameters?: unknown;
79
+ /**
80
+ * Conflicts found while deciding: a `tool-policy.unknownSupportId` for each
81
+ * id the bound inputs name that no tool is registered under. Empty on a host
82
+ * denial, which consults no policy source.
83
+ */
84
+ diagnostics: ToolPolicyDiagnostic[];
78
85
  }
79
86
  /** What the engine knows that a single support-id resolution does not. */
80
87
  export interface FeatureDecisionContext {
@@ -20,13 +20,14 @@
20
20
  * "nothing was bound to ask against" are the same verdict for different
21
21
  * reasons, and only the second is a defect.
22
22
  */
23
+ import { overrideBlockedDiagnostic, unknownSupportIdDiagnostic, } from "./compose-decision.js";
23
24
  /**
24
25
  * Reason text for a denial that had no assessment to decide against.
25
26
  *
26
- * Replaces the `pnp-support` skip's "not configured at any level", which is
27
- * true but reads as a completed evaluation. `rule` and `precedence` stay as the
28
- * source reported them: nothing fired, so naming a seventh rule would describe
29
- * a precedence level that does not exist.
27
+ * Used in place of the `pnp-support` skip's "not configured at any level",
28
+ * which is true but reads as a completed evaluation. `rule` and `precedence`
29
+ * stay as the source reported them: nothing fired, so naming a ninth rule
30
+ * would describe a precedence level that does not exist.
30
31
  */
31
32
  const unboundAssessmentReason = (featureId) => `No assessment is bound, so no policy source could grant "${featureId}"`;
32
33
  /**
@@ -51,6 +52,7 @@ export function hostFeatureDenial(featureId, rule, hostValue, context) {
51
52
  : `Not listed in tools.policy.allowed (${hostValue.join(", ")})`,
52
53
  required: false,
53
54
  assessmentBound: context.assessmentBound,
55
+ diagnostics: [],
54
56
  };
55
57
  }
56
58
  /**
@@ -81,17 +83,21 @@ export function interpretFeatureResult(featureId, result, context) {
81
83
  granted,
82
84
  action: decision?.action ?? "skip",
83
85
  rule: decision?.rule ?? "pnp-support",
84
- precedence: decision?.precedence ?? 6,
86
+ precedence: decision?.precedence ?? 8,
85
87
  sourceType: decision?.sourceType ?? "system",
86
88
  // Only a denial is re-worded. An unbound host can still be granted the
87
- // feature — an item ref carrying `requiredTools` mandates it at precedence 4
88
- // with no assessment in sight — and there the source's reason is the true
89
- // one.
89
+ // feature — an item's registered settings carrying `requiredTools` mandate
90
+ // it at precedence 6 with no assessment in sight — and there the source's
91
+ // reason is the true one.
90
92
  reason: context.assessmentBound || granted
91
93
  ? reason
92
94
  : unboundAssessmentReason(featureId),
93
95
  required: Boolean(flags?.required),
94
96
  parameters: flags?.settings,
95
97
  assessmentBound: context.assessmentBound,
98
+ diagnostics: [
99
+ ...Array.from(result.unmappedSupportIds, ([supportId, origins]) => unknownSupportIdDiagnostic(supportId, origins)),
100
+ ...Array.from(result.blockedOverrides, ([toolId, rule]) => overrideBlockedDiagnostic(toolId, rule)),
101
+ ],
96
102
  };
97
103
  }
@@ -2,64 +2,55 @@
2
2
  * PNP/profile policy input detection. See
3
3
  * `docs/tools-and-accomodations/architecture.md`.
4
4
  *
5
- * Pure helpers that decide whether the inputs the engine has been given
6
- * actually carry PNP/profile policy material. Used by:
7
- *
8
- * - {@link ToolPolicyEngine}'s constructor as the auto-default for
9
- * `pnpEnforcement` when the host did not pass an explicit `"on"` /
10
- * `"off"`.
11
- * - {@link ToolkitCoordinator.resolveEffectivePnpEnforcement} as the
12
- * auto-mode rule. PR 2 used a coarse "any non-null assessment → on"
13
- * placeholder; PR 4 narrows it to "assessment OR currentItemRef
14
- * carries profile policy material."
5
+ * Pure helpers that decide whether policy inputs carry PNP/profile policy
6
+ * material, which is what auto-mode `pnpEnforcement` turns on for. The engine
7
+ * resolves auto-mode per decision: the bound assessment for every decision, and
8
+ * for a decision scoped to an item, that item's settings as well.
15
9
  *
16
10
  * The rule is intentionally narrow. Hosts that bind a bare assessment
17
11
  * record (only `id` / `name`, no PNP and no settings) do not engage
18
12
  * PNP/profile gates. The flip happens the moment the assessment carries any of:
19
13
  *
20
- * - `personalNeedsProfile` (any `supports`, `prohibitedSupports`, or
21
- * `activateAtInit`),
22
- * - `settings.districtPolicy` (any `blockedTools`, `requiredTools`,
23
- * or `policies`),
24
- * - `settings.testAdministration` (any populated key — `mode`,
25
- * `toolOverrides`, etc.),
26
- * - or the bound `currentItemRef.settings` carries
14
+ * - `personalNeedsProfile` (any `supports` or `prohibitedSupports`),
15
+ * - `settings.districtPolicy` (any `blockedTools` or `requiredTools`),
16
+ * - `settings.testAdministration` (any `toolOverrides` entry),
17
+ * - or, for an item-scoped decision, the item's settings carry
27
18
  * `requiredTools` / `restrictedTools` / `toolParameters`.
28
19
  *
29
20
  * Hosts opt out of the auto-on behavior by passing
30
21
  * `pnpEnforcement: "off"` explicitly (engine input or
31
22
  * `ToolkitCoordinator.setPnpEnforcement("off")`).
32
23
  */
33
- import type { AssessmentEntity, AssessmentItemRef } from "@pie-players/pie-players-shared/types";
24
+ import type { AssessmentEntity, ItemSettings } from "@pie-players/pie-players-shared/types";
34
25
  /**
35
26
  * Return `true` when the assessment carries any PNP/profile policy
36
27
  * material that the engine's `PnpPolicySource` would consume.
37
28
  *
38
- * The check is structural — the presence of a non-empty PNP, district
39
- * policy, or test administration block is enough; the content does not
40
- * have to validate against any specific rule.
29
+ * The check is structural — a non-empty field the source reads is enough;
30
+ * the content does not have to validate against any specific rule.
41
31
  */
42
32
  export declare function assessmentHasPnpPolicyInputs(assessment: AssessmentEntity | null | undefined): boolean;
43
33
  /**
44
- * Return `true` when the bound item reference carries item-level profile policy
34
+ * Return `true` when an item's settings carry item-level profile policy
45
35
  * material (`requiredTools`, `restrictedTools`, or `toolParameters`).
46
36
  *
47
- * Used in addition to {@link assessmentHasPnpPolicyInputs} so a host that
48
- * navigates to an item with profile-relevant settings — without a parent
49
- * assessment carrying its own PNP/district/test-admin block — still
50
- * gets PNP/profile gates engaged for that item.
37
+ * Used in addition to {@link assessmentHasPnpPolicyInputs} for a decision scoped
38
+ * to the item, so an item with profile-relevant settings engages PNP/profile
39
+ * gates on its own toolbar without a parent assessment carrying a
40
+ * PNP/district/test-admin block.
51
41
  */
52
- export declare function itemRefHasPnpPolicyInputs(itemRef: AssessmentItemRef | null | undefined): boolean;
42
+ export declare function itemSettingsHavePnpPolicyInputs(settings: ItemSettings | null | undefined): boolean;
53
43
  /**
54
- * Resolve the default `pnpEnforcement` mode given the bound inputs.
44
+ * Resolve the auto-mode `pnpEnforcement` for one decision.
55
45
  *
56
- * Returns `"on"` when {@link assessmentHasPnpPolicyInputs} or
57
- * {@link itemRefHasPnpPolicyInputs} reports any profile policy material; otherwise
58
- * `"off"`. Hosts override the default by passing an explicit
59
- * `pnpEnforcement` value (engine input) or by calling
46
+ * Returns `"on"` when {@link assessmentHasPnpPolicyInputs} reports profile
47
+ * policy material, or when `itemSettings` (the settings of the item a decision
48
+ * is scoped to) do per {@link itemSettingsHavePnpPolicyInputs}; otherwise
49
+ * `"off"`. Hosts override the default by passing an explicit `pnpEnforcement`
50
+ * value (engine input) or by calling
60
51
  * `ToolkitCoordinator.setPnpEnforcement("on" | "off")`.
61
52
  */
62
53
  export declare function resolveDefaultPnpEnforcement(args: {
63
54
  assessment?: AssessmentEntity | null;
64
- currentItemRef?: AssessmentItemRef | null;
55
+ itemSettings?: ItemSettings | null;
65
56
  }): "on" | "off";