@pie-players/pie-assessment-toolkit 0.3.72 → 0.3.74
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.
- package/README.md +171 -56
- package/dist/attempt/AssessmentSession.d.ts +1 -5
- package/dist/attempt/AssessmentSession.js +0 -29
- package/dist/components/ItemToolBar.custom-element.d.ts +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +40 -0
- package/dist/components/chunks/ItemToolBar-gmrryv2v.js +2 -0
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +4 -0
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +2 -0
- package/dist/components/chunks/sre-engine-wf9s43gg.js +1 -0
- package/dist/context/assessment-toolkit-context.d.ts +5 -0
- package/dist/context/runtime-context-consumer.js +5 -38
- package/dist/index.d.ts +6 -6
- package/dist/index.js +8 -9
- package/dist/policy/core/ToolPolicyEngine.d.ts +8 -2
- package/dist/policy/core/ToolPolicyEngine.js +12 -0
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +3 -3
- package/dist/runtime/adapter/SectionEngineAdapter.js +3 -3
- package/dist/runtime/registration-events.d.ts +3 -0
- package/dist/runtime/section-runtime-engine-host-context.d.ts +3 -9
- package/dist/runtime/section-runtime-engine-host-context.js +5 -43
- package/dist/runtime/tool-host-contract.d.ts +20 -0
- package/dist/runtime/tool-host-contract.js +21 -0
- package/dist/services/HighlightCoordinator.d.ts +1 -7
- package/dist/services/HighlightCoordinator.js +1 -3
- package/dist/services/TTSService.d.ts +15 -1
- package/dist/services/TTSService.js +88 -35
- package/dist/services/ToolCoordinator.d.ts +1 -7
- package/dist/services/ToolCoordinator.js +1 -3
- package/dist/services/ToolRegistry.d.ts +13 -6
- package/dist/services/ToolRegistry.js +19 -7
- package/dist/services/ToolkitCoordinator.d.ts +110 -18
- package/dist/services/ToolkitCoordinator.js +273 -85
- package/dist/services/catalog-owner.d.ts +11 -0
- package/dist/services/catalog-owner.js +25 -0
- package/dist/services/create-tools-config.d.ts +2 -2
- package/dist/services/framework-error.d.ts +8 -1
- package/dist/services/interfaces.d.ts +9 -9
- package/dist/services/pnp-standard-features.d.ts +1 -1
- package/dist/services/section-controller-types.d.ts +30 -0
- package/dist/services/spoken-audio-cards.js +1 -1
- package/dist/services/tool-config-validation.d.ts +13 -2
- package/dist/services/tool-config-validation.js +33 -16
- package/dist/services/tool-context.js +29 -13
- package/dist/services/tool-providers/TTSToolProvider.d.ts +27 -65
- package/dist/services/tool-providers/TTSToolProvider.js +47 -9
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +4 -4
- package/dist/services/tool-providers/ToolProviderRegistry.js +4 -16
- package/dist/services/tool-providers/index.d.ts +0 -9
- package/dist/services/tool-providers/index.js +2 -4
- package/dist/services/tools-config-normalizer.d.ts +20 -1
- package/dist/services/tts/catalog-span-alignment.d.ts +2 -17
- package/dist/services/tts/catalog-span-alignment.js +17 -142
- package/dist/services/tts/generated-speech/types.d.ts +0 -12
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +26 -1
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
- package/dist/services/tts/math-alignment/index.d.ts +0 -5
- package/dist/services/tts/math-alignment/index.js +5 -25
- package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
- package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
- package/dist/services/tts/math-alignment/types.d.ts +4 -2
- package/dist/services/tts/math-aware-text-processing.js +44 -2
- package/dist/services/tts/math-speech.d.ts +5 -1
- package/dist/services/tts/math-speech.js +9 -6
- package/dist/services/tts/sre-engine.d.ts +7 -0
- package/dist/services/tts/sre-engine.js +11 -0
- package/dist/services/tts/sre-locales.d.ts +14 -0
- package/dist/services/tts/sre-locales.js +57 -0
- package/dist/services/tts/text-processing.d.ts +0 -1
- package/dist/services/tts/text-processing.js +0 -1
- package/dist/services/tts-runtime-config.d.ts +21 -3
- package/dist/services/tts-runtime-config.js +11 -3
- package/dist/tools/client.d.ts +1 -3
- package/dist/tools/client.js +1 -5
- package/dist/tools/internal.d.ts +6 -5
- package/dist/tools/internal.js +6 -8
- package/package.json +17 -36
- package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
- package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
- package/dist/services/ToolConfigResolver.d.ts +0 -161
- package/dist/services/ToolConfigResolver.js +0 -229
- package/dist/services/catalog-media.d.ts +0 -56
- package/dist/services/catalog-media.js +0 -190
- package/dist/services/createDefaultToolRegistry.d.ts +0 -53
- package/dist/services/createDefaultToolRegistry.js +0 -45
- package/dist/services/tool-providers/CortexToolProvider.d.ts +0 -13
- package/dist/services/tool-providers/CortexToolProvider.js +0 -32
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
- package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
- package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
- package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
- package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
- package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
- package/dist/services/tts/visual-line-ranges.d.ts +0 -18
- package/dist/services/tts/visual-line-ranges.js +0 -226
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -10
- package/dist/tools/response-discovery.d.ts +0 -61
- package/dist/tools/response-discovery.js +0 -113
- package/dist/tools/types.d.ts +0 -337
- package/dist/tools/types.js +0 -12
- package/dist/tools/variant-resolver.d.ts +0 -41
- package/dist/tools/variant-resolver.js +0 -207
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Part of PIE Assessment Toolkit.
|
|
15
15
|
*/
|
|
16
16
|
import { normalizeToolsConfig, } from "./tools-config-normalizer.js";
|
|
17
|
-
import {
|
|
17
|
+
import { collectToolConfigDiagnostics, normalizeToolConfigStrictness, reportToolConfigDiagnostics, } from "./tool-config-validation.js";
|
|
18
18
|
import { AccessibilityCatalogResolver } from "./AccessibilityCatalogResolver.js";
|
|
19
19
|
import { ElementToolStateStore } from "./ElementToolStateStore.js";
|
|
20
20
|
import { frameworkErrorFromCoordinatorContext, } from "./framework-error.js";
|
|
@@ -25,7 +25,7 @@ import { TTSService } from "./TTSService.js";
|
|
|
25
25
|
import { BrowserTTSProvider } from "./tts/browser-provider.js";
|
|
26
26
|
import { buildRuntimeTTSConfig, resolveTTSBackend, resolveTTSRuntimeSettings, } from "./tts-runtime-config.js";
|
|
27
27
|
import { ToolProviderRegistry } from "./tool-providers/index.js";
|
|
28
|
-
import { ToolRegistry } from "./ToolRegistry.js";
|
|
28
|
+
import { resolveToolProviderId, ToolRegistry } from "./ToolRegistry.js";
|
|
29
29
|
import { ToolRequestRegistry } from "./tool-request.js";
|
|
30
30
|
import { ToolPolicyEngine, } from "../policy/engine.js";
|
|
31
31
|
import { resolveDefaultPnpEnforcement } from "../policy/internal.js";
|
|
@@ -81,9 +81,13 @@ const SECTION_SCOPED_EVENT_TYPES = [
|
|
|
81
81
|
*
|
|
82
82
|
* @example
|
|
83
83
|
* ```typescript
|
|
84
|
+
* // createPackagedToolRegistry is exported by @pie-players/pie-default-tool-loaders
|
|
85
|
+
* const toolRegistry = createPackagedToolRegistry();
|
|
86
|
+
*
|
|
84
87
|
* // Create coordinator with configuration
|
|
85
88
|
* const coordinator = new ToolkitCoordinator({
|
|
86
89
|
* assessmentId: 'demo-three-questions',
|
|
90
|
+
* toolRegistry,
|
|
87
91
|
* tools: {
|
|
88
92
|
* providers: {
|
|
89
93
|
* textToSpeech: { enabled: true, backend: 'browser' },
|
|
@@ -96,7 +100,7 @@ const SECTION_SCOPED_EVENT_TYPES = [
|
|
|
96
100
|
* });
|
|
97
101
|
*
|
|
98
102
|
* // Pass to section player
|
|
99
|
-
* player.
|
|
103
|
+
* player.runtime = { ...(player.runtime ?? {}), coordinator };
|
|
100
104
|
*
|
|
101
105
|
* // Access services directly
|
|
102
106
|
* const ttsService = coordinator.ttsService;
|
|
@@ -127,6 +131,14 @@ export class ToolkitCoordinator {
|
|
|
127
131
|
coordinatorReadyNotified = false;
|
|
128
132
|
providerInitPromises = new Map();
|
|
129
133
|
toolRegistry;
|
|
134
|
+
/** Whether the host passed `toolRegistry`, which then always stands. */
|
|
135
|
+
toolRegistrySupplied;
|
|
136
|
+
/**
|
|
137
|
+
* Whether the registry is final: supplied at construction, or adopted from
|
|
138
|
+
* the toolkit this coordinator is bound to. Until then the empty placeholder
|
|
139
|
+
* reports no missing registry, since the toolkit will supply one.
|
|
140
|
+
*/
|
|
141
|
+
toolRegistrySettled;
|
|
130
142
|
toolContextResolvers = new Map();
|
|
131
143
|
toolContextResolverChangeListeners = new Set();
|
|
132
144
|
toolRequests = new ToolRequestRegistry();
|
|
@@ -213,22 +225,40 @@ export class ToolkitCoordinator {
|
|
|
213
225
|
* lifetime for no new information.
|
|
214
226
|
*/
|
|
215
227
|
reportedUnboundFeaturePolicy = false;
|
|
216
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Whether {@link _initializeTTS} has already reported a non-browser backend
|
|
230
|
+
* falling back to browser speech because no `tts` provider is registered.
|
|
231
|
+
* Once per coordinator: a text-to-speech config change re-runs
|
|
232
|
+
* initialization, and the missing provider is the same gap each time.
|
|
233
|
+
*/
|
|
234
|
+
reportedMissingTTSProvider = false;
|
|
235
|
+
/**
|
|
236
|
+
* The backend of a fallback {@link reportMissingTTSProvider} held back while
|
|
237
|
+
* the registry was unsettled, reported if the bound toolkit has no registry.
|
|
238
|
+
*/
|
|
239
|
+
heldMissingTTSProviderBackend = null;
|
|
240
|
+
/**
|
|
241
|
+
* Whether {@link validateToolsConfig} has already reported validating with
|
|
242
|
+
* no registry (`tools.registryUnavailable`). Once per coordinator: every
|
|
243
|
+
* {@link updateToolConfig} and {@link updateToolsPlacement} re-validates, and
|
|
244
|
+
* the missing registry is the same gap each time.
|
|
245
|
+
*/
|
|
246
|
+
reportedRegistryUnavailable = false;
|
|
247
|
+
resolveConfig(config) {
|
|
217
248
|
const strictness = normalizeToolConfigStrictness(config.toolConfigStrictness);
|
|
218
|
-
// An empty
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
// against an empty registry (with a diagnostic saying so) rather than
|
|
249
|
+
// An empty placeholder when the host supplies none, replaced by the bound
|
|
250
|
+
// toolkit's registry in `adoptToolRegistry`. This package does not hold the
|
|
251
|
+
// packaged capability set, and importing it would be a dependency cycle.
|
|
252
|
+
// Tool-id validation is skipped against an empty registry rather than
|
|
223
253
|
// rejecting every configured id.
|
|
224
254
|
const toolRegistry = config.toolRegistry ?? new ToolRegistry();
|
|
225
255
|
const normalized = config.deferToolConfigValidation === true
|
|
226
256
|
? normalizeToolsConfig(config.tools)
|
|
227
|
-
:
|
|
257
|
+
: this.validateToolsConfig(config.tools, {
|
|
228
258
|
strictness,
|
|
229
259
|
source: "ToolkitCoordinator.init",
|
|
230
260
|
toolRegistry,
|
|
231
|
-
})
|
|
261
|
+
});
|
|
232
262
|
const defaultProviders = {
|
|
233
263
|
textToSpeech: {
|
|
234
264
|
enabled: true,
|
|
@@ -256,11 +286,29 @@ export class ToolkitCoordinator {
|
|
|
256
286
|
},
|
|
257
287
|
};
|
|
258
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* `normalizeAndValidateToolsConfig`, except that `tools.registryUnavailable`
|
|
291
|
+
* is reported once per coordinator, whichever call validates.
|
|
292
|
+
*/
|
|
293
|
+
validateToolsConfig(tools, options) {
|
|
294
|
+
const { config, diagnostics } = collectToolConfigDiagnostics(tools, options.toolRegistry);
|
|
295
|
+
const unreported = this.reportedRegistryUnavailable || !this.toolRegistrySettled
|
|
296
|
+
? diagnostics.filter((entry) => entry.code !== "tools.registryUnavailable")
|
|
297
|
+
: diagnostics;
|
|
298
|
+
reportToolConfigDiagnostics(unreported, options);
|
|
299
|
+
if (options.strictness !== "off" &&
|
|
300
|
+
unreported.some((entry) => entry.code === "tools.registryUnavailable")) {
|
|
301
|
+
this.reportedRegistryUnavailable = true;
|
|
302
|
+
}
|
|
303
|
+
return config;
|
|
304
|
+
}
|
|
259
305
|
constructor(config) {
|
|
260
306
|
if (!config.assessmentId) {
|
|
261
307
|
throw new Error("ToolkitCoordinator requires assessmentId in config");
|
|
262
308
|
}
|
|
263
|
-
|
|
309
|
+
this.toolRegistrySupplied = config.toolRegistry != null;
|
|
310
|
+
this.toolRegistrySettled = this.toolRegistrySupplied;
|
|
311
|
+
const resolvedConfig = this.resolveConfig(config);
|
|
264
312
|
this.assessmentId = resolvedConfig.assessmentId;
|
|
265
313
|
this.config = resolvedConfig;
|
|
266
314
|
this.toolRegistry = resolvedConfig.toolRegistry ?? new ToolRegistry();
|
|
@@ -286,6 +334,9 @@ export class ToolkitCoordinator {
|
|
|
286
334
|
this._registerToolProviders();
|
|
287
335
|
// Initialize TTS service based on config
|
|
288
336
|
this.ttsService = new TTSService();
|
|
337
|
+
// Selection read-aloud speaks through this service without the inline TTS
|
|
338
|
+
// tool ever having run, so it cannot rely on that tool to attach highlights.
|
|
339
|
+
this.ttsService.setHighlightCoordinator(this.highlightCoordinator);
|
|
289
340
|
this.setupStatePersistenceHooks();
|
|
290
341
|
// M8 PR 2 — construct the unified ToolPolicyEngine seeded with
|
|
291
342
|
// the validated tools config. PNP/profile inputs (`assessment`,
|
|
@@ -527,6 +578,72 @@ export class ToolkitCoordinator {
|
|
|
527
578
|
});
|
|
528
579
|
return this.stateLoadPromise;
|
|
529
580
|
}
|
|
581
|
+
reportMissingTTSProvider(backend) {
|
|
582
|
+
// The toolkit this coordinator binds to may still supply the provider.
|
|
583
|
+
if (!this.toolRegistrySettled) {
|
|
584
|
+
this.heldMissingTTSProviderBackend = backend;
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (this.reportedMissingTTSProvider)
|
|
588
|
+
return;
|
|
589
|
+
this.reportedMissingTTSProvider = true;
|
|
590
|
+
console.warn(`[ToolkitCoordinator] Text-to-speech is configured for the "${backend}" backend but falls back to browser speech: no "tts" tool provider is registered. A coordinator registers tool providers only from its \`toolRegistry\`, or from its toolkit's when constructed without one, so supply one that carries the text-to-speech registration — for the packaged capability set, \`createPackagedToolRegistry()\` from "@pie-players/pie-default-tool-loaders". Reported once per coordinator.`);
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Take the registry of the toolkit this coordinator is bound to, when the host
|
|
594
|
+
* constructed it without one: the config is validated against it, its
|
|
595
|
+
* providers register, and text-to-speech re-initializes through it if it
|
|
596
|
+
* already started. The toolkit elements call this on binding. A registry
|
|
597
|
+
* passed at construction stands, and the first call wins; `null` records a
|
|
598
|
+
* toolkit without one, which reports the registry as missing.
|
|
599
|
+
*
|
|
600
|
+
* @returns Whether `registry` was adopted.
|
|
601
|
+
*/
|
|
602
|
+
adoptToolRegistry(registry) {
|
|
603
|
+
if (this.disposePromise !== null || this.toolRegistrySettled)
|
|
604
|
+
return false;
|
|
605
|
+
this.toolRegistrySettled = true;
|
|
606
|
+
const heldBackend = this.heldMissingTTSProviderBackend;
|
|
607
|
+
this.heldMissingTTSProviderBackend = null;
|
|
608
|
+
const strictness = this.config.toolConfigStrictness ?? "error";
|
|
609
|
+
const source = "ToolkitCoordinator.adoptToolRegistry";
|
|
610
|
+
if (!registry) {
|
|
611
|
+
this.validateToolsConfig(this.config.tools, {
|
|
612
|
+
strictness,
|
|
613
|
+
source,
|
|
614
|
+
toolRegistry: this.toolRegistry,
|
|
615
|
+
});
|
|
616
|
+
if (heldBackend)
|
|
617
|
+
this.reportMissingTTSProvider(heldBackend);
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
this.toolRegistry = registry;
|
|
621
|
+
this.config.toolRegistry = registry;
|
|
622
|
+
try {
|
|
623
|
+
// Warn at most: the config was accepted at construction, and a binding
|
|
624
|
+
// toolkit has no caller to throw to.
|
|
625
|
+
this.config.tools = this.validateToolsConfig(this.config.tools, {
|
|
626
|
+
strictness: strictness === "off" ? "off" : "warn",
|
|
627
|
+
source,
|
|
628
|
+
toolRegistry: registry,
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
catch (err) {
|
|
632
|
+
console.warn("[ToolkitCoordinator] Tool config does not validate against the adopted tool registry:", err);
|
|
633
|
+
}
|
|
634
|
+
this.policyEngine.replaceToolRegistry(registry, this.config.tools);
|
|
635
|
+
const ttsStarted = this.ttsInitialized ||
|
|
636
|
+
this.ttsInitPromise !== undefined ||
|
|
637
|
+
this.ttsReconfigurePromise !== undefined;
|
|
638
|
+
for (const tool of this.getProviderDescriptorTools()) {
|
|
639
|
+
if (ttsStarted && tool.toolId === "textToSpeech")
|
|
640
|
+
continue;
|
|
641
|
+
void this.registerProviderFromTool(tool);
|
|
642
|
+
}
|
|
643
|
+
if (ttsStarted)
|
|
644
|
+
this.scheduleTTSReconfigure();
|
|
645
|
+
return true;
|
|
646
|
+
}
|
|
530
647
|
/**
|
|
531
648
|
* Register tool providers in the registry
|
|
532
649
|
*/
|
|
@@ -539,40 +656,52 @@ export class ToolkitCoordinator {
|
|
|
539
656
|
getProviderDescriptorTools() {
|
|
540
657
|
return this.toolRegistry.getAllTools().filter((tool) => !!tool.provider);
|
|
541
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Register the provider `tool`'s descriptor creates for the tool's current
|
|
661
|
+
* config, unless the tool is disabled or that provider id is registered. A
|
|
662
|
+
* descriptor that throws is reported the way a failed registration is.
|
|
663
|
+
*/
|
|
542
664
|
async registerProviderFromTool(tool) {
|
|
543
665
|
const descriptor = tool.provider;
|
|
544
666
|
if (!descriptor)
|
|
545
667
|
return;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
toolConfig?.
|
|
551
|
-
|
|
552
|
-
|
|
668
|
+
let providerId = null;
|
|
669
|
+
let registration;
|
|
670
|
+
try {
|
|
671
|
+
const toolConfig = this.getToolConfig(tool.toolId) || undefined;
|
|
672
|
+
if (toolConfig?.enabled === false)
|
|
673
|
+
return;
|
|
674
|
+
providerId = resolveToolProviderId(tool, toolConfig);
|
|
675
|
+
if (!providerId || this.toolProviderRegistry.has(providerId))
|
|
676
|
+
return;
|
|
677
|
+
const provider = descriptor.createProvider(toolConfig);
|
|
678
|
+
const initConfig = descriptor.getInitConfig?.(toolConfig) ??
|
|
679
|
+
toolConfig?.provider?.init ??
|
|
680
|
+
{};
|
|
681
|
+
const initConfigWithTelemetry = this.addToolTelemetryReporter({
|
|
682
|
+
toolId: tool.toolId,
|
|
683
|
+
providerId,
|
|
684
|
+
initConfig,
|
|
685
|
+
});
|
|
686
|
+
const registryTelemetry = this.createToolTelemetryForwarder({
|
|
687
|
+
toolId: tool.toolId,
|
|
688
|
+
providerId,
|
|
689
|
+
});
|
|
690
|
+
const authFetcher = descriptor.getAuthFetcher?.(toolConfig) ??
|
|
691
|
+
toolConfig?.provider?.runtime?.authFetcher;
|
|
692
|
+
registration = {
|
|
693
|
+
provider,
|
|
694
|
+
config: initConfigWithTelemetry,
|
|
695
|
+
lazy: descriptor.lazy ?? true,
|
|
696
|
+
authFetcher,
|
|
697
|
+
onTelemetry: registryTelemetry,
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
catch (err) {
|
|
701
|
+
this.reportProviderRegisterFailure(err, providerId, tool.toolId);
|
|
553
702
|
return;
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
toolConfig?.provider?.init ??
|
|
557
|
-
{};
|
|
558
|
-
const initConfigWithTelemetry = this.addToolTelemetryReporter({
|
|
559
|
-
toolId: tool.toolId,
|
|
560
|
-
providerId,
|
|
561
|
-
initConfig,
|
|
562
|
-
});
|
|
563
|
-
const registryTelemetry = this.createToolTelemetryForwarder({
|
|
564
|
-
toolId: tool.toolId,
|
|
565
|
-
providerId,
|
|
566
|
-
});
|
|
567
|
-
const authFetcher = descriptor.getAuthFetcher?.(toolConfig) ??
|
|
568
|
-
toolConfig?.provider?.runtime?.authFetcher;
|
|
569
|
-
await this.registerProvider(providerId, {
|
|
570
|
-
provider,
|
|
571
|
-
config: initConfigWithTelemetry,
|
|
572
|
-
lazy: descriptor.lazy ?? true,
|
|
573
|
-
authFetcher,
|
|
574
|
-
onTelemetry: registryTelemetry,
|
|
575
|
-
});
|
|
703
|
+
}
|
|
704
|
+
await this.registerProvider(providerId, registration);
|
|
576
705
|
}
|
|
577
706
|
createToolTelemetryForwarder(args) {
|
|
578
707
|
return async (eventName, payload) => {
|
|
@@ -616,10 +745,22 @@ export class ToolkitCoordinator {
|
|
|
616
745
|
});
|
|
617
746
|
}
|
|
618
747
|
catch (err) {
|
|
619
|
-
|
|
620
|
-
this.handleError(err, { phase: "provider-register", providerId });
|
|
748
|
+
this.reportProviderRegisterFailure(err, providerId);
|
|
621
749
|
}
|
|
622
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* A console warning and a `provider-register` framework error. `providerId`
|
|
753
|
+
* is null when resolving it is what failed; the warning names the tool then.
|
|
754
|
+
*/
|
|
755
|
+
reportProviderRegisterFailure(err, providerId, toolId) {
|
|
756
|
+
console.warn(providerId
|
|
757
|
+
? `[ToolkitCoordinator] Failed to register provider "${providerId}":`
|
|
758
|
+
: `[ToolkitCoordinator] Failed to register the provider of tool "${toolId}":`, err);
|
|
759
|
+
this.handleError(err, {
|
|
760
|
+
phase: "provider-register",
|
|
761
|
+
providerId: providerId ?? undefined,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
623
764
|
async ensureProviderReady(providerId) {
|
|
624
765
|
this.assertNotDisposed();
|
|
625
766
|
const existing = this.providerInitPromises.get(providerId);
|
|
@@ -1505,6 +1646,9 @@ export class ToolkitCoordinator {
|
|
|
1505
1646
|
console.warn("[ToolkitCoordinator] Failed to initialize TTS via registry, falling back to browser provider:", normalized);
|
|
1506
1647
|
}
|
|
1507
1648
|
}
|
|
1649
|
+
else if (resolvedBackend !== "browser") {
|
|
1650
|
+
this.reportMissingTTSProvider(resolvedBackend);
|
|
1651
|
+
}
|
|
1508
1652
|
// Fallback to browser provider
|
|
1509
1653
|
const provider = new BrowserTTSProvider();
|
|
1510
1654
|
try {
|
|
@@ -1688,8 +1832,7 @@ export class ToolkitCoordinator {
|
|
|
1688
1832
|
};
|
|
1689
1833
|
}
|
|
1690
1834
|
getTTSConfigFromProviders() {
|
|
1691
|
-
|
|
1692
|
-
return providers.textToSpeech;
|
|
1835
|
+
return this.config.tools?.providers?.textToSpeech;
|
|
1693
1836
|
}
|
|
1694
1837
|
assertCanonicalToolId(toolId) {
|
|
1695
1838
|
if (typeof toolId !== "string" || toolId.trim().length === 0) {
|
|
@@ -1703,8 +1846,8 @@ export class ToolkitCoordinator {
|
|
|
1703
1846
|
// host's every tool-config call into an exception — including the calls
|
|
1704
1847
|
// this coordinator's own default-provider block provokes, which is how a
|
|
1705
1848
|
// host that passes no registry ended up unable to read its own config.
|
|
1706
|
-
//
|
|
1707
|
-
//
|
|
1849
|
+
// Validation reports the missing registry once per coordinator, as
|
|
1850
|
+
// `tools.registryUnavailable`; a report per call would be noise.
|
|
1708
1851
|
if (this.toolRegistry.getAllToolIds().length === 0)
|
|
1709
1852
|
return;
|
|
1710
1853
|
if (!this.toolRegistry.get(toolId)) {
|
|
@@ -1752,26 +1895,20 @@ export class ToolkitCoordinator {
|
|
|
1752
1895
|
// Enabled by default unless explicitly set to false
|
|
1753
1896
|
return toolConfig?.enabled !== false;
|
|
1754
1897
|
}
|
|
1755
|
-
/**
|
|
1756
|
-
* Get tool configuration.
|
|
1757
|
-
*
|
|
1758
|
-
* @param toolId Tool identifier
|
|
1759
|
-
* @returns Tool configuration or null if not configured
|
|
1760
|
-
*/
|
|
1761
1898
|
getToolConfig(toolId) {
|
|
1762
1899
|
this.assertCanonicalToolId(toolId);
|
|
1763
|
-
return
|
|
1900
|
+
return this.config.tools?.providers?.[toolId] || null;
|
|
1764
1901
|
}
|
|
1765
1902
|
updateToolConfig(toolId, updates) {
|
|
1766
1903
|
// Update config
|
|
1767
1904
|
this.assertCanonicalToolId(toolId);
|
|
1768
1905
|
const current = this.getToolConfig(toolId) || {};
|
|
1769
1906
|
if (!this.config.tools) {
|
|
1770
|
-
this.config.tools =
|
|
1907
|
+
this.config.tools = this.validateToolsConfig(undefined, {
|
|
1771
1908
|
strictness: this.config.toolConfigStrictness ?? "error",
|
|
1772
1909
|
source: "ToolkitCoordinator.updateToolConfig",
|
|
1773
1910
|
toolRegistry: this.toolRegistry,
|
|
1774
|
-
})
|
|
1911
|
+
});
|
|
1775
1912
|
}
|
|
1776
1913
|
if (!this.config.tools.providers) {
|
|
1777
1914
|
this.config.tools.providers = {};
|
|
@@ -1780,7 +1917,7 @@ export class ToolkitCoordinator {
|
|
|
1780
1917
|
...(this.config.tools.providers || {}),
|
|
1781
1918
|
[toolId]: mergeToolConfigUpdate(toolId, current, updates),
|
|
1782
1919
|
};
|
|
1783
|
-
|
|
1920
|
+
this.config.tools = this.validateToolsConfig({
|
|
1784
1921
|
...this.config.tools,
|
|
1785
1922
|
providers: nextProviders,
|
|
1786
1923
|
}, {
|
|
@@ -1788,7 +1925,6 @@ export class ToolkitCoordinator {
|
|
|
1788
1925
|
source: "ToolkitCoordinator.updateToolConfig",
|
|
1789
1926
|
toolRegistry: this.toolRegistry,
|
|
1790
1927
|
});
|
|
1791
|
-
this.config.tools = validated.config;
|
|
1792
1928
|
// M8 PR 2 — keep the policy engine's tools input in lockstep
|
|
1793
1929
|
// with the validated coordinator config. The engine emits an
|
|
1794
1930
|
// `inputs` change event so subscribers (e.g. PR 3 toolbars) can
|
|
@@ -1798,7 +1934,7 @@ export class ToolkitCoordinator {
|
|
|
1798
1934
|
});
|
|
1799
1935
|
void this.emitTelemetry("pie-toolkit-tool-config-updated", { toolId });
|
|
1800
1936
|
// Apply configuration changes to services
|
|
1801
|
-
this._applyToolConfigChange(toolId,
|
|
1937
|
+
this._applyToolConfigChange(toolId, current);
|
|
1802
1938
|
}
|
|
1803
1939
|
/**
|
|
1804
1940
|
* Update the enabled tool list for one placement level.
|
|
@@ -1816,11 +1952,11 @@ export class ToolkitCoordinator {
|
|
|
1816
1952
|
*/
|
|
1817
1953
|
updateToolsPlacement(partial) {
|
|
1818
1954
|
if (!this.config.tools) {
|
|
1819
|
-
this.config.tools =
|
|
1955
|
+
this.config.tools = this.validateToolsConfig(undefined, {
|
|
1820
1956
|
strictness: this.config.toolConfigStrictness ?? "error",
|
|
1821
1957
|
source: "ToolkitCoordinator.updateToolsPlacement",
|
|
1822
1958
|
toolRegistry: this.toolRegistry,
|
|
1823
|
-
})
|
|
1959
|
+
});
|
|
1824
1960
|
}
|
|
1825
1961
|
const currentPlacement = this.config.tools?.placement ?? {
|
|
1826
1962
|
section: [],
|
|
@@ -1832,7 +1968,7 @@ export class ToolkitCoordinator {
|
|
|
1832
1968
|
item: [...(partial.item ?? currentPlacement.item ?? [])],
|
|
1833
1969
|
passage: [...(partial.passage ?? currentPlacement.passage ?? [])],
|
|
1834
1970
|
};
|
|
1835
|
-
const validated =
|
|
1971
|
+
const validated = this.validateToolsConfig({
|
|
1836
1972
|
...this.config.tools,
|
|
1837
1973
|
placement: nextPlacement,
|
|
1838
1974
|
}, {
|
|
@@ -1840,8 +1976,8 @@ export class ToolkitCoordinator {
|
|
|
1840
1976
|
source: "ToolkitCoordinator.updateToolsPlacement",
|
|
1841
1977
|
toolRegistry: this.toolRegistry,
|
|
1842
1978
|
});
|
|
1843
|
-
validated.
|
|
1844
|
-
this.config.tools = validated
|
|
1979
|
+
validated.placement = nextPlacement;
|
|
1980
|
+
this.config.tools = validated;
|
|
1845
1981
|
this.policyEngine.updateInputs({
|
|
1846
1982
|
tools: this.config.tools,
|
|
1847
1983
|
});
|
|
@@ -1867,10 +2003,19 @@ export class ToolkitCoordinator {
|
|
|
1867
2003
|
* capability it asks about, which is indistinguishable from a student who was
|
|
1868
2004
|
* properly declined — so without this, forgetting {@link updateAssessment}
|
|
1869
2005
|
* presents as an accommodation that silently never appears.
|
|
2006
|
+
*
|
|
2007
|
+
* A coordinator built with `assessmentOptional` reports only while PNP
|
|
2008
|
+
* enforcement is explicitly `"on"`. Its toolkit binds whatever assessment the
|
|
2009
|
+
* host gave, usually none, and a host that passes no profile and leaves
|
|
2010
|
+
* enforcement unset or `"off"` has asked for no accommodation.
|
|
1870
2011
|
*/
|
|
1871
2012
|
decideFeaturePolicy(featureId) {
|
|
1872
2013
|
const decision = this.policyEngine.decideFeature(featureId);
|
|
1873
|
-
|
|
2014
|
+
const unboundIsMisconfigured = this.config.assessmentOptional !== true ||
|
|
2015
|
+
this.pnpEnforcementOverride === "on";
|
|
2016
|
+
if (!decision.assessmentBound &&
|
|
2017
|
+
unboundIsMisconfigured &&
|
|
2018
|
+
!this.reportedUnboundFeaturePolicy) {
|
|
1874
2019
|
this.reportedUnboundFeaturePolicy = true;
|
|
1875
2020
|
console.warn(`[ToolkitCoordinator] Feature policy was asked about "${featureId}" with no assessment bound, so every capability will be declined for want of a profile to read. Call updateAssessment(...) with the assessment (its personalNeedsProfile, settings.districtPolicy and settings.testAdministration are what policy reads) before relying on any accommodation. Reported once per coordinator.`);
|
|
1876
2021
|
}
|
|
@@ -2109,37 +2254,80 @@ export class ToolkitCoordinator {
|
|
|
2109
2254
|
}
|
|
2110
2255
|
/**
|
|
2111
2256
|
* Apply tool configuration changes to underlying services.
|
|
2112
|
-
* Called after updateToolConfig().
|
|
2257
|
+
* Called after updateToolConfig() with the tool's config before the update.
|
|
2113
2258
|
*/
|
|
2114
|
-
_applyToolConfigChange(toolId,
|
|
2259
|
+
_applyToolConfigChange(toolId, previousConfig) {
|
|
2115
2260
|
if (this.disposePromise !== null)
|
|
2116
2261
|
return;
|
|
2117
2262
|
// Apply configuration changes based on tool
|
|
2118
2263
|
switch (toolId) {
|
|
2119
|
-
case "textToSpeech":
|
|
2120
|
-
|
|
2121
|
-
this.ttsReconfigurePromise = reconfigurePromise;
|
|
2122
|
-
void reconfigurePromise.finally(() => {
|
|
2123
|
-
if (this.ttsReconfigurePromise === reconfigurePromise) {
|
|
2124
|
-
this.ttsReconfigurePromise = undefined;
|
|
2125
|
-
}
|
|
2126
|
-
});
|
|
2127
|
-
void reconfigurePromise.then(async () => {
|
|
2128
|
-
if (this.disposePromise !== null)
|
|
2129
|
-
return;
|
|
2130
|
-
const ttsConfig = this.getTTSConfigFromProviders();
|
|
2131
|
-
if (!this.lazyInit && ttsConfig?.enabled !== false) {
|
|
2132
|
-
await this.ensureTTSReady(ttsConfig);
|
|
2133
|
-
}
|
|
2134
|
-
});
|
|
2264
|
+
case "textToSpeech":
|
|
2265
|
+
this.scheduleTTSReconfigure();
|
|
2135
2266
|
break;
|
|
2136
|
-
}
|
|
2137
2267
|
case "answerEliminator":
|
|
2138
2268
|
// Future: Could notify answer eliminator tools of strategy change
|
|
2139
2269
|
break;
|
|
2140
|
-
|
|
2270
|
+
default:
|
|
2271
|
+
this._registerChangedToolProvider(toolId, previousConfig);
|
|
2141
2272
|
}
|
|
2142
2273
|
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Register the provider a tool's updated config names, and unregister the
|
|
2276
|
+
* one its previous config named when the provider id changed, as
|
|
2277
|
+
* {@link _reconfigureTTSProvider} does for `tts`. Registration completes
|
|
2278
|
+
* before this returns, so a check on the policy change the update dispatched
|
|
2279
|
+
* finds the new provider.
|
|
2280
|
+
*/
|
|
2281
|
+
_registerChangedToolProvider(toolId, previousConfig) {
|
|
2282
|
+
const registration = this.getProviderDescriptorTools().find((tool) => tool.toolId === toolId);
|
|
2283
|
+
if (!registration)
|
|
2284
|
+
return;
|
|
2285
|
+
void this.registerProviderFromTool(registration);
|
|
2286
|
+
const resolveQuietly = (config) => {
|
|
2287
|
+
try {
|
|
2288
|
+
return resolveToolProviderId(registration, config);
|
|
2289
|
+
}
|
|
2290
|
+
catch {
|
|
2291
|
+
return null;
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
const previousId = resolveQuietly(previousConfig);
|
|
2295
|
+
const nextId = resolveQuietly(this.getToolConfig(toolId) || undefined);
|
|
2296
|
+
if (!previousId ||
|
|
2297
|
+
previousId === nextId ||
|
|
2298
|
+
!this.toolProviderRegistry.has(previousId)) {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
void this.toolProviderRegistry.unregister(previousId).catch((err) => {
|
|
2302
|
+
console.warn(`[ToolkitCoordinator] Failed to unregister provider "${previousId}":`, err);
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Re-register the `tts` provider for the current config and registry, then
|
|
2307
|
+
* re-initialize unless initialization is lazy. An initialization already
|
|
2308
|
+
* running finishes first: it would otherwise mark its provider ready after
|
|
2309
|
+
* the reset.
|
|
2310
|
+
*/
|
|
2311
|
+
scheduleTTSReconfigure() {
|
|
2312
|
+
const inFlight = this.ttsInitPromise;
|
|
2313
|
+
const reconfigurePromise = inFlight
|
|
2314
|
+
? inFlight.catch(() => { }).then(() => this._reconfigureTTSProvider())
|
|
2315
|
+
: this._reconfigureTTSProvider();
|
|
2316
|
+
this.ttsReconfigurePromise = reconfigurePromise;
|
|
2317
|
+
void reconfigurePromise.finally(() => {
|
|
2318
|
+
if (this.ttsReconfigurePromise === reconfigurePromise) {
|
|
2319
|
+
this.ttsReconfigurePromise = undefined;
|
|
2320
|
+
}
|
|
2321
|
+
});
|
|
2322
|
+
void reconfigurePromise.then(async () => {
|
|
2323
|
+
if (this.disposePromise !== null)
|
|
2324
|
+
return;
|
|
2325
|
+
const ttsConfig = this.getTTSConfigFromProviders();
|
|
2326
|
+
if (!this.lazyInit && ttsConfig?.enabled !== false) {
|
|
2327
|
+
await this.ensureTTSReady(ttsConfig);
|
|
2328
|
+
}
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2143
2331
|
async _reconfigureTTSProvider() {
|
|
2144
2332
|
this.ttsInitialized = false;
|
|
2145
2333
|
this.ttsInitPromise = undefined;
|
|
@@ -68,4 +68,15 @@ export declare function catalogOwnerContextFor(owner: CatalogOwnerIdentity): Cat
|
|
|
68
68
|
* register the owner once through `AccessibilityCatalogResolver.registerOwner`.
|
|
69
69
|
*/
|
|
70
70
|
export declare function collectOwnerCatalogRegistrations(entity: CatalogSourceEntity | null | undefined, owner: CatalogOwnerIdentity): CatalogOwnerRegistrationEntry[];
|
|
71
|
+
/**
|
|
72
|
+
* The catalogs an owner registration of `entity` would file, as a string equal
|
|
73
|
+
* for any two entities that register the same catalogs. A shell holding new
|
|
74
|
+
* content under an unchanged identity compares it to decide whether the runtime
|
|
75
|
+
* has anything new to register. It is derived from the registration walk, so it
|
|
76
|
+
* cannot disagree with the registration about what counts as a catalog.
|
|
77
|
+
*
|
|
78
|
+
* `null` when the catalogs do not serialize: nothing can be concluded, and the
|
|
79
|
+
* caller registers again.
|
|
80
|
+
*/
|
|
81
|
+
export declare function catalogSourceSignature(entity: CatalogSourceEntity | null | undefined, kind: CatalogOwnerIdentity["kind"]): string | null;
|
|
71
82
|
export {};
|
|
@@ -62,3 +62,28 @@ export function collectOwnerCatalogRegistrations(entity, owner) {
|
|
|
62
62
|
}
|
|
63
63
|
return registrations;
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The catalogs an owner registration of `entity` would file, as a string equal
|
|
67
|
+
* for any two entities that register the same catalogs. A shell holding new
|
|
68
|
+
* content under an unchanged identity compares it to decide whether the runtime
|
|
69
|
+
* has anything new to register. It is derived from the registration walk, so it
|
|
70
|
+
* cannot disagree with the registration about what counts as a catalog.
|
|
71
|
+
*
|
|
72
|
+
* `null` when the catalogs do not serialize: nothing can be concluded, and the
|
|
73
|
+
* caller registers again.
|
|
74
|
+
*/
|
|
75
|
+
export function catalogSourceSignature(entity, kind) {
|
|
76
|
+
const registrations = collectOwnerCatalogRegistrations(entity, {
|
|
77
|
+
kind,
|
|
78
|
+
itemId: "",
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
return JSON.stringify(registrations.map((registration) => [
|
|
82
|
+
registration.context.modelId ?? null,
|
|
83
|
+
registration.catalogs,
|
|
84
|
+
]));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ToolsConfigInput } from "./tools-config-normalizer.js";
|
|
2
2
|
import type { ToolRegistry } from "./ToolRegistry.js";
|
|
3
3
|
import { type ToolConfigStrictness, type ToolConfigValidationResult } from "./tool-config-validation.js";
|
|
4
4
|
export interface CreateToolsConfigArgs {
|
|
5
|
-
tools?:
|
|
5
|
+
tools?: ToolsConfigInput | null;
|
|
6
6
|
toolRegistry?: ToolRegistry | null;
|
|
7
7
|
strictness?: ToolConfigStrictness;
|
|
8
8
|
source?: string;
|
|
@@ -13,7 +13,14 @@ export type FrameworkErrorKind = "tool-config" | "runtime-init" | "runtime-dispo
|
|
|
13
13
|
* key still resolves through the English fallback chain, so the player renders
|
|
14
14
|
* — in the wrong language, which a host wants to know about.
|
|
15
15
|
*/
|
|
16
|
-
| "i18n-locale-load"
|
|
16
|
+
| "i18n-locale-load"
|
|
17
|
+
/**
|
|
18
|
+
* A section player could not register the elements its items need before
|
|
19
|
+
* mounting them: a renderable failed the config contract, an element bundle
|
|
20
|
+
* or module failed to load, or a `preloaded` page lacks a registration the
|
|
21
|
+
* content names. Not recoverable — the section's items stay unmounted.
|
|
22
|
+
*/
|
|
23
|
+
| "element-preload" | "unknown";
|
|
17
24
|
export type FrameworkErrorSeverity = "warning" | "error";
|
|
18
25
|
export interface FrameworkErrorModel {
|
|
19
26
|
kind: FrameworkErrorKind;
|