@pie-players/pie-assessment-toolkit 0.3.75 → 1.0.0-beta.1

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 (142) hide show
  1. package/README.md +451 -541
  2. package/custom-elements.json +1315 -0
  3. package/dist/attempt/TestSession.d.ts +0 -1
  4. package/dist/attempt/TestSession.js +10 -5
  5. package/dist/components/ItemScope.custom-element.js +1 -1
  6. package/dist/components/ItemToolBar.custom-element.js +1 -1
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +8 -8
  8. package/dist/components/SectionToolBar.custom-element.js +1 -1
  9. package/dist/components/chunks/{ItemToolBar-7tt7v2xp.js → ItemToolBar-3zrc4p3d.js} +4 -4
  10. package/dist/components/chunks/{ItemToolBar-jx38w99s.js → ItemToolBar-66hcwmfw.js} +2 -2
  11. package/dist/components/chunks/ItemToolBar-asenkvx6.js +40 -0
  12. package/dist/components/chunks/ItemToolBar-hzc5wyjp.js +2 -0
  13. package/dist/components/chunks/{ItemToolBar-97hx60z4.js → ItemToolBar-kkj69ptr.js} +1 -1
  14. package/dist/components/chunks/sre-engine-bj997w4b.js +1 -0
  15. package/dist/context/assessment-toolkit-context.d.ts +6 -0
  16. package/dist/index.d.ts +7 -7
  17. package/dist/index.js +4 -10
  18. package/dist/policy/core/PolicySource.d.ts +1 -1
  19. package/dist/policy/core/ToolPolicyEngine.d.ts +46 -7
  20. package/dist/policy/core/ToolPolicyEngine.js +73 -16
  21. package/dist/policy/core/compose-decision.d.ts +3 -3
  22. package/dist/policy/core/compose-decision.js +32 -30
  23. package/dist/policy/core/decision-types.d.ts +51 -34
  24. package/dist/policy/core/feature-decision.d.ts +17 -15
  25. package/dist/policy/core/feature-decision.js +11 -13
  26. package/dist/policy/core/pnp-policy-inputs.d.ts +7 -3
  27. package/dist/policy/core/pnp-policy-inputs.js +17 -33
  28. package/dist/policy/core/provenance.d.ts +2 -2
  29. package/dist/policy/core/tool-parameters.d.ts +12 -0
  30. package/dist/policy/core/tool-parameters.js +27 -0
  31. package/dist/policy/engine.d.ts +1 -1
  32. package/dist/policy/sources/PnpPolicySource.d.ts +18 -24
  33. package/dist/policy/sources/PnpPolicySource.js +57 -29
  34. package/dist/runtime/SectionControllerBinding.d.ts +6 -2
  35. package/dist/runtime/SectionControllerBinding.js +21 -15
  36. package/dist/runtime/adapter/subscriber-fanout.d.ts +0 -1
  37. package/dist/runtime/adapter/subscriber-fanout.js +1 -4
  38. package/dist/runtime/composition-emit-scheduler.d.ts +3 -3
  39. package/dist/runtime/composition-emit-scheduler.js +1 -1
  40. package/dist/runtime/content-region.d.ts +0 -4
  41. package/dist/runtime/content-region.js +2 -2
  42. package/dist/runtime/core/SectionEngineCore.d.ts +0 -2
  43. package/dist/runtime/core/SectionEngineCore.js +0 -4
  44. package/dist/runtime/core/cohort.d.ts +0 -5
  45. package/dist/runtime/core/cohort.js +0 -9
  46. package/dist/runtime/core/engine-output.d.ts +3 -2
  47. package/dist/runtime/core/engine-transition.js +3 -2
  48. package/dist/runtime/registration-events.d.ts +11 -14
  49. package/dist/runtime/runtime-id.d.ts +1 -1
  50. package/dist/runtime/runtime-id.js +1 -1
  51. package/dist/runtime/shell-scope.d.ts +4 -4
  52. package/dist/runtime/shell-scope.js +15 -9
  53. package/dist/runtime/tool-host-contract.d.ts +0 -4
  54. package/dist/runtime/tool-host-contract.js +1 -1
  55. package/dist/services/AccessibilityCatalogResolver.d.ts +21 -31
  56. package/dist/services/AccessibilityCatalogResolver.js +29 -42
  57. package/dist/services/ElementToolStateStore.d.ts +32 -61
  58. package/dist/services/ElementToolStateStore.js +37 -67
  59. package/dist/services/HighlightCoordinator.d.ts +9 -21
  60. package/dist/services/HighlightCoordinator.js +52 -80
  61. package/dist/services/RangeSerializer.d.ts +4 -35
  62. package/dist/services/RangeSerializer.js +4 -48
  63. package/dist/services/SSMLExtractor.d.ts +8 -31
  64. package/dist/services/SSMLExtractor.js +18 -40
  65. package/dist/services/TTSService.d.ts +51 -57
  66. package/dist/services/TTSService.js +323 -284
  67. package/dist/services/ToolCoordinator.d.ts +6 -49
  68. package/dist/services/ToolCoordinator.js +25 -61
  69. package/dist/services/ToolRegistry.d.ts +83 -80
  70. package/dist/services/ToolRegistry.js +91 -67
  71. package/dist/services/ToolkitCoordinator.d.ts +83 -81
  72. package/dist/services/ToolkitCoordinator.js +186 -174
  73. package/dist/services/framework-error.d.ts +30 -7
  74. package/dist/services/framework-error.js +10 -13
  75. package/dist/services/interfaces.d.ts +51 -71
  76. package/dist/services/section-controller-types.d.ts +7 -7
  77. package/dist/services/section-session-assignment.js +6 -8
  78. package/dist/services/spoken-audio-cards.d.ts +2 -4
  79. package/dist/services/spoken-audio-cards.js +2 -4
  80. package/dist/services/tool-config-validation.d.ts +5 -7
  81. package/dist/services/tool-config-validation.js +11 -26
  82. package/dist/services/tool-context.d.ts +39 -24
  83. package/dist/services/tool-context.js +158 -92
  84. package/dist/services/tool-failure.d.ts +30 -0
  85. package/dist/services/tool-failure.js +11 -0
  86. package/dist/services/tool-icons.d.ts +1 -2
  87. package/dist/services/tool-icons.js +2 -2
  88. package/dist/services/tool-instance-id.d.ts +8 -6
  89. package/dist/services/tool-instance-id.js +12 -18
  90. package/dist/services/tool-providers/TTSToolProvider.d.ts +6 -43
  91. package/dist/services/tool-providers/TTSToolProvider.js +7 -63
  92. package/dist/services/tool-providers/ToolProviderApi.d.ts +0 -7
  93. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +7 -42
  94. package/dist/services/tool-providers/ToolProviderRegistry.js +21 -63
  95. package/dist/services/tool-request.d.ts +4 -0
  96. package/dist/services/tool-request.js +7 -2
  97. package/dist/services/toolbar-decided-tools.d.ts +2 -0
  98. package/dist/services/toolbar-decided-tools.js +2 -0
  99. package/dist/services/toolbar-items.js +2 -5
  100. package/dist/services/tools-config-normalizer.d.ts +18 -9
  101. package/dist/services/tools-config-normalizer.js +25 -19
  102. package/dist/services/tts/browser-provider.d.ts +2 -5
  103. package/dist/services/tts/browser-provider.js +13 -22
  104. package/dist/services/tts/catalog-span-alignment.js +14 -7
  105. package/dist/services/tts/flat-tree.d.ts +0 -7
  106. package/dist/services/tts/flat-tree.js +2 -2
  107. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +2 -2
  108. package/dist/services/tts/generated-speech/index.d.ts +1 -1
  109. package/dist/services/tts/generated-speech/index.js +1 -1
  110. package/dist/services/tts/generated-speech/types.d.ts +1 -1
  111. package/dist/services/tts/generated-speech/types.js +1 -1
  112. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +10 -12
  113. package/dist/services/tts/highlight-target-resolver.d.ts +12 -0
  114. package/dist/services/tts/math-aware-text-processing.js +10 -29
  115. package/dist/services/tts/math-speech.d.ts +1 -1
  116. package/dist/services/tts/math-speech.js +3 -0
  117. package/dist/services/tts/mathml-sanitization.d.ts +7 -0
  118. package/dist/services/tts/mathml-sanitization.js +9 -1
  119. package/dist/services/tts/provider-options.d.ts +12 -1
  120. package/dist/services/tts/ssml/spoken-text.js +1 -1
  121. package/dist/services/tts/start-failure.d.ts +0 -7
  122. package/dist/services/tts/start-failure.js +1 -1
  123. package/dist/services/tts/text-segmentation.d.ts +5 -0
  124. package/dist/services/tts/text-segmentation.js +13 -10
  125. package/dist/services/tts-runtime-config.d.ts +32 -12
  126. package/dist/services/tts-runtime-config.js +14 -20
  127. package/dist/tools/content-capability-resolution.d.ts +2 -2
  128. package/dist/tools/registration.d.ts +7 -6
  129. package/dist/tools/registration.js +10 -9
  130. package/dist/tools/registrations/toolbar-registration-helpers.d.ts +6 -0
  131. package/dist/tools/registrations/toolbar-registration-helpers.js +6 -0
  132. package/dist/tools/tool-surface-host.js +1 -8
  133. package/package.json +8 -6
  134. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +0 -2
  135. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +0 -40
  136. package/dist/components/chunks/sre-engine-jd8cc08x.js +0 -1
  137. package/dist/services/ContextVariableStore.d.ts +0 -113
  138. package/dist/services/ContextVariableStore.js +0 -206
  139. package/dist/services/tts-control-events.d.ts +0 -13
  140. package/dist/services/tts-control-events.js +0 -7
  141. package/dist/utils/logger.d.ts +0 -6
  142. package/dist/utils/logger.js +0 -10
@@ -1,10 +1,25 @@
1
- import type { ToolConfigDiagnostic } from "./tool-config-validation.js";
1
+ import type { ToolFailurePhase } from "./tool-failure.js";
2
2
  export type FrameworkErrorKind = "tool-config" | "runtime-init" | "runtime-dispose" | "coordinator-init" | "provider-init" | "provider-register" | "tts-init" | "tool-state-load" | "tool-state-save" | "section-controller-init" | "section-controller-dispose" | "tool-surface"
3
3
  /**
4
4
  * A toolbar could not load a tool's module. Recoverable unless policy grants
5
5
  * the tool: the toolbar withholds the tool and the assessment goes on.
6
6
  */
7
7
  | "tool-module-load"
8
+ /**
9
+ * A toolbar threw opening a requested tool or answering whether it hosts one.
10
+ * Recoverable: the request goes unclaimed or passes the toolbar by.
11
+ */
12
+ | "tool-request"
13
+ /**
14
+ * A tool registration's relevance check or applicability gate threw.
15
+ * Recoverable: the toolbar applies the answer a missing check would give.
16
+ */
17
+ | "tool-registration"
18
+ /**
19
+ * A tool could not play, seek or change the rate of speech after it started.
20
+ * Recoverable: the tool stays available and the learner can retry.
21
+ */
22
+ | "tool-playback"
8
23
  /**
9
24
  * A timed-media section could not deliver a policy as authored: a media time
10
25
  * source missing `canPause` / `canRestrictSeeking` (recoverable — cues still
@@ -34,6 +49,14 @@ export type FrameworkErrorSeverity = "warning" | "error";
34
49
  * later section runs on, so readiness stays failed. Set by the report site.
35
50
  */
36
51
  export type FrameworkErrorScope = "cohort" | "runtime";
52
+ /**
53
+ * The section a `cohort`-scoped error was reported for. A section can fail after
54
+ * the learner has left it, so a reader holding another section ignores the error.
55
+ */
56
+ export interface FrameworkErrorCohort {
57
+ sectionId: string;
58
+ attemptId?: string;
59
+ }
37
60
  export interface FrameworkErrorModel {
38
61
  kind: FrameworkErrorKind;
39
62
  severity: FrameworkErrorSeverity;
@@ -42,6 +65,8 @@ export interface FrameworkErrorModel {
42
65
  details: string[];
43
66
  recoverable: boolean;
44
67
  scope: FrameworkErrorScope;
68
+ /** Set on a `cohort`-scoped error whose report site knows its section. */
69
+ cohort?: FrameworkErrorCohort;
45
70
  cause?: unknown;
46
71
  }
47
72
  export declare function toFrameworkErrorModel(args: {
@@ -53,6 +78,7 @@ export declare function toFrameworkErrorModel(args: {
53
78
  recoverable?: boolean;
54
79
  /** Defaults to `runtime`. */
55
80
  scope?: FrameworkErrorScope;
81
+ cohort?: FrameworkErrorCohort;
56
82
  cause?: unknown;
57
83
  }): FrameworkErrorModel;
58
84
  export declare function frameworkErrorFromUnknown(args: {
@@ -61,6 +87,7 @@ export declare function frameworkErrorFromUnknown(args: {
61
87
  error: unknown;
62
88
  recoverable?: boolean;
63
89
  scope?: FrameworkErrorScope;
90
+ cohort?: FrameworkErrorCohort;
64
91
  }): FrameworkErrorModel;
65
92
  /**
66
93
  * Coordinator-side error context shape consumed by
@@ -73,7 +100,7 @@ export declare function frameworkErrorFromUnknown(args: {
73
100
  * truth for coordinator code; values of that type satisfy this shape.
74
101
  */
75
102
  export interface FrameworkErrorCoordinatorContext {
76
- phase: "coordinator-ready" | "state-load" | "state-save" | "provider-register" | "provider-init" | "tts-init" | "section-controller-init" | "section-controller-dispose" | "tool-module-load";
103
+ phase: "coordinator-ready" | "state-load" | "state-save" | "provider-register" | "provider-init" | "tts-init" | "section-controller-init" | "section-controller-dispose" | ToolFailurePhase;
77
104
  toolId?: string;
78
105
  details?: Record<string, unknown>;
79
106
  }
@@ -95,10 +122,6 @@ export declare function frameworkErrorFromCoordinatorContext(args: {
95
122
  context: FrameworkErrorCoordinatorContext;
96
123
  recoverable?: boolean;
97
124
  scope?: FrameworkErrorScope;
98
- }): FrameworkErrorModel;
99
- export declare function frameworkErrorFromToolConfigDiagnostics(args: {
100
- source: string;
101
- diagnostics: ToolConfigDiagnostic[];
102
- recoverable?: boolean;
125
+ cohort?: FrameworkErrorCohort;
103
126
  }): FrameworkErrorModel;
104
127
  export declare function formatFrameworkErrorForConsole(error: FrameworkErrorModel): string;
@@ -7,6 +7,7 @@ export function toFrameworkErrorModel(args) {
7
7
  details: [...(args.details || [])],
8
8
  recoverable: args.recoverable === true,
9
9
  scope: args.scope ?? "runtime",
10
+ ...(args.cohort ? { cohort: { ...args.cohort } } : {}),
10
11
  cause: args.cause,
11
12
  };
12
13
  }
@@ -20,6 +21,7 @@ export function frameworkErrorFromUnknown(args) {
20
21
  message,
21
22
  recoverable: args.recoverable,
22
23
  scope: args.scope,
24
+ cohort: args.cohort,
23
25
  cause: args.error,
24
26
  });
25
27
  }
@@ -33,6 +35,13 @@ const COORDINATOR_PHASE_TO_KIND = {
33
35
  "section-controller-init": "section-controller-init",
34
36
  "section-controller-dispose": "section-controller-dispose",
35
37
  "tool-module-load": "tool-module-load",
38
+ "tool-request-open": "tool-request",
39
+ "tool-request-host-check": "tool-request",
40
+ "tool-visibility": "tool-registration",
41
+ "tool-applicability": "tool-registration",
42
+ "tool-state-load": "tool-state-load",
43
+ "tool-state-save": "tool-state-save",
44
+ "tool-playback": "tool-playback",
36
45
  };
37
46
  /**
38
47
  * Build a {@link FrameworkErrorModel} from a coordinator-side error +
@@ -58,19 +67,7 @@ export function frameworkErrorFromCoordinatorContext(args) {
58
67
  error: args.error,
59
68
  recoverable: args.recoverable,
60
69
  scope: args.scope,
61
- });
62
- }
63
- export function frameworkErrorFromToolConfigDiagnostics(args) {
64
- const details = args.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`);
65
- const message = details.length > 0
66
- ? "Invalid tools config."
67
- : "Invalid tools config (no diagnostics details were provided).";
68
- return toFrameworkErrorModel({
69
- kind: "tool-config",
70
- source: args.source,
71
- message,
72
- details,
73
- recoverable: args.recoverable,
70
+ cohort: args.cohort,
74
71
  });
75
72
  }
76
73
  export function formatFrameworkErrorForConsole(error) {
@@ -9,22 +9,24 @@
9
9
  */
10
10
  import type { I18nServiceApi } from "@pie-players/pie-players-shared/i18n";
11
11
  import type { AccessibilityCatalog, SectionControllerSessionState } from "@pie-players/pie-players-shared/types";
12
- import type { AccessibilityCatalogResolver, CatalogChangeListener, CatalogLookupContext, CatalogLookupOptions, CatalogOwnerRegistration, CatalogOwnerView, CatalogStatistics, CatalogType, ResolvedCatalog } from "./AccessibilityCatalogResolver.js";
12
+ import type { 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 { Annotation, HighlightColor, HighlightType } from "./HighlightCoordinator.js";
16
+ import type { ToolFailurePhase } from "./tool-failure.js";
17
+ import type { Annotation, HighlightColor } from "./HighlightCoordinator.js";
17
18
  import type { SerializedRange } from "./RangeSerializer.js";
18
19
  import type { SectionControllerHandle, SectionItemEventSubscriptionArgs, SectionScopedEventSubscriptionArgs, SectionEventSubscriptionArgs, ToolkitCoordinatorHooks } from "./ToolkitCoordinator.js";
20
+ import type { ElementIdComponents } from "./ElementToolStateStore.js";
19
21
  import type { ZIndexLayer } from "./ToolCoordinator.js";
20
- import type { PlaybackState, TTSConfig } from "./TTSService.js";
22
+ import type { PlaybackState } from "./TTSService.js";
21
23
  import type { TTSHighlightTargetResolverProvider } from "./tts/highlight-target-resolver.js";
22
24
  import type { TextToSpeechToolProviderConfig, ToolPlacementConfig, ToolProviderConfig } from "./tools-config-normalizer.js";
23
25
  import type { ToolProviderApi } from "./tool-providers/ToolProviderApi.js";
24
26
  import type { ToolProviderRegistry } from "./tool-providers/ToolProviderRegistry.js";
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";
27
- import type { ITTSProvider, TTSProviderCapabilities } from "@pie-players/pie-tts";
27
+ import type { FeaturePolicyDecision, PolicySource, PnpEnforcementMode, ResolvedEngineInputs, ToolPolicyChangeListener, ToolPolicyDecision, ToolPolicyDecisionRequest, ToolPolicyDiagnostic, ToolScope } from "../policy/engine.js";
28
+ import type { AssessmentEntity, ItemSettings, ToolParametersFor } from "@pie-players/pie-players-shared/types";
29
+ import type { TTSConfig } from "@pie-players/pie-tts";
28
30
  import type { ResolvedToolContext, ToolContextResolverContext, ToolRegistry } from "./ToolRegistry.js";
29
31
  import type { ToolOpenRequest, ToolRequestTarget } from "./tool-request.js";
30
32
  export type { I18nServiceApi };
@@ -34,10 +36,6 @@ export type { I18nServiceApi };
34
36
  * Manages content highlighting for TTS, annotations, and selections.
35
37
  */
36
38
  export interface HighlightCoordinatorApi {
37
- /**
38
- * Highlight a text range
39
- */
40
- highlightRange(range: Range, type: HighlightType, color: HighlightColor): void;
41
39
  /**
42
40
  * Highlight the word being read (temporary): one range per tree the word
43
41
  * spans, all painted, so a word split across inline elements highlights
@@ -68,17 +66,9 @@ export interface HighlightCoordinatorApi {
68
66
  */
69
67
  clearTTS(): void;
70
68
  /**
71
- * Clear highlights of a specific type
72
- */
73
- clearHighlights(type: HighlightType): void;
74
- /**
75
- * Check if highlighting is supported in current environment
69
+ * Clear the TTS word highlight, keeping the sentence highlight
76
70
  */
77
- isSupported(): boolean;
78
- /**
79
- * Update TTS highlight style dynamically
80
- */
81
- updateTTSHighlightStyle(color: string, opacity: number): void;
71
+ clearTTSWord(): void;
82
72
  /**
83
73
  * Record a learner annotation over a range.
84
74
  *
@@ -214,6 +204,17 @@ export interface SpeakOptions {
214
204
  * element target is its own root.
215
205
  */
216
206
  contentRoot?: Element | null;
207
+ /**
208
+ * Who starts the read. The run carries it until the next read starts, a
209
+ * seek's restart included, and {@link TtsServiceApi.getRunOwner} reports it.
210
+ */
211
+ ownerId?: string;
212
+ /** The rate the read starts at; it stays the service's rate afterwards. */
213
+ rate?: number;
214
+ /** The pitch the read starts at, 0–2 with 1 normal; it stays the service's. */
215
+ pitch?: number;
216
+ /** The volume the read starts at, 0–1; it stays the service's. */
217
+ volume?: number;
217
218
  }
218
219
  /**
219
220
  * TTS service interface
@@ -222,10 +223,6 @@ export interface SpeakOptions {
222
223
  * Supports QTI 3.0 accessibility catalogs for pre-authored spoken content.
223
224
  */
224
225
  export interface TtsServiceApi {
225
- /**
226
- * Initialize TTS with a provider
227
- */
228
- initialize(provider: ITTSProvider, config?: Partial<TTSConfig>): Promise<void>;
229
226
  /**
230
227
  * Read `target` aloud: a range reads the text it selects, an element its
231
228
  * content. A node with a spoken card that the target holds whole reads its
@@ -243,7 +240,8 @@ export interface TtsServiceApi {
243
240
  */
244
241
  resume(): void;
245
242
  /**
246
- * Stop playback
243
+ * Stop playback and release the run owner. A read that ends on its own
244
+ * keeps its owner.
247
245
  */
248
246
  stop(): void;
249
247
  /**
@@ -251,13 +249,6 @@ export interface TtsServiceApi {
251
249
  * service cannot speak.
252
250
  */
253
251
  dispose(): void;
254
- /**
255
- * Request active TTS controls to hand off/deactivate their UI state.
256
- *
257
- * This is an orchestration hint for TTS tool chrome and does not replace
258
- * playback controls such as stop/pause/resume.
259
- */
260
- requestControlHandoff(): void;
261
252
  /**
262
253
  * Seek forward by sentence units
263
254
  */
@@ -266,30 +257,21 @@ export interface TtsServiceApi {
266
257
  * Seek backward by sentence units
267
258
  */
268
259
  seekBackward(units?: number): Promise<void>;
269
- /**
270
- * Check if currently playing
271
- */
272
- isPlaying(): boolean;
273
- /**
274
- * Check if paused
275
- */
276
- isPaused(): boolean;
277
260
  /**
278
261
  * Get current playback state
279
262
  */
280
263
  getState(): PlaybackState;
281
264
  /**
282
- * Get currently speaking text
265
+ * The `ownerId` the latest read started with, or null when it named none,
266
+ * nothing has been read or {@link stop} released it. A new read announces
267
+ * itself through {@link onStateChange} with `loading`, and a stop that
268
+ * releases an owner announces `idle`, so a listener sees the owner change.
283
269
  */
284
- getCurrentText(): string | null;
270
+ getRunOwner(): string | null;
285
271
  /**
286
272
  * Subscribe to state changes. Returns the function that unsubscribes.
287
273
  */
288
274
  onStateChange(callback: (state: PlaybackState) => void): () => void;
289
- /**
290
- * Get capabilities of current provider
291
- */
292
- getCapabilities(): TTSProviderCapabilities | null;
293
275
  /**
294
276
  * Update TTS settings dynamically (rate, pitch, voice)
295
277
  */
@@ -298,18 +280,10 @@ export interface TtsServiceApi {
298
280
  * Update playback speed and apply it to active planned playback.
299
281
  */
300
282
  setPlaybackRate(rate: number): Promise<void>;
301
- /**
302
- * Set highlight coordinator for word highlighting
303
- */
304
- setHighlightCoordinator(coordinator: HighlightCoordinatorApi): void;
305
283
  /**
306
284
  * Set a late-bound provider for optional host TTS highlight target remapping.
307
285
  */
308
- setHighlightTargetResolverProvider?(provider: TTSHighlightTargetResolverProvider | null): () => void;
309
- /**
310
- * Set accessibility catalog resolver for spoken content
311
- */
312
- setCatalogResolver(resolver: AccessibilityCatalogResolver): void;
286
+ setHighlightTargetResolverProvider(provider: TTSHighlightTargetResolverProvider | null): () => void;
313
287
  }
314
288
  /**
315
289
  * Accessibility catalog resolver interface
@@ -387,18 +361,15 @@ export interface AccessibilityCatalogResolverApi {
387
361
  */
388
362
  export interface ElementToolStateStoreApi {
389
363
  /**
390
- * Generate a globally unique element ID from components
364
+ * Generate a globally unique element ID from its parts. `attemptId` is `""`
365
+ * when the host names no attempt; a tool keyed by item or section leaves
366
+ * `elementId`, and for a section `itemId`, as `""`.
391
367
  */
392
- getGlobalElementId(assessmentId: string, sectionId: string, itemId: string, elementId: string): string;
368
+ getGlobalElementId(parts: ElementIdComponents): string;
393
369
  /**
394
370
  * Parse a global element ID into its components
395
371
  */
396
- parseGlobalElementId(globalElementId: string): {
397
- assessmentId: string;
398
- sectionId: string;
399
- itemId: string;
400
- elementId: string;
401
- } | null;
372
+ parseGlobalElementId(globalElementId: string): ElementIdComponents | null;
402
373
  /**
403
374
  * Set state for a specific tool on an element
404
375
  */
@@ -436,7 +407,7 @@ export interface ElementToolStateStoreApi {
436
407
  */
437
408
  clearTool(toolId: string): void;
438
409
  /**
439
- * Clear all elements in a specific section
410
+ * Clear all elements in a specific section, across its attempts
440
411
  */
441
412
  clearSection(assessmentId: string, sectionId: string): void;
442
413
  /**
@@ -480,9 +451,9 @@ export interface ToolkitCoordinatorApi {
480
451
  */
481
452
  readonly toolProviderRegistry: ToolProviderRegistry;
482
453
  /**
483
- * Ensure TTS service is initialized and ready.
454
+ * Ensure the TTS service is initialized from the `textToSpeech` tool config.
484
455
  */
485
- ensureTTSReady(config?: Record<string, unknown>): Promise<void>;
456
+ ensureTTSReady(): Promise<void>;
486
457
  /**
487
458
  * Ensure a tool's provider is initialized and ready. Providers register under
488
459
  * their tool's id, so a tool passes its own base tool id. A tool starts its
@@ -619,11 +590,11 @@ export interface ToolkitCoordinatorApi {
619
590
  */
620
591
  reportFrameworkError?(model: FrameworkErrorModel): void;
621
592
  /**
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.
593
+ * Report that a tool failed in `phase`, under the tool failure policy
594
+ * {@link ToolFailurePhase} describes. Optional so structural host
595
+ * coordinators remain assignable; a reporter without it logs.
625
596
  */
626
- reportToolModuleFailure?(toolId: string, error: unknown): void;
597
+ reportToolFailure?(toolId: string, phase: ToolFailurePhase, error: unknown): void;
627
598
  /**
628
599
  * Resolve the visible tool set for a given placement level + scope.
629
600
  * Returns the engine's full decision (visible tools, diagnostics,
@@ -643,7 +614,7 @@ export interface ToolkitCoordinatorApi {
643
614
  * Optional so host-supplied coordinator stubs predating this method stay
644
615
  * assignable; call sites must feature-detect.
645
616
  */
646
- decideFeaturePolicy?(featureId: string, scope?: ToolScope): FeaturePolicyDecision;
617
+ decideFeaturePolicy?<K extends string>(featureId: K, scope?: ToolScope): FeaturePolicyDecision<ToolParametersFor<K>>;
647
618
  /**
648
619
  * Subscribe to policy-engine change events. Fires whenever the
649
620
  * coordinator's bound policy inputs change (`updateToolConfig`,
@@ -653,6 +624,15 @@ export interface ToolkitCoordinatorApi {
653
624
  * should call `decideToolPolicy(...)` with their level / scope.
654
625
  */
655
626
  onPolicyChange(listener: ToolPolicyChangeListener): () => void;
627
+ /**
628
+ * Subscribe to the policy diagnostics the coordinator reports: the ones it
629
+ * logs as console warnings, once per code, tool and item. A new listener is
630
+ * first handed every diagnostic already reported. A change to the bound
631
+ * policy inputs or to PNP enforcement clears the record, and so does a tool
632
+ * registration that makes a named support id known or unknown, so a conflict
633
+ * that survives the change is reported again once.
634
+ */
635
+ onPolicyDiagnostic(listener: (diagnostic: ToolPolicyDiagnostic) => void): () => void;
656
636
  /**
657
637
  * Subscribe to accessibility-catalog registrations and removals.
658
638
  *
@@ -27,14 +27,14 @@ export interface SectionSessionPersistenceConfig {
27
27
  context: SectionControllerContext;
28
28
  strategy: SectionSessionPersistenceStrategy;
29
29
  }
30
+ /**
31
+ * applySession contract:
32
+ * - Preferred input is the output of getSession().
33
+ * - itemSessions may contain canonical attempt entries
34
+ * ({ itemIdentifier, session, isCompleted }) or raw session payloads
35
+ * ({ id, data, ... }); controller normalizes these before applying.
36
+ */
30
37
  export interface SectionControllerApplySessionOptions {
31
- /**
32
- * applySession contract:
33
- * - Preferred input is the output of getSession().
34
- * - itemSessions may contain canonical attempt entries
35
- * ({ itemIdentifier, session, isCompleted }) or raw session payloads
36
- * ({ id, data, ... }); controller normalizes these before applying.
37
- */
38
38
  mode?: "replace" | "merge";
39
39
  }
40
40
  export interface SectionControllerLoadedRenderable {
@@ -15,16 +15,14 @@
15
15
  */
16
16
  import { hasResponseField, hasResponseValue, } from "@pie-players/pie-players-shared/pie";
17
17
  import { structurallyEqual } from "../utils/structural-equality.js";
18
- function isRecord(value) {
19
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
20
- }
18
+ import { isPlainRecord } from "@pie-players/pie-players-shared/object";
21
19
  /** An entry is canonical (`{ itemIdentifier, session, ... }`) or the raw item session. */
22
20
  function itemSessionOf(entry) {
23
- return isRecord(entry) && isRecord(entry.session) ? entry.session : entry;
21
+ return isPlainRecord(entry) && isPlainRecord(entry.session) ? entry.session : entry;
24
22
  }
25
23
  function sameItemSessions(next, current) {
26
- const nextSessions = isRecord(next) ? next : {};
27
- const currentSessions = isRecord(current) ? current : {};
24
+ const nextSessions = isPlainRecord(next) ? next : {};
25
+ const currentSessions = isPlainRecord(current) ? current : {};
28
26
  const itemIds = new Set([
29
27
  ...Object.keys(nextSessions),
30
28
  ...Object.keys(currentSessions),
@@ -63,10 +61,10 @@ function sameSessionContent(next, current) {
63
61
  export function resolveSectionSessionAssignment(current, next) {
64
62
  if (!current)
65
63
  return next;
66
- const itemSessions = isRecord(next.itemSessions)
64
+ const itemSessions = isPlainRecord(next.itemSessions)
67
65
  ? { ...next.itemSessions }
68
66
  : {};
69
- const currentItemSessions = isRecord(current.itemSessions)
67
+ const currentItemSessions = isPlainRecord(current.itemSessions)
70
68
  ? current.itemSessions
71
69
  : {};
72
70
  for (const [itemId, currentEntry] of Object.entries(currentItemSessions)) {
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * QTI 3 treats a recording and synthesized speech as the *same* support — both
5
5
  * are `spoken`, and a card carries recorded audio through `qti-file-href` plus a
6
- * MIME type — so this is not a new accommodation but the other form the existing
7
- * one can take. Some programs prefer a human voice to synthesis; PIE's `spoken`
6
+ * MIME type — so a recording is the other form the existing support takes.
7
+ * Some programs prefer a human voice to synthesis; PIE's `spoken`
8
8
  * card was string-only, so it had no way to say "play this file for this node".
9
9
  *
10
10
  * A node commonly carries both forms in the same language: the reading script
@@ -17,8 +17,6 @@
17
17
  * Validation posture matches sign-language cards: "treat as absent, never as
18
18
  * partially valid". A malformed payload must not produce a silent player that
19
19
  * looks like read-aloud is working.
20
- *
21
- * Part of PIE Assessment Toolkit.
22
20
  */
23
21
  import type { CatalogCardPayload, MediaFragmentRange, MediaSource } from "@pie-players/pie-players-shared/types";
24
22
  /** A validated recorded spoken alternate, flattened for playback. */
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * QTI 3 treats a recording and synthesized speech as the *same* support — both
5
5
  * are `spoken`, and a card carries recorded audio through `qti-file-href` plus a
6
- * MIME type — so this is not a new accommodation but the other form the existing
7
- * one can take. Some programs prefer a human voice to synthesis; PIE's `spoken`
6
+ * MIME type — so a recording is the other form the existing support takes.
7
+ * Some programs prefer a human voice to synthesis; PIE's `spoken`
8
8
  * card was string-only, so it had no way to say "play this file for this node".
9
9
  *
10
10
  * A node commonly carries both forms in the same language: the reading script
@@ -17,8 +17,6 @@
17
17
  * Validation posture matches sign-language cards: "treat as absent, never as
18
18
  * partially valid". A malformed payload must not produce a silent player that
19
19
  * looks like read-aloud is working.
20
- *
21
- * Part of PIE Assessment Toolkit.
22
20
  */
23
21
  import { isUnsupportedMediaAssetVersion, normalizeMediaFragment, normalizeMediaSources, trimmedOrUndefined, } from "@pie-players/pie-players-shared/media";
24
22
  /**
@@ -1,5 +1,4 @@
1
1
  import { type CanonicalToolsConfig, type ToolsConfigInput } from "./tools-config-normalizer.js";
2
- import { type FrameworkErrorModel } from "./framework-error.js";
3
2
  import type { ToolRegistry } from "./ToolRegistry.js";
4
3
  export type ToolConfigStrictness = "off" | "warn" | "error";
5
4
  export type ToolConfigDiagnosticSeverity = "warning" | "error";
@@ -19,13 +18,12 @@ export interface ToolConfigValidationResult {
19
18
  config: CanonicalToolsConfig;
20
19
  diagnostics: ToolConfigDiagnostic[];
21
20
  }
22
- export declare function frameworkErrorFromToolConfigValidation(args: {
23
- source: string;
24
- diagnostics?: ToolConfigDiagnostic[] | null;
25
- error?: unknown;
26
- recoverable?: boolean;
27
- }): FrameworkErrorModel;
28
21
  export declare function normalizeToolConfigStrictness(value: unknown): ToolConfigStrictness;
22
+ /**
23
+ * Normalize a tools config, sanitize and validate each provider entry with its
24
+ * registration's hooks, check ids and levels against `options.toolRegistry`,
25
+ * and report the diagnostics by `options.strictness` (default `"error"`).
26
+ */
29
27
  export declare function normalizeAndValidateToolsConfig(input?: ToolsConfigInput | null, options?: ToolConfigValidationOptions): ToolConfigValidationResult;
30
28
  /**
31
29
  * The normalization and diagnostics of {@link normalizeAndValidateToolsConfig},
@@ -1,20 +1,4 @@
1
1
  import { normalizeToolsConfig, } from "./tools-config-normalizer.js";
2
- import { frameworkErrorFromToolConfigDiagnostics, frameworkErrorFromUnknown, } from "./framework-error.js";
3
- export function frameworkErrorFromToolConfigValidation(args) {
4
- if (Array.isArray(args.diagnostics) && args.diagnostics.length > 0) {
5
- return frameworkErrorFromToolConfigDiagnostics({
6
- source: args.source,
7
- diagnostics: args.diagnostics,
8
- recoverable: args.recoverable,
9
- });
10
- }
11
- return frameworkErrorFromUnknown({
12
- kind: "tool-config",
13
- source: args.source,
14
- error: args.error ?? new Error("Invalid tools config"),
15
- recoverable: args.recoverable,
16
- });
17
- }
18
2
  const DEFAULT_STRICTNESS = "error";
19
3
  export function normalizeToolConfigStrictness(value) {
20
4
  if (value === "off" || value === "warn" || value === "error") {
@@ -198,6 +182,11 @@ function throwValidationError(diagnostics, source) {
198
182
  .join("\n");
199
183
  throw new Error(`[tool-config-validation:${source}] Invalid tools config:\n${formatted}`);
200
184
  }
185
+ /**
186
+ * Normalize a tools config, sanitize and validate each provider entry with its
187
+ * registration's hooks, check ids and levels against `options.toolRegistry`,
188
+ * and report the diagnostics by `options.strictness` (default `"error"`).
189
+ */
201
190
  export function normalizeAndValidateToolsConfig(input, options = {}) {
202
191
  const result = collectToolConfigDiagnostics(input, options.toolRegistry);
203
192
  reportToolConfigDiagnostics(result.diagnostics, options);
@@ -212,11 +201,9 @@ export function collectToolConfigDiagnostics(input, toolRegistry) {
212
201
  const normalized = normalizeToolsConfig(input);
213
202
  const diagnostics = [];
214
203
  // Tool-id and provider checks need a registry to check against, and every
215
- // collector below returns early without one. Say so: this package no longer
216
- // falls back to a packaged registry, so a caller that used to get id
217
- // validation for free now gets none, and a silent downgrade of "your ids are
218
- // valid" to "nobody looked" is the kind of change that surfaces as a typo
219
- // reaching a learner.
204
+ // collector below returns early without one. Say so, since there is no
205
+ // packaged fallback registry: silence would read as "your ids are valid" when
206
+ // nobody looked, and a typo would reach a learner.
220
207
  const hasConfiguredTools = Object.values(normalized.placement).some((ids) => ids.length > 0) ||
221
208
  Object.keys(normalized.providers).length > 0 ||
222
209
  (normalized.policy.allowed?.length ?? 0) > 0 ||
@@ -260,11 +247,9 @@ export function reportToolConfigDiagnostics(diagnostics, options = {}) {
260
247
  if (strictness === "warn") {
261
248
  emitWarnings(diagnostics, source);
262
249
  }
263
- // Severity decides what `strictness: "error"` rejects. Every diagnostic was
264
- // `"error"` until `tools.registryUnavailable`, which reports that validation
265
- // could not run rather than that the config is wrong — throwing on it would
266
- // turn "no registry supplied" from a host's existing, working setup into a
267
- // construction failure.
250
+ // Severity decides what `strictness: "error"` rejects. `tools.registryUnavailable`
251
+ // is a warning: it reports that validation could not run, and throwing on it
252
+ // would fail construction for a host whose setup works without a registry.
268
253
  const blocking = diagnostics.filter((entry) => entry.severity === "error");
269
254
  if (strictness === "error" && blocking.length > 0) {
270
255
  throwValidationError(blocking, source);
@@ -102,39 +102,54 @@ export interface ElementToolContext extends BaseToolContext {
102
102
  */
103
103
  export type ToolContext = AssessmentToolContext | SectionToolContext | ItemToolContext | PassageToolContext | RubricToolContext | ElementToolContext;
104
104
  /**
105
- * Type guard to check if context is at item level
106
- */
107
- export declare function isItemContext(context: ToolContext): context is ItemToolContext;
108
- /**
109
- * Type guard to check if context is at passage level
110
- */
111
- export declare function isPassageContext(context: ToolContext): context is PassageToolContext;
112
- /**
113
- * Type guard to check if context is at rubric level
114
- */
115
- export declare function isRubricContext(context: ToolContext): context is RubricToolContext;
116
- /**
117
- * Type guard to check if context is at element level
105
+ * Helper to check if context contains mathematical content
106
+ * (Basic heuristic - can be overridden by tools)
118
107
  */
119
- export declare function isElementContext(context: ToolContext): context is ElementToolContext;
120
- /** The plain text a context carries, tags removed. */
121
- export declare function extractTextContent(context: ToolContext): string;
108
+ export declare function hasMathContent(context: ToolContext): boolean;
122
109
  /**
123
- * The authored markup a context carries, tags intact.
110
+ * What one element of a context declares about the PNP supports it implements.
124
111
  *
125
- * For indicators that live in the markup rather than in the prose — `<math>`
126
- * above all, whose whole signal is the element name.
127
- */
128
- export declare function extractMarkupContent(context: ToolContext): string;
129
- export declare function hasMathContent(context: ToolContext): boolean;
112
+ * An element declares them on its class, as `static pieSupports`: a list of AfA
113
+ * PNP 3.0 support terms, such as `answer-masking`. The toolkit reads the class
114
+ * `customElements` holds under the element's runtime tag, so the declaration is
115
+ * known before the element renders. What a support needs from the rendered
116
+ * element is between the element and the tool that provides it, and that tool
117
+ * documents it.
118
+ */
119
+ export interface ElementSupports {
120
+ /** The element's model id. */
121
+ elementId: string | undefined;
122
+ /** The tag the item binds the element to, as authored. */
123
+ tag: string;
124
+ /** The element's package spec in the item's `elements` map, when it has one. */
125
+ packageSpec: string | undefined;
126
+ /**
127
+ * The terms the element's class declares: `undefined` while the class is
128
+ * undefined, and for a class that declares nothing. An empty list is a
129
+ * declaration that the element implements none.
130
+ */
131
+ supports: readonly string[] | undefined;
132
+ /**
133
+ * The runtime tags the class can still be defined under, empty once it is
134
+ * defined. A definition landing under one of them can change `supports`.
135
+ */
136
+ pendingTags: readonly string[];
137
+ }
130
138
  /**
131
- * Helper to check if context contains choice-based interactions
139
+ * The supports each element of a context declares: the element's own at element
140
+ * level, every element of the item or passage config at those levels, and none
141
+ * elsewhere. A model that names no element renders nothing and is skipped.
132
142
  */
133
- export declare function hasChoiceInteraction(context: ToolContext): boolean;
143
+ export declare function elementSupports(context: ToolContext): ElementSupports[];
134
144
  /**
135
145
  * Helper to check if context contains text that can be read aloud
136
146
  */
137
147
  export declare function hasReadableText(context: ToolContext): boolean;
148
+ /**
149
+ * Whether read-aloud has something to read: readable text, or markup carrying
150
+ * a catalog idref, whose spoken card reads for content with no text.
151
+ */
152
+ export declare function hasSpokenContent(context: ToolContext): boolean;
138
153
  /**
139
154
  * Helper to check if context contains science content
140
155
  * (Basic heuristic - can be overridden by tools)