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

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 +449 -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-9gqhfxtn.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 +30 -30
  23. package/dist/policy/core/decision-types.d.ts +44 -32
  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 +5 -2
  27. package/dist/policy/core/pnp-policy-inputs.js +12 -32
  28. package/dist/policy/core/provenance.d.ts +1 -1
  29. package/dist/policy/core/tool-parameters.d.ts +11 -0
  30. package/dist/policy/core/tool-parameters.js +15 -0
  31. package/dist/policy/engine.d.ts +1 -1
  32. package/dist/policy/sources/PnpPolicySource.d.ts +14 -22
  33. package/dist/policy/sources/PnpPolicySource.js +30 -28
  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 +49 -56
  66. package/dist/services/TTSService.js +308 -283
  67. package/dist/services/ToolCoordinator.d.ts +6 -49
  68. package/dist/services/ToolCoordinator.js +25 -61
  69. package/dist/services/ToolRegistry.d.ts +82 -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 +47 -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 +7 -23
  83. package/dist/services/tool-context.js +99 -53
  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 +1 -0
  98. package/dist/services/toolbar-decided-tools.js +1 -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 +4 -20
  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 +6 -5
  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
@@ -3,8 +3,6 @@
3
3
  *
4
4
  * Centralized registry for managing tool providers.
5
5
  * Handles initialization, authentication, and lazy loading.
6
- *
7
- * Part of PIE Assessment Toolkit.
8
6
  */
9
7
  import type { ToolProviderApi, ToolCategory } from "./ToolProviderApi.js";
10
8
  /**
@@ -36,7 +34,7 @@ export interface ToolProviderRegistration<TConfig = any> {
36
34
  * @example
37
35
  * ```typescript
38
36
  * authFetcher: async () => {
39
- * const response = await fetch('/api/desmos/token');
37
+ * const response = await fetch('/api/tools/desmos/auth');
40
38
  * return response.json(); // { apiKey: '...' }
41
39
  * }
42
40
  * ```
@@ -90,9 +88,6 @@ export declare class ToolProviderRegistry {
90
88
  * A registration under an id already registered replaces it: the replaced
91
89
  * provider is destroyed once a start in progress settles, and a caller
92
90
  * waiting on that start gets the replacement.
93
- *
94
- * @param toolId Id of the tool the provider serves
95
- * @param config Provider configuration
96
91
  */
97
92
  register(toolId: string, config: ToolProviderRegistration): void;
98
93
  /**
@@ -100,10 +95,7 @@ export declare class ToolProviderRegistry {
100
95
  *
101
96
  * Fetches auth if needed and initializes the provider.
102
97
  * Safe to call multiple times - subsequent calls wait for first initialization.
103
- *
104
- * @param toolId Tool whose provider to initialize
105
- * @returns Promise that resolves when initialization complete
106
- * @throws Error if provider not registered or initialization fails
98
+ * Throws when the provider is not registered or its initialization fails.
107
99
  */
108
100
  initialize(toolId: string): Promise<void>;
109
101
  /** Whether a registration replaced `provider` under `toolId`. */
@@ -117,51 +109,24 @@ export declare class ToolProviderRegistry {
117
109
  * Get a provider instance
118
110
  *
119
111
  * Retrieves a registered provider. If autoInitialize is true and provider
120
- * is not initialized, initializes it first.
121
- *
122
- * @param toolId Id of the tool the provider serves
123
- * @param autoInitialize Auto-initialize if not ready (default: true)
124
- * @returns Provider instance
125
- * @throws Error if provider not registered
112
+ * is not initialized, initializes it first. Throws when the provider is not
113
+ * registered.
126
114
  */
127
115
  getProvider<T extends ToolProviderApi = ToolProviderApi>(toolId: string, autoInitialize?: boolean): Promise<T>;
128
- /**
129
- * The ids of the tools whose provider is in `category`.
130
- *
131
- * @param category Tool category to filter by
132
- */
116
+ /** The ids of the tools whose provider is in `category`. */
133
117
  getProvidersByCategory(category: ToolCategory): string[];
134
118
  /**
135
119
  * The ids of the tools with a registered provider.
136
120
  */
137
121
  getProviderIds(): string[];
138
- /**
139
- * Check if provider is registered
140
- *
141
- * @param toolId Id of the tool the provider serves
142
- * @returns true if provider is registered
143
- */
144
122
  has(toolId: string): boolean;
145
- /**
146
- * Check if provider is initialized
147
- *
148
- * @param toolId Id of the tool the provider serves
149
- * @returns true if provider is initialized
150
- */
151
123
  isInitialized(toolId: string): boolean;
152
- /**
153
- * Check if provider is currently initializing
154
- *
155
- * @param toolId Id of the tool the provider serves
156
- * @returns true if initialization in progress
157
- */
158
124
  isInitializing(toolId: string): boolean;
159
125
  /**
160
126
  * Unregister and destroy a provider
161
127
  *
162
- * Removes the provider from the registry and calls its destroy method.
163
- *
164
- * @param toolId Tool whose provider to unregister
128
+ * Removes the provider from the registry and calls its destroy method,
129
+ * after any start in progress settles.
165
130
  */
166
131
  unregister(toolId: string): Promise<void>;
167
132
  /**
@@ -3,12 +3,17 @@
3
3
  *
4
4
  * Centralized registry for managing tool providers.
5
5
  * Handles initialization, authentication, and lazy loading.
6
- *
7
- * Part of PIE Assessment Toolkit.
8
6
  */
9
7
  import { createPieLogger, isGlobalDebugEnabled, } from "@pie-players/pie-players-shared/pie";
10
8
  // Lifecycle lines need `window.PIE_DEBUG = true`, read on each line.
11
9
  const logger = createPieLogger("ToolProviderRegistry", isGlobalDebugEnabled);
10
+ /** The `backend` a provider config names, for telemetry payloads. */
11
+ function telemetryBackend(value) {
12
+ const backend = value && typeof value === "object"
13
+ ? value.backend
14
+ : undefined;
15
+ return typeof backend === "string" && backend ? backend : "unknown";
16
+ }
12
17
  /**
13
18
  * Tool Provider Registry
14
19
  *
@@ -59,9 +64,6 @@ export class ToolProviderRegistry {
59
64
  * A registration under an id already registered replaces it: the replaced
60
65
  * provider is destroyed once a start in progress settles, and a caller
61
66
  * waiting on that start gets the replacement.
62
- *
63
- * @param toolId Id of the tool the provider serves
64
- * @param config Provider configuration
65
67
  */
66
68
  register(toolId, config) {
67
69
  const replaced = this.providers.get(toolId);
@@ -81,7 +83,6 @@ export class ToolProviderRegistry {
81
83
  }
82
84
  }
83
85
  logger.debug(`${replaced ? "Replaced" : "Registered"} provider "${toolId}" (${config.provider.providerName})`);
84
- // Initialize immediately if not lazy
85
86
  if (config.lazy === false) {
86
87
  this.initialize(toolId).catch((error) => {
87
88
  console.error(`[ToolProviderRegistry] Failed to initialize provider "${toolId}":`, error);
@@ -93,22 +94,16 @@ export class ToolProviderRegistry {
93
94
  *
94
95
  * Fetches auth if needed and initializes the provider.
95
96
  * Safe to call multiple times - subsequent calls wait for first initialization.
96
- *
97
- * @param toolId Tool whose provider to initialize
98
- * @returns Promise that resolves when initialization complete
99
- * @throws Error if provider not registered or initialization fails
97
+ * Throws when the provider is not registered or its initialization fails.
100
98
  */
101
99
  async initialize(toolId) {
102
- // Already initialized
103
100
  if (this.initialized.get(toolId)) {
104
101
  return;
105
102
  }
106
- // Initialization in progress - wait for it
107
103
  const existingPromise = this.initializationPromises.get(toolId);
108
104
  if (existingPromise) {
109
105
  return existingPromise;
110
106
  }
111
- // Start new initialization
112
107
  const provider = this.providers.get(toolId);
113
108
  const initPromise = this._doInitialize(toolId);
114
109
  this.initializationPromises.set(toolId, initPromise);
@@ -116,6 +111,8 @@ export class ToolProviderRegistry {
116
111
  await initPromise;
117
112
  }
118
113
  catch (error) {
114
+ // When a registration replaced the provider mid-start, this caller
115
+ // gets the replacement, so the old start's failure is dropped.
119
116
  if (!this.isReplaced(toolId, provider))
120
117
  throw error;
121
118
  }
@@ -150,26 +147,18 @@ export class ToolProviderRegistry {
150
147
  if (!provider || !config) {
151
148
  throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
152
149
  }
153
- // Start with base config
154
150
  let providerConfig = { ...config.config };
155
- const deriveBackend = (value) => {
156
- const backend = value && typeof value === "object"
157
- ? value.backend
158
- : undefined;
159
- return typeof backend === "string" && backend ? backend : "unknown";
160
- };
161
151
  const providerInitStartedAt = Date.now();
162
152
  await this.emitTelemetry(config, "pie-tool-init-start", {
163
153
  toolId,
164
- backend: deriveBackend(providerConfig),
154
+ backend: telemetryBackend(providerConfig),
165
155
  operation: "provider-initialize",
166
156
  });
167
- // Fetch auth if needed
168
157
  if (provider.requiresAuth && config.authFetcher) {
169
158
  const authFetchStartedAt = Date.now();
170
159
  await this.emitTelemetry(config, "pie-tool-backend-call-start", {
171
160
  toolId,
172
- backend: deriveBackend(providerConfig),
161
+ backend: telemetryBackend(providerConfig),
173
162
  operation: "auth-fetch",
174
163
  });
175
164
  logger.debug(`Fetching auth for "${toolId}"...`);
@@ -178,7 +167,7 @@ export class ToolProviderRegistry {
178
167
  providerConfig = { ...providerConfig, ...authData };
179
168
  await this.emitTelemetry(config, "pie-tool-backend-call-success", {
180
169
  toolId,
181
- backend: deriveBackend(providerConfig),
170
+ backend: telemetryBackend(providerConfig),
182
171
  operation: "auth-fetch",
183
172
  duration: Date.now() - authFetchStartedAt,
184
173
  });
@@ -186,7 +175,7 @@ export class ToolProviderRegistry {
186
175
  catch (error) {
187
176
  await this.emitTelemetry(config, "pie-tool-backend-call-error", {
188
177
  toolId,
189
- backend: deriveBackend(providerConfig),
178
+ backend: telemetryBackend(providerConfig),
190
179
  operation: "auth-fetch",
191
180
  duration: Date.now() - authFetchStartedAt,
192
181
  errorType: "ProviderAuthFetchError",
@@ -196,7 +185,6 @@ export class ToolProviderRegistry {
196
185
  throw new Error(`Failed to fetch auth credentials for provider "${toolId}"`);
197
186
  }
198
187
  }
199
- // Initialize provider
200
188
  try {
201
189
  await provider.initialize(providerConfig);
202
190
  if (this.providers.get(toolId) === provider) {
@@ -204,7 +192,7 @@ export class ToolProviderRegistry {
204
192
  }
205
193
  await this.emitTelemetry(config, "pie-tool-init-success", {
206
194
  toolId,
207
- backend: deriveBackend(providerConfig),
195
+ backend: telemetryBackend(providerConfig),
208
196
  operation: "provider-initialize",
209
197
  duration: Date.now() - providerInitStartedAt,
210
198
  });
@@ -213,7 +201,7 @@ export class ToolProviderRegistry {
213
201
  catch (error) {
214
202
  await this.emitTelemetry(config, "pie-tool-init-error", {
215
203
  toolId,
216
- backend: deriveBackend(providerConfig),
204
+ backend: telemetryBackend(providerConfig),
217
205
  operation: "provider-initialize",
218
206
  duration: Date.now() - providerInitStartedAt,
219
207
  errorType: "ProviderInitializationError",
@@ -227,12 +215,8 @@ export class ToolProviderRegistry {
227
215
  * Get a provider instance
228
216
  *
229
217
  * Retrieves a registered provider. If autoInitialize is true and provider
230
- * is not initialized, initializes it first.
231
- *
232
- * @param toolId Id of the tool the provider serves
233
- * @param autoInitialize Auto-initialize if not ready (default: true)
234
- * @returns Provider instance
235
- * @throws Error if provider not registered
218
+ * is not initialized, initializes it first. Throws when the provider is not
219
+ * registered.
236
220
  */
237
221
  async getProvider(toolId, autoInitialize = true) {
238
222
  const provider = this.providers.get(toolId);
@@ -243,18 +227,13 @@ export class ToolProviderRegistry {
243
227
  });
244
228
  throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
245
229
  }
246
- // Auto-initialize if needed
247
230
  if (autoInitialize && !this.initialized.get(toolId)) {
248
231
  logger.debug("Auto-initializing provider:", toolId);
249
232
  await this.initialize(toolId);
250
233
  }
251
234
  return provider;
252
235
  }
253
- /**
254
- * The ids of the tools whose provider is in `category`.
255
- *
256
- * @param category Tool category to filter by
257
- */
236
+ /** The ids of the tools whose provider is in `category`. */
258
237
  getProvidersByCategory(category) {
259
238
  return Array.from(this.providers.entries())
260
239
  .filter(([_, provider]) => provider.category === category)
@@ -266,44 +245,24 @@ export class ToolProviderRegistry {
266
245
  getProviderIds() {
267
246
  return Array.from(this.providers.keys());
268
247
  }
269
- /**
270
- * Check if provider is registered
271
- *
272
- * @param toolId Id of the tool the provider serves
273
- * @returns true if provider is registered
274
- */
275
248
  has(toolId) {
276
249
  return this.providers.has(toolId);
277
250
  }
278
- /**
279
- * Check if provider is initialized
280
- *
281
- * @param toolId Id of the tool the provider serves
282
- * @returns true if provider is initialized
283
- */
284
251
  isInitialized(toolId) {
285
252
  return this.initialized.get(toolId) === true;
286
253
  }
287
- /**
288
- * Check if provider is currently initializing
289
- *
290
- * @param toolId Id of the tool the provider serves
291
- * @returns true if initialization in progress
292
- */
293
254
  isInitializing(toolId) {
294
255
  return this.initializationPromises.has(toolId);
295
256
  }
296
257
  /**
297
258
  * Unregister and destroy a provider
298
259
  *
299
- * Removes the provider from the registry and calls its destroy method.
300
- *
301
- * @param toolId Tool whose provider to unregister
260
+ * Removes the provider from the registry and calls its destroy method,
261
+ * after any start in progress settles.
302
262
  */
303
263
  async unregister(toolId) {
304
264
  const provider = this.providers.get(toolId);
305
265
  if (provider) {
306
- // Wait for any pending initialization
307
266
  const initPromise = this.initializationPromises.get(toolId);
308
267
  if (initPromise) {
309
268
  try {
@@ -318,7 +277,6 @@ export class ToolProviderRegistry {
318
277
  return;
319
278
  }
320
279
  this.destroyProvider(toolId, provider);
321
- // Remove from registry
322
280
  this.providers.delete(toolId);
323
281
  this.configs.delete(toolId);
324
282
  this.initialized.delete(toolId);
@@ -19,6 +19,7 @@
19
19
  * Core names no capability. The requester supplies the tool id, which is why the
20
20
  * pairing of a selection action to a dictionary lives in the composition layer.
21
21
  */
22
+ import type { ToolFailurePhase } from "./tool-failure.js";
22
23
  import type { ToolPlacementLevel } from "./tools-config-normalizer.js";
23
24
  /** Level a request resolves against when the requester names none. */
24
25
  export declare const DEFAULT_TOOL_REQUEST_LEVEL: ToolPlacementLevel;
@@ -79,8 +80,11 @@ export interface ToolRequestTarget {
79
80
  * earlier one is still mounted does not displace it.
80
81
  */
81
82
  export declare class ToolRequestRegistry {
83
+ private readonly onTargetFailure;
82
84
  private readonly targets;
83
85
  private readonly changeListeners;
86
+ /** `onTargetFailure` receives a target's throw, which the registry recovers from. */
87
+ constructor(onTargetFailure?: (toolId: string, phase: ToolFailurePhase, error: unknown) => void);
84
88
  registerTarget(target: ToolRequestTarget): () => void;
85
89
  /**
86
90
  * Whether a request for this tool would reach a toolbar.
@@ -28,8 +28,13 @@ export const DEFAULT_TOOL_REQUEST_LEVEL = "section";
28
28
  * earlier one is still mounted does not displace it.
29
29
  */
30
30
  export class ToolRequestRegistry {
31
+ onTargetFailure;
31
32
  targets = new Set();
32
33
  changeListeners = new Set();
34
+ /** `onTargetFailure` receives a target's throw, which the registry recovers from. */
35
+ constructor(onTargetFailure = (toolId, phase, error) => console.error(`[ToolRequestRegistry] "${toolId}" ${phase} failed:`, error)) {
36
+ this.onTargetFailure = onTargetFailure;
37
+ }
33
38
  registerTarget(target) {
34
39
  this.targets.add(target);
35
40
  this.notifyTargetsChange();
@@ -58,7 +63,7 @@ export class ToolRequestRegistry {
58
63
  target.open(request.toolId, request.params);
59
64
  }
60
65
  catch (error) {
61
- console.error(`[ToolRequestRegistry] Target failed to open "${request.toolId}":`, error);
66
+ this.onTargetFailure(request.toolId, "tool-request-open", error);
62
67
  return false;
63
68
  }
64
69
  return true;
@@ -115,7 +120,7 @@ export class ToolRequestRegistry {
115
120
  return target.hostsTool(toolId) === true;
116
121
  }
117
122
  catch (error) {
118
- console.warn(`[ToolRequestRegistry] Target at level "${target.level}" failed the host check for "${toolId}":`, error);
123
+ this.onTargetFailure(toolId, "tool-request-host-check", error);
119
124
  return false;
120
125
  }
121
126
  }
@@ -4,6 +4,7 @@ export interface DecidedToolbarTool {
4
4
  toolId: string;
5
5
  required: boolean;
6
6
  alwaysAvailable: boolean;
7
+ parameters?: Record<string, unknown>;
7
8
  }
8
9
  /**
9
10
  * A toolbar's decided tools across re-decisions. The tracker returns its
@@ -17,6 +17,7 @@ export function createDecidedToolsTracker() {
17
17
  toolId: entry.toolId,
18
18
  required: entry.required,
19
19
  alwaysAvailable: entry.alwaysAvailable,
20
+ parameters: entry.parameters,
20
21
  }));
21
22
  if (last?.renderVersion === renderVersion &&
22
23
  structurallyEqual(last.tools, tools)) {
@@ -19,13 +19,10 @@ export function isValidToolbarItemShape(value) {
19
19
  return false;
20
20
  if (item.disabled !== undefined && typeof item.disabled !== "boolean")
21
21
  return false;
22
+ // A link or a button: exactly one of `href` and `onClick`.
22
23
  const hasHref = typeof item.href === "string";
23
24
  const hasOnClick = typeof item.onClick === "function";
24
- if (hasHref === hasOnClick)
25
- return false;
26
- if (hasHref)
27
- return true;
28
- return hasOnClick;
25
+ return hasHref !== hasOnClick;
29
26
  }
30
27
  export function isInlineSvgIcon(icon) {
31
28
  return typeof icon === "string" && icon.trimStart().startsWith("<svg");
@@ -76,16 +76,16 @@ export interface ToolProvidersConfig {
76
76
  }
77
77
  /**
78
78
  * PNP/profile enforcement mode, set as `tools.pnpEnforcement` (on a section
79
- * player, `runtime.tools.pnpEnforcement`).
79
+ * player, `runtime.tools.pnpEnforcement`) or through `setPnpEnforcement`.
80
80
  *
81
- * - `"on"` — force enforcement (engine applies PNP/profile gates).
82
- * - `"off"` — opt out (engine ignores PNP/profile inputs).
83
- * - omitted / `undefined` — auto-mode (the default). The toolkit
84
- * coordinator computes the effective mode from PNP/profile policy material on the
85
- * bound `AssessmentEntity` / `AssessmentItemRef`. See
81
+ * - `"on"` — toolbar decisions apply PNP/profile policy.
82
+ * - `"off"` — toolbar decisions skip it. Feature decisions read it either way.
83
+ * - omitted — auto-mode (the default): on when the bound assessment carries
84
+ * PNP/profile policy material, or, for a decision scoped to an item, when
85
+ * that item's settings require or restrict a tool. See
86
86
  * `resolveDefaultPnpEnforcement` in `policy/core/pnp-policy-inputs.ts`.
87
87
  */
88
- export type ToolsPnpEnforcement = "on" | "off";
88
+ export type PnpEnforcementMode = "on" | "off";
89
89
  export interface CanonicalToolsConfig {
90
90
  policy: ToolPolicyConfig;
91
91
  placement: Required<ToolPlacementConfig>;
@@ -93,9 +93,9 @@ export interface CanonicalToolsConfig {
93
93
  /**
94
94
  * Optional PNP/profile enforcement override, read by
95
95
  * `<pie-assessment-toolkit>` and `ToolkitCoordinator`. See
96
- * {@link ToolsPnpEnforcement} for semantics.
96
+ * {@link PnpEnforcementMode} for semantics.
97
97
  */
98
- pnpEnforcement?: ToolsPnpEnforcement;
98
+ pnpEnforcement?: PnpEnforcementMode;
99
99
  }
100
100
  /**
101
101
  * The tools config a host supplies. Every part is optional, placement levels
@@ -104,6 +104,15 @@ export interface CanonicalToolsConfig {
104
104
  export type ToolsConfigInput = Omit<Partial<CanonicalToolsConfig>, "placement"> & {
105
105
  placement?: ToolPlacementConfig;
106
106
  };
107
+ /**
108
+ * Tool ids trimmed, with empty ones dropped and duplicates removed in
109
+ * first-seen order. Throws on a non-string entry.
110
+ */
107
111
  export declare function normalizeToolList(toolIds: string[] | undefined | null): string[];
108
112
  export declare function parseToolList(input: string | undefined | null): string[];
113
+ /**
114
+ * Check a host's tools config shape, throwing at the first malformed field, and
115
+ * return it canonical: tool lists trimmed and deduplicated, and every placement
116
+ * level that names no tool filled with its default.
117
+ */
109
118
  export declare function normalizeToolsConfig(input?: ToolsConfigInput | null): CanonicalToolsConfig;
@@ -1,12 +1,10 @@
1
1
  import { DEFAULT_TOOL_PLACEMENT } from "./tool-config-defaults.js";
2
+ import { isPlainRecord } from "@pie-players/pie-players-shared/object";
2
3
  const DEFAULT_PLACEMENT = {
3
4
  section: [...DEFAULT_TOOL_PLACEMENT.section],
4
5
  item: [...DEFAULT_TOOL_PLACEMENT.item],
5
6
  passage: [...DEFAULT_TOOL_PLACEMENT.passage],
6
7
  };
7
- function isPlainObject(value) {
8
- return typeof value === "object" && value !== null && !Array.isArray(value);
9
- }
10
8
  function assertStringArray(value, fieldPath) {
11
9
  if (value == null)
12
10
  return [];
@@ -22,7 +20,7 @@ function assertStringArray(value, fieldPath) {
22
20
  function assertPlacementConfig(value) {
23
21
  if (value == null)
24
22
  return undefined;
25
- if (!isPlainObject(value)) {
23
+ if (!isPlainRecord(value)) {
26
24
  throw new Error('Invalid tools config at "placement": expected an object with section/item/passage arrays.');
27
25
  }
28
26
  return value;
@@ -30,7 +28,7 @@ function assertPlacementConfig(value) {
30
28
  function assertPolicyConfig(value) {
31
29
  if (value == null)
32
30
  return undefined;
33
- if (!isPlainObject(value)) {
31
+ if (!isPlainRecord(value)) {
34
32
  throw new Error('Invalid tools config at "policy": expected an object with allowed/blocked arrays.');
35
33
  }
36
34
  return value;
@@ -38,7 +36,7 @@ function assertPolicyConfig(value) {
38
36
  function assertProviderConfig(providerId, value) {
39
37
  if (value == null)
40
38
  return undefined;
41
- if (!isPlainObject(value)) {
39
+ if (!isPlainRecord(value)) {
42
40
  throw new Error(`Invalid tools config at "providers.${providerId}": expected an object.`);
43
41
  }
44
42
  const config = value;
@@ -49,12 +47,12 @@ function assertProviderConfig(providerId, value) {
49
47
  }
50
48
  if ("settings" in config &&
51
49
  config.settings !== undefined &&
52
- !isPlainObject(config.settings)) {
50
+ !isPlainRecord(config.settings)) {
53
51
  throw new Error(`Invalid tools config at "providers.${providerId}.settings": expected an object.`);
54
52
  }
55
53
  if ("provider" in config &&
56
54
  config.provider !== undefined &&
57
- !isPlainObject(config.provider)) {
55
+ !isPlainRecord(config.provider)) {
58
56
  throw new Error(`Invalid tools config at "providers.${providerId}.provider": expected an object.`);
59
57
  }
60
58
  return config;
@@ -62,7 +60,7 @@ function assertProviderConfig(providerId, value) {
62
60
  function assertProvidersConfig(value) {
63
61
  if (value == null)
64
62
  return {};
65
- if (!isPlainObject(value)) {
63
+ if (!isPlainRecord(value)) {
66
64
  throw new Error('Invalid tools config at "providers": expected an object keyed by tool id.');
67
65
  }
68
66
  const normalized = {};
@@ -71,6 +69,10 @@ function assertProvidersConfig(value) {
71
69
  }
72
70
  return normalized;
73
71
  }
72
+ /**
73
+ * Tool ids trimmed, with empty ones dropped and duplicates removed in
74
+ * first-seen order. Throws on a non-string entry.
75
+ */
74
76
  export function normalizeToolList(toolIds) {
75
77
  if (!toolIds || toolIds.length === 0)
76
78
  return [];
@@ -101,8 +103,18 @@ function assertPnpEnforcement(value, fieldPath) {
101
103
  return value;
102
104
  throw new Error(`Invalid tools config at "${fieldPath}": expected "on" or "off", got ${JSON.stringify(value)}.`);
103
105
  }
106
+ /** A placement level's tool ids, or the level's default when it names none. */
107
+ function normalizePlacementLevel(placement, level) {
108
+ const toolIds = normalizeToolList(assertStringArray(placement?.[level], `placement.${level}`));
109
+ return toolIds.length ? toolIds : [...DEFAULT_PLACEMENT[level]];
110
+ }
111
+ /**
112
+ * Check a host's tools config shape, throwing at the first malformed field, and
113
+ * return it canonical: tool lists trimmed and deduplicated, and every placement
114
+ * level that names no tool filled with its default.
115
+ */
104
116
  export function normalizeToolsConfig(input) {
105
- if (input != null && !isPlainObject(input)) {
117
+ if (input != null && !isPlainRecord(input)) {
106
118
  throw new Error('Invalid tools config: expected an object with "policy", "placement", and "providers".');
107
119
  }
108
120
  const policy = assertPolicyConfig(input?.policy);
@@ -115,15 +127,9 @@ export function normalizeToolsConfig(input) {
115
127
  blocked: normalizeToolList(assertStringArray(policy?.blocked, "policy.blocked")),
116
128
  },
117
129
  placement: {
118
- section: normalizeToolList(assertStringArray(placement?.section, "placement.section")).length
119
- ? normalizeToolList(assertStringArray(placement?.section, "placement.section"))
120
- : [...DEFAULT_PLACEMENT.section],
121
- item: normalizeToolList(assertStringArray(placement?.item, "placement.item")).length
122
- ? normalizeToolList(assertStringArray(placement?.item, "placement.item"))
123
- : [...DEFAULT_PLACEMENT.item],
124
- passage: normalizeToolList(assertStringArray(placement?.passage, "placement.passage")).length
125
- ? normalizeToolList(assertStringArray(placement?.passage, "placement.passage"))
126
- : [...DEFAULT_PLACEMENT.passage],
130
+ section: normalizePlacementLevel(placement, "section"),
131
+ item: normalizePlacementLevel(placement, "item"),
132
+ passage: normalizePlacementLevel(placement, "passage"),
127
133
  },
128
134
  providers,
129
135
  };
@@ -3,16 +3,15 @@
3
3
  *
4
4
  * Text-to-speech provider using the browser's Web Speech API.
5
5
  * Works in all modern browsers that support SpeechSynthesis.
6
- *
7
- * Part of PIE Assessment Toolkit.
8
6
  */
9
7
  import type { ITTSProvider, ITTSProviderImplementation, TTSConfig, TTSProviderCapabilities } from "@pie-players/pie-tts";
8
+ import type { ToolkitTTSConfig } from "./provider-options.js";
10
9
  /**
11
10
  * The part of another provider's configuration that keeps its meaning on
12
11
  * browser speech. Every path that replaces a provider with browser speech
13
12
  * starts it with this.
14
13
  */
15
- export declare const browserFallbackConfig: (config: Partial<TTSConfig>) => Partial<TTSConfig>;
14
+ export declare const browserFallbackConfig: (config: Partial<ToolkitTTSConfig>) => Partial<ToolkitTTSConfig>;
16
15
  /**
17
16
  * The voice traits browser voice selection reads. A `SpeechSynthesisVoice` has
18
17
  * them all; a host's own voice list may carry a subset.
@@ -51,8 +50,6 @@ export declare const waitForBrowserVoices: (synth: SpeechSynthesis, timeoutMs: n
51
50
  */
52
51
  export declare class BrowserTTSProvider implements ITTSProvider {
53
52
  readonly providerId = "browser";
54
- readonly providerName = "Browser Speech Synthesis";
55
- readonly version = "1.0.0";
56
53
  initialize(config: TTSConfig): Promise<ITTSProviderImplementation>;
57
54
  getCapabilities(): TTSProviderCapabilities;
58
55
  destroy(): void;