@pie-players/pie-assessment-toolkit 0.3.74 → 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 (228) hide show
  1. package/README.md +349 -313
  2. package/dist/attempt/TestSession.d.ts +1 -20
  3. package/dist/attempt/TestSession.js +8 -99
  4. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  5. package/dist/components/ItemScope.custom-element.js +1 -0
  6. package/dist/components/ItemToolBar.custom-element.js +1 -1
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
  8. package/dist/components/SectionToolBar.custom-element.js +1 -1
  9. package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
  10. package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-97hx60z4.js} +1 -1
  11. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
  12. package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
  13. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
  14. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  15. package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
  16. package/dist/components/item-scope-element.d.ts +2 -0
  17. package/dist/components/item-scope-element.js +2 -0
  18. package/dist/context/assessment-toolkit-context.d.ts +27 -6
  19. package/dist/index.d.ts +22 -47
  20. package/dist/index.js +12 -36
  21. package/dist/policy/core/PolicySource.d.ts +1 -1
  22. package/dist/policy/core/ToolPolicyEngine.d.ts +77 -32
  23. package/dist/policy/core/ToolPolicyEngine.js +136 -46
  24. package/dist/policy/core/compose-decision.d.ts +28 -7
  25. package/dist/policy/core/compose-decision.js +75 -54
  26. package/dist/policy/core/decision-types.d.ts +65 -34
  27. package/dist/policy/core/feature-decision.d.ts +16 -9
  28. package/dist/policy/core/feature-decision.js +14 -8
  29. package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
  30. package/dist/policy/core/pnp-policy-inputs.js +28 -56
  31. package/dist/policy/core/provenance.d.ts +9 -4
  32. package/dist/policy/core/provenance.js +1 -1
  33. package/dist/policy/engine.d.ts +7 -23
  34. package/dist/policy/engine.js +5 -20
  35. package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
  36. package/dist/policy/sources/PnpPolicySource.js +117 -118
  37. package/dist/runtime/SectionControllerBinding.d.ts +279 -0
  38. package/dist/runtime/SectionControllerBinding.js +450 -0
  39. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  40. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  41. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  42. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  43. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  44. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  45. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
  46. package/dist/runtime/adapter/subscriber-fanout.js +1 -2
  47. package/dist/runtime/catalog-context.d.ts +19 -0
  48. package/dist/runtime/catalog-context.js +28 -0
  49. package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
  50. package/dist/runtime/composition-emit-scheduler.js +9 -15
  51. package/dist/runtime/content-language.d.ts +32 -0
  52. package/dist/runtime/content-language.js +46 -0
  53. package/dist/runtime/content-region.d.ts +17 -0
  54. package/dist/runtime/content-region.js +29 -0
  55. package/dist/runtime/content-registration.d.ts +13 -0
  56. package/dist/runtime/content-registration.js +25 -0
  57. package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
  58. package/dist/runtime/core/SectionEngineCore.js +2 -2
  59. package/dist/runtime/core/cohort.d.ts +1 -1
  60. package/dist/runtime/core/cohort.js +1 -1
  61. package/dist/runtime/core/engine-input.d.ts +18 -30
  62. package/dist/runtime/core/engine-input.js +11 -12
  63. package/dist/runtime/core/engine-output.d.ts +16 -40
  64. package/dist/runtime/core/engine-output.js +9 -21
  65. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  66. package/dist/runtime/core/engine-readiness.js +8 -41
  67. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  68. package/dist/runtime/core/engine-resolver.js +18 -43
  69. package/dist/runtime/core/engine-state.d.ts +28 -36
  70. package/dist/runtime/core/engine-state.js +18 -21
  71. package/dist/runtime/core/engine-transition.d.ts +14 -19
  72. package/dist/runtime/core/engine-transition.js +71 -51
  73. package/dist/runtime/engine.d.ts +16 -19
  74. package/dist/runtime/engine.js +12 -19
  75. package/dist/runtime/page-warnings.d.ts +13 -0
  76. package/dist/runtime/page-warnings.js +22 -0
  77. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  78. package/dist/runtime/policy-input-forwarding.js +25 -0
  79. package/dist/runtime/registration-events.d.ts +11 -0
  80. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  81. package/dist/runtime/runtime-event-claim.js +15 -0
  82. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  83. package/dist/runtime/shell-event-bridge.js +127 -0
  84. package/dist/runtime/shell-registration.d.ts +73 -0
  85. package/dist/runtime/shell-registration.js +95 -0
  86. package/dist/runtime/shell-scope.d.ts +68 -0
  87. package/dist/runtime/shell-scope.js +188 -0
  88. package/dist/runtime/tool-host-contract.d.ts +8 -10
  89. package/dist/runtime/tool-host-contract.js +11 -11
  90. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  91. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  92. package/dist/services/ElementToolStateStore.d.ts +14 -10
  93. package/dist/services/ElementToolStateStore.js +37 -13
  94. package/dist/services/HighlightCoordinator.d.ts +21 -21
  95. package/dist/services/HighlightCoordinator.js +431 -325
  96. package/dist/services/RangeSerializer.d.ts +10 -1
  97. package/dist/services/RangeSerializer.js +87 -14
  98. package/dist/services/TTSService.d.ts +117 -70
  99. package/dist/services/TTSService.js +668 -578
  100. package/dist/services/ToolCoordinator.d.ts +43 -60
  101. package/dist/services/ToolCoordinator.js +164 -206
  102. package/dist/services/ToolRegistry.d.ts +74 -57
  103. package/dist/services/ToolRegistry.js +104 -114
  104. package/dist/services/ToolkitCoordinator.d.ts +183 -189
  105. package/dist/services/ToolkitCoordinator.js +477 -431
  106. package/dist/services/audio-handoff.d.ts +6 -7
  107. package/dist/services/audio-handoff.js +20 -14
  108. package/dist/services/catalog-owner.d.ts +0 -1
  109. package/dist/services/framework-error-bus.d.ts +3 -6
  110. package/dist/services/framework-error-bus.js +3 -6
  111. package/dist/services/framework-error.d.ts +22 -7
  112. package/dist/services/framework-error.js +9 -7
  113. package/dist/services/interfaces.d.ts +158 -129
  114. package/dist/services/section-session-assignment.d.ts +21 -0
  115. package/dist/services/section-session-assignment.js +82 -0
  116. package/dist/services/spoken-audio-cards.d.ts +0 -2
  117. package/dist/services/spoken-audio-cards.js +0 -2
  118. package/dist/services/tool-config-defaults.d.ts +3 -3
  119. package/dist/services/tool-config-defaults.js +3 -3
  120. package/dist/services/tool-config-validation.d.ts +4 -5
  121. package/dist/services/tool-config-validation.js +36 -61
  122. package/dist/services/tool-context.d.ts +0 -8
  123. package/dist/services/tool-context.js +0 -12
  124. package/dist/services/tool-icons.d.ts +4 -3
  125. package/dist/services/tool-icons.js +4 -3
  126. package/dist/services/tool-instance-id.d.ts +0 -1
  127. package/dist/services/tool-instance-id.js +0 -6
  128. package/dist/services/tool-providers/TTSToolProvider.d.ts +18 -19
  129. package/dist/services/tool-providers/TTSToolProvider.js +37 -34
  130. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
  131. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +30 -26
  132. package/dist/services/tool-providers/ToolProviderRegistry.js +119 -93
  133. package/dist/services/tool-request.d.ts +27 -12
  134. package/dist/services/tool-request.js +24 -14
  135. package/dist/services/toolbar-decided-tools.d.ts +15 -0
  136. package/dist/services/toolbar-decided-tools.js +28 -0
  137. package/dist/services/tools-config-normalizer.d.ts +17 -22
  138. package/dist/services/tools-config-normalizer.js +2 -10
  139. package/dist/services/tts/browser-provider.d.ts +38 -2
  140. package/dist/services/tts/browser-provider.js +276 -140
  141. package/dist/services/tts/flat-tree.d.ts +71 -0
  142. package/dist/services/tts/flat-tree.js +269 -0
  143. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  144. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  145. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  146. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  147. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  148. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  149. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +12 -4
  150. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  151. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  152. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  153. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  154. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  155. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  156. package/dist/services/tts/math-aware-text-processing.js +32 -18
  157. package/dist/services/tts/math-control-names.d.ts +21 -0
  158. package/dist/services/tts/math-control-names.js +142 -0
  159. package/dist/services/tts/math-speech.d.ts +11 -0
  160. package/dist/services/tts/math-speech.js +51 -18
  161. package/dist/services/tts/provider-options.d.ts +41 -0
  162. package/dist/services/tts/provider-options.js +1 -0
  163. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  164. package/dist/services/tts/ssml/spoken-text.js +29 -0
  165. package/dist/services/tts/start-failure.d.ts +11 -0
  166. package/dist/services/tts/start-failure.js +19 -0
  167. package/dist/services/tts/text-processing.d.ts +9 -0
  168. package/dist/services/tts/text-processing.js +46 -33
  169. package/dist/services/tts/text-segmentation.d.ts +7 -0
  170. package/dist/services/tts/text-segmentation.js +47 -0
  171. package/dist/services/tts-runtime-config.d.ts +25 -17
  172. package/dist/services/tts-runtime-config.js +16 -95
  173. package/dist/tools/content-capability-resolution.d.ts +7 -9
  174. package/dist/tools/content-capability-resolution.js +8 -45
  175. package/dist/tools/registration.d.ts +58 -0
  176. package/dist/tools/registration.js +54 -0
  177. package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
  178. package/dist/tools/tool-surface-host.d.ts +6 -10
  179. package/dist/tools/tool-surface-host.js +44 -42
  180. package/dist/tools/tool-tag-map.d.ts +5 -15
  181. package/dist/tools/tool-tag-map.js +6 -20
  182. package/dist/utils/structural-equality.d.ts +11 -0
  183. package/dist/utils/structural-equality.js +47 -0
  184. package/package.json +13 -29
  185. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  186. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  187. package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
  188. package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
  189. package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
  190. package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
  191. package/dist/context/runtime-context-consumer.d.ts +0 -13
  192. package/dist/context/runtime-context-consumer.js +0 -18
  193. package/dist/item-loader.d.ts +0 -44
  194. package/dist/item-loader.js +0 -61
  195. package/dist/policy/core/engine-context.d.ts +0 -29
  196. package/dist/policy/core/engine-context.js +0 -27
  197. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  198. package/dist/policy/core/policy-source-tag.js +0 -28
  199. package/dist/policy/internal.d.ts +0 -25
  200. package/dist/policy/internal.js +0 -25
  201. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  202. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  203. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  204. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  205. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  206. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  207. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  208. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  209. package/dist/runtime/engine-context.d.ts +0 -39
  210. package/dist/runtime/engine-context.js +0 -31
  211. package/dist/runtime/internal.d.ts +0 -43
  212. package/dist/runtime/internal.js +0 -45
  213. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
  214. package/dist/runtime/section-runtime-engine-host-context.js +0 -57
  215. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  216. package/dist/runtime/stage-emit-gate.js +0 -82
  217. package/dist/services/I18nService.d.ts +0 -47
  218. package/dist/services/I18nService.js +0 -84
  219. package/dist/services/ThemeProvider.d.ts +0 -95
  220. package/dist/services/ThemeProvider.js +0 -334
  221. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  222. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  223. package/dist/services/pnp-standard-features.d.ts +0 -238
  224. package/dist/services/pnp-standard-features.js +0 -338
  225. package/dist/tools/client.d.ts +0 -10
  226. package/dist/tools/client.js +0 -10
  227. package/dist/tools/internal.d.ts +0 -44
  228. package/dist/tools/internal.js +0 -39
@@ -8,48 +8,26 @@
8
8
  * Part of PIE Assessment Toolkit.
9
9
  */
10
10
  import type { I18nServiceApi } from "@pie-players/pie-players-shared/i18n";
11
- import type { AccessibilityCatalog } from "@pie-players/pie-players-shared/types";
11
+ import type { AccessibilityCatalog, SectionControllerSessionState } from "@pie-players/pie-players-shared/types";
12
12
  import type { AccessibilityCatalogResolver, CatalogChangeListener, CatalogLookupContext, CatalogLookupOptions, CatalogOwnerRegistration, CatalogOwnerView, CatalogStatistics, CatalogType, ResolvedCatalog } from "./AccessibilityCatalogResolver.js";
13
13
  import type { CatalogOwnerContext } from "./catalog-owner.js";
14
14
  import type { FrameworkErrorListener } from "./framework-error-bus.js";
15
15
  import type { FrameworkErrorModel } from "./framework-error.js";
16
- import type { HighlightColor, HighlightType } from "./HighlightCoordinator.js";
17
- import type { SectionControllerHandle, SectionItemEventSubscriptionArgs, SectionScopedEventSubscriptionArgs, SectionEventSubscriptionArgs, ToolkitCoordinatorHooks, ToolkitInitStatus, TTSToolConfig } from "./ToolkitCoordinator.js";
18
- import type { ThemeConfig } from "./ThemeProvider.js";
16
+ import type { Annotation, HighlightColor, HighlightType } from "./HighlightCoordinator.js";
17
+ import type { SerializedRange } from "./RangeSerializer.js";
18
+ import type { SectionControllerHandle, SectionItemEventSubscriptionArgs, SectionScopedEventSubscriptionArgs, SectionEventSubscriptionArgs, ToolkitCoordinatorHooks } from "./ToolkitCoordinator.js";
19
19
  import type { ZIndexLayer } from "./ToolCoordinator.js";
20
20
  import type { PlaybackState, TTSConfig } from "./TTSService.js";
21
21
  import type { TTSHighlightTargetResolverProvider } from "./tts/highlight-target-resolver.js";
22
- import type { ToolPlacementConfig, ToolPlacementLevel, ToolProviderConfig } from "./tools-config-normalizer.js";
22
+ import type { TextToSpeechToolProviderConfig, ToolPlacementConfig, ToolProviderConfig } from "./tools-config-normalizer.js";
23
+ import type { ToolProviderApi } from "./tool-providers/ToolProviderApi.js";
23
24
  import type { ToolProviderRegistry } from "./tool-providers/ToolProviderRegistry.js";
24
- import type { FeaturePolicyDecision, PolicySource, PnpEnforcementMode, ResolvedEngineInputs, ToolPolicyChangeListener, ToolPolicyDecision, ToolPolicyDecisionRequest } from "../policy/engine.js";
25
- import type { AssessmentEntity, AssessmentItemRef } from "@pie-players/pie-players-shared/types";
25
+ import type { FeaturePolicyDecision, PolicySource, PnpEnforcementMode, ResolvedEngineInputs, ToolPolicyChangeListener, ToolPolicyDecision, ToolPolicyDecisionRequest, ToolScope } from "../policy/engine.js";
26
+ import type { AssessmentEntity, ItemSettings } from "@pie-players/pie-players-shared/types";
26
27
  import type { ITTSProvider, TTSProviderCapabilities } from "@pie-players/pie-tts";
27
- import type { ResolvedToolContext, ToolContextResolver, ToolContextResolverContext, ToolContextResolverMap } from "./ToolRegistry.js";
28
+ import type { ResolvedToolContext, ToolContextResolverContext, ToolRegistry } from "./ToolRegistry.js";
28
29
  import type { ToolOpenRequest, ToolRequestTarget } from "./tool-request.js";
29
30
  export type { I18nServiceApi };
30
- /**
31
- * Theme provider interface
32
- *
33
- * Applies consistent accessibility theming across items and tools.
34
- */
35
- export interface ThemeProviderApi {
36
- /**
37
- * Apply theme configuration
38
- */
39
- applyTheme(config: ThemeConfig): void;
40
- /**
41
- * Get current theme configuration
42
- */
43
- getCurrentTheme(): Required<ThemeConfig>;
44
- /**
45
- * Reset to default theme
46
- */
47
- reset(): void;
48
- /**
49
- * Clean up and remove theme styles
50
- */
51
- destroy(): void;
52
- }
53
31
  /**
54
32
  * Highlight coordinator interface
55
33
  *
@@ -61,9 +39,11 @@ export interface HighlightCoordinatorApi {
61
39
  */
62
40
  highlightRange(range: Range, type: HighlightType, color: HighlightColor): void;
63
41
  /**
64
- * Highlight a word for TTS (temporary)
42
+ * Highlight the word being read (temporary): one range per tree the word
43
+ * spans, all painted, so a word split across inline elements highlights
44
+ * whole.
65
45
  */
66
- highlightTTSWord(textNode: Text, startOffset: number, endOffset: number): void;
46
+ highlightTTSWord(ranges: Range[]): void;
67
47
  /**
68
48
  * Highlight a single element as the active TTS word (temporary).
69
49
  *
@@ -73,20 +53,16 @@ export interface HighlightCoordinatorApi {
73
53
  * (e.g. `<mjx-mi><mjx-c/></mjx-mi>`) and whole-expression fallbacks. Painting
74
54
  * the element itself is what lets a resolved math token highlight as a token
75
55
  * rather than escalating to the full `<math>` / `<mjx-container>`.
76
- *
77
- * Optional so lightweight coordinator mocks can omit it.
78
56
  */
79
- highlightTTSWordElement?(element: Element): void;
57
+ highlightTTSWordElement(element: Element): void;
80
58
  /**
81
59
  * Highlight sentence(s) for TTS (background layer)
82
60
  */
83
61
  highlightTTSSentence(ranges: Range[]): void;
84
62
  /**
85
63
  * Highlight sentence/block element targets for TTS (background layer).
86
- *
87
- * Optional so lightweight coordinator mocks can omit it.
88
64
  */
89
- highlightTTSSentenceElements?(elements: Element[]): void;
65
+ highlightTTSSentenceElements(elements: Element[]): void;
90
66
  /**
91
67
  * Clear all TTS highlights (word and sentence)
92
68
  */
@@ -95,10 +71,6 @@ export interface HighlightCoordinatorApi {
95
71
  * Clear highlights of a specific type
96
72
  */
97
73
  clearHighlights(type: HighlightType): void;
98
- /**
99
- * Clear all highlights
100
- */
101
- clearAll(): void;
102
74
  /**
103
75
  * Check if highlighting is supported in current environment
104
76
  */
@@ -107,6 +79,46 @@ export interface HighlightCoordinatorApi {
107
79
  * Update TTS highlight style dynamically
108
80
  */
109
81
  updateTTSHighlightStyle(color: string, opacity: number): void;
82
+ /**
83
+ * Record a learner annotation over a range.
84
+ *
85
+ * @returns Annotation id for later lookup or removal
86
+ */
87
+ addAnnotation(range: Range, color?: HighlightColor): string;
88
+ /**
89
+ * Remove one annotation
90
+ */
91
+ removeAnnotation(id: string): void;
92
+ /**
93
+ * Remove all annotations
94
+ */
95
+ clearAnnotations(): void;
96
+ /**
97
+ * All recorded annotations
98
+ */
99
+ getAnnotations(): Annotation[];
100
+ /**
101
+ * One annotation by id
102
+ */
103
+ getAnnotation(id: string): Annotation | null;
104
+ /**
105
+ * Serialize annotations relative to `root` for persistence
106
+ */
107
+ exportAnnotations(root?: Element): Array<SerializedRange & {
108
+ id: string;
109
+ color: HighlightColor;
110
+ timestamp: number;
111
+ }>;
112
+ /**
113
+ * Restore annotations serialized by `exportAnnotations` against the same root
114
+ *
115
+ * @returns Number of annotations restored
116
+ */
117
+ importAnnotations(data: Array<SerializedRange & {
118
+ id?: string;
119
+ color: HighlightColor;
120
+ timestamp?: number;
121
+ }>, root?: Element): number;
110
122
  }
111
123
  /**
112
124
  * Tool state interface
@@ -118,6 +130,11 @@ export interface ToolState {
118
130
  element: HTMLElement | null;
119
131
  layer: ZIndexLayer;
120
132
  }
133
+ /** Narrows tool-state queries to the instances of one tool. */
134
+ export interface ToolStateFilter {
135
+ /** Base tool id, such as `calculator`, matching every scoped instance of it. */
136
+ baseId?: string;
137
+ }
121
138
  /**
122
139
  * Tool coordinator interface
123
140
  *
@@ -167,14 +184,37 @@ export interface ToolCoordinatorApi {
167
184
  */
168
185
  getToolState(id: string): ToolState | undefined;
169
186
  /**
170
- * Get all visible tools
187
+ * Every visible tool, or with `{ baseId }` every visible instance of one
188
+ * tool, so a host need not parse scoped ids.
189
+ */
190
+ getVisibleTools(filter?: ToolStateFilter): ToolState[];
191
+ /**
192
+ * Hide every visible tool, or every instance of one tool with `{ baseId }`.
171
193
  */
172
- getVisibleTools(): ToolState[];
194
+ hideAllTools(filter?: ToolStateFilter): void;
173
195
  /**
174
196
  * Subscribe to tool state changes
175
197
  */
176
198
  subscribe(listener: () => void): () => void;
177
199
  }
200
+ /** Options for {@link TtsServiceApi.speak}. */
201
+ export interface SpeakOptions {
202
+ /**
203
+ * The spoken card of the content root, read in place of its content when the
204
+ * target holds the whole root.
205
+ */
206
+ catalogId?: string;
207
+ /** The owner whose registered cards apply; without it only assessment-level cards do. */
208
+ catalogContext?: CatalogLookupContext;
209
+ /** BCP 47 language of the content read. */
210
+ language?: string;
211
+ /**
212
+ * For a range target, the content root that scopes highlighting and the
213
+ * offsets of the selection. Defaults to the range's nearest element. An
214
+ * element target is its own root.
215
+ */
216
+ contentRoot?: Element | null;
217
+ }
178
218
  /**
179
219
  * TTS service interface
180
220
  *
@@ -187,20 +227,13 @@ export interface TtsServiceApi {
187
227
  */
188
228
  initialize(provider: ITTSProvider, config?: Partial<TTSConfig>): Promise<void>;
189
229
  /**
190
- * Speak text with optional catalog support
191
- */
192
- speak(text: string, options?: {
193
- catalogId?: string;
194
- catalogContext?: CatalogLookupContext;
195
- language?: string;
196
- contentElement?: Element;
197
- }): Promise<void>;
198
- /**
199
- * Speak a text range
230
+ * Read `target` aloud: a range reads the text it selects, an element its
231
+ * content. A node with a spoken card that the target holds whole reads its
232
+ * card, and math reads as math speech. Content marked not-to-be-spoken is
233
+ * never read; when nothing speakable remains, nothing is spoken and playback
234
+ * already running continues.
200
235
  */
201
- speakRange(range: Range, options?: {
202
- contentRoot?: Element | null;
203
- }): Promise<void>;
236
+ speak(target: Range | Element, options?: SpeakOptions): Promise<void>;
204
237
  /**
205
238
  * Pause playback
206
239
  */
@@ -213,6 +246,11 @@ export interface TtsServiceApi {
213
246
  * Stop playback
214
247
  */
215
248
  stop(): void;
249
+ /**
250
+ * Stop, release the provider and drop every listener and timer. A disposed
251
+ * service cannot speak.
252
+ */
253
+ dispose(): void;
216
254
  /**
217
255
  * Request active TTS controls to hand off/deactivate their UI state.
218
256
  *
@@ -245,19 +283,9 @@ export interface TtsServiceApi {
245
283
  */
246
284
  getCurrentText(): string | null;
247
285
  /**
248
- * Whether a catalog holds spoken content this service could speak.
249
- * Optional: a service without one resolves a selection to the nearest
250
- * docked catalog id.
286
+ * Subscribe to state changes. Returns the function that unsubscribes.
251
287
  */
252
- hasSpokenAlternate?(catalogId: string, language?: string): boolean;
253
- /**
254
- * Subscribe to state changes
255
- */
256
- onStateChange(id: string, callback: (state: PlaybackState) => void): void;
257
- /**
258
- * Unsubscribe from state changes
259
- */
260
- offStateChange(id: string, callback: (state: PlaybackState) => void): void;
288
+ onStateChange(callback: (state: PlaybackState) => void): () => void;
261
289
  /**
262
290
  * Get capabilities of current provider
263
291
  */
@@ -451,25 +479,17 @@ export interface ToolkitCoordinatorApi {
451
479
  * Tool provider registry
452
480
  */
453
481
  readonly toolProviderRegistry: ToolProviderRegistry;
454
- /**
455
- * Get all services as a bundle
456
- */
457
- getServiceBundle(): {
458
- ttsService: TtsServiceApi;
459
- toolCoordinator: ToolCoordinatorApi;
460
- highlightCoordinator: HighlightCoordinatorApi;
461
- elementToolStateStore: ElementToolStateStoreApi;
462
- catalogResolver: AccessibilityCatalogResolverApi;
463
- toolProviderRegistry: ToolProviderRegistry;
464
- };
465
482
  /**
466
483
  * Ensure TTS service is initialized and ready.
467
484
  */
468
485
  ensureTTSReady(config?: Record<string, unknown>): Promise<void>;
469
486
  /**
470
- * Ensure a provider is initialized and ready.
487
+ * Ensure a tool's provider is initialized and ready. Providers register under
488
+ * their tool's id, so a tool passes its own base tool id. A tool starts its
489
+ * provider here, so that a failure meets the toolkit's tool failure policy:
490
+ * recoverable unless policy grants the tool.
471
491
  */
472
- ensureProviderReady(providerId: string): Promise<unknown>;
492
+ ensureProviderReady(toolId: string): Promise<ToolProviderApi>;
473
493
  /**
474
494
  * Wait until coordinator initialization is complete.
475
495
  */
@@ -479,27 +499,19 @@ export interface ToolkitCoordinatorApi {
479
499
  */
480
500
  isReady(): boolean;
481
501
  /**
482
- * Read current initialization status.
483
- */
484
- getInitStatus(): ToolkitInitStatus;
485
- /**
486
- * Check if a tool is enabled
502
+ * Subscribe to changes of {@link isReady}.
487
503
  */
488
- isToolEnabled(toolId: string): boolean;
504
+ onReadyChange(listener: () => void): () => void;
489
505
  /**
490
506
  * Get tool configuration
491
507
  */
492
- getToolConfig(toolId: "textToSpeech"): TTSToolConfig | null;
508
+ getToolConfig(toolId: "textToSpeech"): TextToSpeechToolProviderConfig | null;
493
509
  getToolConfig(toolId: string): ToolProviderConfig | null;
494
510
  /**
495
511
  * Update tool configuration
496
512
  */
497
- updateToolConfig(toolId: "textToSpeech", updates: Partial<TTSToolConfig>): void;
513
+ updateToolConfig(toolId: "textToSpeech", updates: Partial<TextToSpeechToolProviderConfig>): void;
498
514
  updateToolConfig(toolId: string, updates: Partial<ToolProviderConfig>): void;
499
- /**
500
- * Update the enabled tool list for one placement level.
501
- */
502
- updateToolPlacement(level: ToolPlacementLevel, toolIds: string[]): void;
503
515
  /**
504
516
  * Patch one or more placement levels in the canonical tools config.
505
517
  */
@@ -526,11 +538,12 @@ export interface ToolkitCoordinatorApi {
526
538
  * aggregate `section-loading-complete`, in the canonical order a
527
539
  * fresh subscriber would have observed.
528
540
  *
529
- * Throws if no active section cohort exists; host code must call
530
- * `getOrCreateSectionController(...)` at least once before
531
- * subscribing. (`toolkit-ready` alone is not sufficient — it fires
532
- * once toolkit state has loaded but before any section controller
533
- * has been created.) The typical pattern is to subscribe once
541
+ * Throws until the first `getOrCreateSectionController(...)` call; a
542
+ * listener added while a section is starting binds when that section
543
+ * becomes active. `runtime-ready` alone is not sufficient: it fires once
544
+ * the coordinator is bound, before any section controller exists. A
545
+ * `<pie-assessment-toolkit>` holding a section emits `toolkit-ready`
546
+ * after its controller resolves. The typical pattern is to subscribe once
534
547
  * immediately after the first `getOrCreateSectionController(...)`
535
548
  * resolves; the subscription then follows the active cohort across
536
549
  * all subsequent navigation without further wiring.
@@ -559,12 +572,18 @@ export interface ToolkitCoordinatorApi {
559
572
  subscribeSectionLifecycleEvents(args: SectionScopedEventSubscriptionArgs): () => void;
560
573
  /**
561
574
  * Create or reuse a section controller with single-flight deduplication.
575
+ *
576
+ * `initialSession` is a host-supplied session. A new controller applies it in
577
+ * replace mode in place of `hydrate()`, before it is published; an existing
578
+ * one applies it unless it equals the current session, keeping recorded
579
+ * responses that a response-free item session would replace.
562
580
  */
563
581
  getOrCreateSectionController(args: {
564
582
  sectionId: string;
565
583
  attemptId?: string;
566
584
  input?: unknown;
567
585
  updateExisting?: boolean;
586
+ initialSession?: SectionControllerSessionState | null;
568
587
  createDefaultController: () => SectionControllerHandle | Promise<SectionControllerHandle>;
569
588
  }): Promise<SectionControllerHandle>;
570
589
  /**
@@ -599,6 +618,12 @@ export interface ToolkitCoordinatorApi {
599
618
  * Optional so structural host coordinators remain assignable.
600
619
  */
601
620
  reportFrameworkError?(model: FrameworkErrorModel): void;
621
+ /**
622
+ * Report that a toolbar could not load a tool's module. The tool degrades
623
+ * unless policy grants it, in which case the failure is fatal. Optional so
624
+ * structural host coordinators remain assignable; a toolbar without it logs.
625
+ */
626
+ reportToolModuleFailure?(toolId: string, error: unknown): void;
602
627
  /**
603
628
  * Resolve the visible tool set for a given placement level + scope.
604
629
  * Returns the engine's full decision (visible tools, diagnostics,
@@ -607,22 +632,25 @@ export interface ToolkitCoordinatorApi {
607
632
  */
608
633
  decideToolPolicy(request: ToolPolicyDecisionRequest): ToolPolicyDecision;
609
634
  /**
610
- * Resolve eligibility for one PNP/AfA feature id through the six-level
635
+ * Resolve eligibility for one PNP/AfA feature id through the eight-level
611
636
  * precedence, independent of toolbar placement — for capabilities that
612
637
  * render as their own surface rather than a toolbar button (a signed
613
638
  * alternate's region, for example).
614
639
  *
640
+ * `scope` is the surface asking; an item's scope brings in that item's
641
+ * registered settings ({@link registerItemSettings}).
642
+ *
615
643
  * Optional so host-supplied coordinator stubs predating this method stay
616
644
  * assignable; call sites must feature-detect.
617
645
  */
618
- decideFeaturePolicy?(featureId: string): FeaturePolicyDecision;
646
+ decideFeaturePolicy?(featureId: string, scope?: ToolScope): FeaturePolicyDecision;
619
647
  /**
620
648
  * Subscribe to policy-engine change events. Fires whenever the
621
649
  * coordinator's bound policy inputs change (`updateToolConfig`,
622
- * `updateToolPlacement`, `updateAssessment`, `updateCurrentItemRef`,
623
- * `setPnpEnforcement`) or a custom `PolicySource` is registered /
624
- * removed. Listeners that need the new visible tool set should
625
- * call `decideToolPolicy(...)` with their level / scope.
650
+ * `updateToolsPlacement`, `updateAssessment`, `setPnpEnforcement`), an
651
+ * item's settings are registered or withdrawn, or a custom `PolicySource`
652
+ * is registered / removed. Listeners that need the new visible tool set
653
+ * should call `decideToolPolicy(...)` with their level / scope.
626
654
  */
627
655
  onPolicyChange(listener: ToolPolicyChangeListener): () => void;
628
656
  /**
@@ -645,30 +673,31 @@ export interface ToolkitCoordinatorApi {
645
673
  /**
646
674
  * Bind (or clear) the active assessment for PNP/profile policy decisions.
647
675
  *
648
- * Under auto-mode (no host override via {@link setPnpEnforcement}),
649
- * the engine flips to `pnpEnforcement: "on"` iff the assessment
650
- * carries profile precedence material (`personalNeedsProfile`,
651
- * `settings.districtPolicy`, `settings.testAdministration`) or the
652
- * currently-bound item ref carries item-level profile inputs. A bare
653
- * assessment record (just `id` / `name`) keeps `"off"`.
676
+ * Under auto-mode (no host override via {@link setPnpEnforcement}), a
677
+ * decision enforces PNP/profile policy iff the assessment carries profile
678
+ * precedence material (`personalNeedsProfile`, `settings.districtPolicy`,
679
+ * `settings.testAdministration`), or the decision is scoped to an item whose
680
+ * registered settings carry item-level policy inputs. A bare assessment
681
+ * record (just `id` / `name`) keeps `"off"`.
654
682
  *
655
683
  * The host override set via {@link setPnpEnforcement} is sticky
656
684
  * across assessment swaps.
657
685
  */
658
686
  updateAssessment(assessment: AssessmentEntity | null): void;
659
687
  /**
660
- * Bind (or clear) the current item reference for policy decisions.
661
- * Used by item-level profile gates (item `requiredTools` /
662
- * `restrictedTools` / `toolParameters`). Item-level profile material
663
- * also feeds the auto-mode helper — navigating to an item with
664
- * profile settings can flip auto-mode to `"on"` even when the parent
665
- * assessment carries no profile block of its own.
688
+ * File a mounted item's policy settings (`requiredTools`, `restrictedTools`,
689
+ * `toolParameters`) under its canonical id; the returned function withdraws
690
+ * them. They govern only decisions scoped to that item: its own item-level
691
+ * toolbar, and feature decisions asked with the item's scope. A section- or
692
+ * assessment-level toolbar ignores them, and reports each tool on it that
693
+ * they restrict or require as `tool-policy.itemSettingNotApplied`.
694
+ * `<pie-item-scope>` registers its `settings` property through this.
666
695
  */
667
- updateCurrentItemRef(itemRef: AssessmentItemRef | null): void;
696
+ registerItemSettings(itemId: string, settings: ItemSettings): () => void;
668
697
  /**
669
698
  * Override the auto-mode PNP/profile enforcement decision. Pass `"on"` /
670
699
  * `"off"` to pin the mode, or `null` to clear the override and
671
- * return to auto-mode (`"on"` iff the bound assessment / item ref
700
+ * return to auto-mode (`"on"` for a decision whose assessment or item
672
701
  * carries profile material, otherwise `"off"`).
673
702
  */
674
703
  setPnpEnforcement(mode: PnpEnforcementMode | null): void;
@@ -683,14 +712,6 @@ export interface ToolkitCoordinatorApi {
683
712
  * (the returned function detaches).
684
713
  */
685
714
  registerPolicySource(source: PolicySource): () => void;
686
- /**
687
- * Register a host-owned resolver for scoped tool render context.
688
- */
689
- registerToolContextResolver(toolId: string, resolver: ToolContextResolver): () => void;
690
- /**
691
- * Replace all host-owned render-context resolvers.
692
- */
693
- setToolContextResolvers(resolvers: ToolContextResolverMap | null | undefined): void;
694
715
  /**
695
716
  * Whether a host resolver is registered for this tool.
696
717
  */
@@ -703,6 +724,14 @@ export interface ToolkitCoordinatorApi {
703
724
  * Subscribe to resolver registration/removal changes.
704
725
  */
705
726
  onToolContextResolverChange(listener: () => void): () => void;
727
+ /**
728
+ * The registry this coordinator's providers and policy resolve against: the
729
+ * one it was built with or adopted, else the empty one it started with. A
730
+ * toolbar that is not handed a registry reads it here, again on each
731
+ * {@link onPolicyChange}, which an adoption fires. Optional for the same reason
732
+ * as the request seam below: a host-supplied coordinator may predate it.
733
+ */
734
+ getToolRegistry?(): ToolRegistry;
706
735
  /**
707
736
  * The tool-open request seam, optional as a group.
708
737
  *
@@ -724,7 +753,7 @@ export interface ToolkitCoordinatorApi {
724
753
  * Whether a request for this tool would reach a toolbar. A surface asks before
725
754
  * offering the affordance.
726
755
  */
727
- canRequestTool?(toolId: string, level?: ToolOpenRequest["level"]): boolean;
756
+ canRequestTool?(toolId: string, level?: ToolOpenRequest["level"], scopeId?: string): boolean;
728
757
  /**
729
758
  * Subscribe to toolbar registration/removal, so a surface can re-evaluate the
730
759
  * actions it offers.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * A host-assigned section session, resolved against the session a live
3
+ * controller already holds.
4
+ *
5
+ * The rules are the ones `ItemController.setSession` applies to one item:
6
+ * - a value whose content equals the current session is a no-op, so echoing a
7
+ * snapshot back into the `session` property applies nothing;
8
+ * - an item session with neither a response value nor a response field leaves
9
+ * an item session that holds responses in place. The embedded item player
10
+ * applies the same rule when the section hands the session down, so applying
11
+ * it at section level keeps the controller and its item players from
12
+ * diverging.
13
+ *
14
+ * A controller that holds no session yet takes the value as assigned.
15
+ */
16
+ import type { SectionControllerSessionState } from "@pie-players/pie-players-shared/types";
17
+ /**
18
+ * The session to apply in replace mode, or `null` when applying `next` would
19
+ * change nothing.
20
+ */
21
+ export declare function resolveSectionSessionAssignment(current: SectionControllerSessionState | null | undefined, next: SectionControllerSessionState): SectionControllerSessionState | null;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * A host-assigned section session, resolved against the session a live
3
+ * controller already holds.
4
+ *
5
+ * The rules are the ones `ItemController.setSession` applies to one item:
6
+ * - a value whose content equals the current session is a no-op, so echoing a
7
+ * snapshot back into the `session` property applies nothing;
8
+ * - an item session with neither a response value nor a response field leaves
9
+ * an item session that holds responses in place. The embedded item player
10
+ * applies the same rule when the section hands the session down, so applying
11
+ * it at section level keeps the controller and its item players from
12
+ * diverging.
13
+ *
14
+ * A controller that holds no session yet takes the value as assigned.
15
+ */
16
+ import { hasResponseField, hasResponseValue, } from "@pie-players/pie-players-shared/pie";
17
+ import { structurallyEqual } from "../utils/structural-equality.js";
18
+ function isRecord(value) {
19
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+ /** An entry is canonical (`{ itemIdentifier, session, ... }`) or the raw item session. */
22
+ function itemSessionOf(entry) {
23
+ return isRecord(entry) && isRecord(entry.session) ? entry.session : entry;
24
+ }
25
+ function sameItemSessions(next, current) {
26
+ const nextSessions = isRecord(next) ? next : {};
27
+ const currentSessions = isRecord(current) ? current : {};
28
+ const itemIds = new Set([
29
+ ...Object.keys(nextSessions),
30
+ ...Object.keys(currentSessions),
31
+ ]);
32
+ for (const itemId of itemIds) {
33
+ if (!structurallyEqual(itemSessionOf(nextSessions[itemId]), itemSessionOf(currentSessions[itemId]))) {
34
+ return false;
35
+ }
36
+ }
37
+ return true;
38
+ }
39
+ /**
40
+ * Whether applying `next` would leave `current` as it is. A field `next` leaves
41
+ * out is unchanged, and item sessions compare by their session payload, so a
42
+ * raw `{ id, data }` matches the canonical entry the controller stored for it.
43
+ */
44
+ function sameSessionContent(next, current) {
45
+ for (const [key, value] of Object.entries(next)) {
46
+ if (value === undefined)
47
+ continue;
48
+ if (key === "itemSessions") {
49
+ if (!sameItemSessions(value, current.itemSessions))
50
+ return false;
51
+ continue;
52
+ }
53
+ if (!structurallyEqual(value, current[key])) {
54
+ return false;
55
+ }
56
+ }
57
+ return true;
58
+ }
59
+ /**
60
+ * The session to apply in replace mode, or `null` when applying `next` would
61
+ * change nothing.
62
+ */
63
+ export function resolveSectionSessionAssignment(current, next) {
64
+ if (!current)
65
+ return next;
66
+ const itemSessions = isRecord(next.itemSessions)
67
+ ? { ...next.itemSessions }
68
+ : {};
69
+ const currentItemSessions = isRecord(current.itemSessions)
70
+ ? current.itemSessions
71
+ : {};
72
+ for (const [itemId, currentEntry] of Object.entries(currentItemSessions)) {
73
+ if (!hasResponseValue(itemSessionOf(currentEntry)))
74
+ continue;
75
+ const assigned = itemSessionOf(itemSessions[itemId]);
76
+ if (hasResponseValue(assigned) || hasResponseField(assigned))
77
+ continue;
78
+ itemSessions[itemId] = currentEntry;
79
+ }
80
+ const resolved = { ...next, itemSessions };
81
+ return sameSessionContent(resolved, current) ? null : resolved;
82
+ }
@@ -21,8 +21,6 @@
21
21
  * Part of PIE Assessment Toolkit.
22
22
  */
23
23
  import type { CatalogCardPayload, MediaFragmentRange, MediaSource } from "@pie-players/pie-players-shared/types";
24
- /** Catalog type token for spoken alternates. Matches QTI 3's `support` value. */
25
- export declare const SPOKEN_CATALOG_TYPE = "spoken";
26
24
  /** A validated recorded spoken alternate, flattened for playback. */
27
25
  export interface SpokenAudioMedia {
28
26
  /**
@@ -21,8 +21,6 @@
21
21
  * Part of PIE Assessment Toolkit.
22
22
  */
23
23
  import { isUnsupportedMediaAssetVersion, normalizeMediaFragment, normalizeMediaSources, trimmedOrUndefined, } from "@pie-players/pie-players-shared/media";
24
- /** Catalog type token for spoken alternates. Matches QTI 3's `support` value. */
25
- export const SPOKEN_CATALOG_TYPE = "spoken";
26
24
  /**
27
25
  * Validate a `spoken` card's payload into something playable, or `null`.
28
26
  *
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Placement defaults this package can hold.
3
3
  *
4
- * Only the empty one. `PACKAGED_TOOL_PLACEMENT` and
5
- * `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` name capabilities, so they moved to
6
- * the composition layer (`@pie-players/pie-default-tool-loaders`).
4
+ * Only the empty one. `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` names
5
+ * capabilities, so it lives in the composition layer
6
+ * (`@pie-players/pie-default-tool-loaders`).
7
7
  */
8
8
  export declare const DEFAULT_TOOL_PLACEMENT: {
9
9
  readonly assessment: readonly [];
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Placement defaults this package can hold.
3
3
  *
4
- * Only the empty one. `PACKAGED_TOOL_PLACEMENT` and
5
- * `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` name capabilities, so they moved to
6
- * the composition layer (`@pie-players/pie-default-tool-loaders`).
4
+ * Only the empty one. `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` names
5
+ * capabilities, so it lives in the composition layer
6
+ * (`@pie-players/pie-default-tool-loaders`).
7
7
  */
8
8
  export const DEFAULT_TOOL_PLACEMENT = {
9
9
  assessment: [],