@pie-players/pie-assessment-toolkit 0.3.44 → 0.3.46
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 +59 -70
- package/dist/components/ItemToolBar.custom-element.js +776 -357
- package/dist/components/PieAssessmentToolkit.custom-element.js +19501 -3591
- package/dist/components/SectionToolBar.custom-element.js +1058 -540
- package/dist/index.d.ts +3 -6
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +1 -2
- package/dist/policy/core/ToolPolicyEngine.js +1 -2
- package/dist/policy/core/ToolPolicyEngine.js.map +1 -1
- package/dist/policy/core/pnp-policy-inputs.d.ts +2 -3
- package/dist/policy/core/pnp-policy-inputs.js +2 -3
- package/dist/policy/core/pnp-policy-inputs.js.map +1 -1
- package/dist/policy/core/provenance.d.ts +7 -34
- package/dist/policy/core/provenance.js +4 -21
- package/dist/policy/core/provenance.js.map +1 -1
- package/dist/policy/sources/PnpPolicySource.d.ts +6 -19
- package/dist/policy/sources/PnpPolicySource.js +6 -19
- package/dist/policy/sources/PnpPolicySource.js.map +1 -1
- package/dist/runtime/SectionRuntimeEngine.d.ts +13 -28
- package/dist/runtime/SectionRuntimeEngine.js +13 -27
- package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +2 -2
- package/dist/runtime/adapter/SectionEngineAdapter.js +2 -2
- package/dist/runtime/adapter/SectionEngineAdapter.js.map +1 -1
- package/dist/runtime/adapter/coordinator-bridge.d.ts +1 -1
- package/dist/runtime/adapter/coordinator-bridge.js +1 -1
- package/dist/runtime/adapter/coordinator-bridge.js.map +1 -1
- package/dist/runtime/adapter/dom-event-bridge.d.ts +1 -1
- package/dist/runtime/adapter/dom-event-bridge.js +1 -1
- package/dist/runtime/adapter/dom-event-bridge.js.map +1 -1
- package/dist/runtime/catalog-registration.d.ts +12 -0
- package/dist/runtime/catalog-registration.js +54 -0
- package/dist/runtime/catalog-registration.js.map +1 -0
- package/dist/runtime/core/engine-output.d.ts +2 -2
- package/dist/runtime/core/engine-output.js +2 -2
- package/dist/runtime/core/engine-output.js.map +1 -1
- package/dist/runtime/core/engine-readiness.d.ts +2 -2
- package/dist/runtime/core/engine-readiness.js.map +1 -1
- package/dist/runtime/core/engine-resolver.d.ts +13 -41
- package/dist/runtime/core/engine-resolver.js +19 -76
- package/dist/runtime/core/engine-resolver.js.map +1 -1
- package/dist/runtime/core/engine-transition.d.ts +1 -1
- package/dist/runtime/core/engine-transition.js +1 -1
- package/dist/runtime/core/engine-transition.js.map +1 -1
- package/dist/runtime/engine.d.ts +1 -1
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/section-runtime-engine-host-context.d.ts +22 -15
- package/dist/runtime/section-runtime-engine-host-context.js +10 -10
- package/dist/runtime/section-runtime-engine-host-context.js.map +1 -1
- package/dist/runtime/stage-emit-gate.d.ts +4 -4
- package/dist/runtime/stage-emit-gate.js +3 -3
- package/dist/runtime/stage-emit-gate.js.map +1 -1
- package/dist/services/AccessibilityCatalogResolver.d.ts +23 -0
- package/dist/services/AccessibilityCatalogResolver.js +200 -28
- package/dist/services/AccessibilityCatalogResolver.js.map +1 -1
- package/dist/services/HighlightCoordinator.d.ts +25 -0
- package/dist/services/HighlightCoordinator.js +125 -0
- package/dist/services/HighlightCoordinator.js.map +1 -1
- package/dist/services/SSMLExtractor.d.ts +16 -1
- package/dist/services/SSMLExtractor.js +86 -7
- package/dist/services/SSMLExtractor.js.map +1 -1
- package/dist/services/TTSService.d.ts +30 -4
- package/dist/services/TTSService.js +652 -159
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ToolRegistry.d.ts +11 -2
- package/dist/services/ToolRegistry.js +1 -1
- package/dist/services/ToolRegistry.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +16 -73
- package/dist/services/ToolkitCoordinator.js +34 -113
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/interfaces.d.ts +20 -2
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/tool-config-validation.d.ts +1 -1
- package/dist/services/tool-config-validation.js +7 -7
- package/dist/services/tool-config-validation.js.map +1 -1
- package/dist/services/tool-instance-id.js +1 -1
- package/dist/services/tool-instance-id.js.map +1 -1
- package/dist/services/tool-providers/TTSToolProvider.js +3 -3
- package/dist/services/tool-providers/TTSToolProvider.js.map +1 -1
- package/dist/services/tools-config-normalizer.d.ts +13 -2
- package/dist/services/tools-config-normalizer.js +3 -8
- package/dist/services/tools-config-normalizer.js.map +1 -1
- package/dist/services/tts/browser-provider.js +2 -0
- package/dist/services/tts/browser-provider.js.map +1 -1
- package/dist/services/tts/catalog-span-alignment.d.ts +39 -0
- package/dist/services/tts/catalog-span-alignment.js +300 -0
- package/dist/services/tts/catalog-span-alignment.js.map +1 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +33 -0
- package/dist/services/tts/generated-speech/assemble-plan.js +111 -0
- package/dist/services/tts/generated-speech/assemble-plan.js.map +1 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +25 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +66 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +1 -0
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +19 -0
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +71 -0
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +1 -0
- package/dist/services/tts/generated-speech/dom/types.d.ts +37 -0
- package/dist/services/tts/generated-speech/dom/types.js +2 -0
- package/dist/services/tts/generated-speech/dom/types.js.map +1 -0
- package/dist/services/tts/generated-speech/index.d.ts +17 -0
- package/dist/services/tts/generated-speech/index.js +17 -0
- package/dist/services/tts/generated-speech/index.js.map +1 -0
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +15 -0
- package/dist/services/tts/generated-speech/math-speech-cache.js +78 -0
- package/dist/services/tts/generated-speech/math-speech-cache.js.map +1 -0
- package/dist/services/tts/generated-speech/types.d.ts +104 -0
- package/dist/services/tts/generated-speech/types.js +18 -0
- package/dist/services/tts/generated-speech/types.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +106 -0
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.d.ts +6 -0
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +217 -0
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/highlight-policy.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/highlight-policy.js +32 -0
- package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/index.d.ts +9 -0
- package/dist/services/tts/highlight-pipeline/index.js +9 -0
- package/dist/services/tts/highlight-pipeline/index.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.d.ts +3 -0
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +32 -0
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.d.ts +29 -0
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +44 -0
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +20 -0
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/readable-region.d.ts +1 -0
- package/dist/services/tts/highlight-pipeline/readable-region.js +19 -0
- package/dist/services/tts/highlight-pipeline/readable-region.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.d.ts +6 -0
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +198 -0
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/types.d.ts +72 -0
- package/dist/services/tts/highlight-pipeline/types.js +2 -0
- package/dist/services/tts/highlight-pipeline/types.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +22 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +1 -0
- package/dist/services/tts/math-alignment/index.d.ts +30 -0
- package/dist/services/tts/math-alignment/index.js +66 -0
- package/dist/services/tts/math-alignment/index.js.map +1 -0
- package/dist/services/tts/math-alignment/mathml-tokenizer.d.ts +2 -0
- package/dist/services/tts/math-alignment/mathml-tokenizer.js +317 -0
- package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +1 -0
- package/dist/services/tts/math-alignment/range-resolver.d.ts +10 -0
- package/dist/services/tts/math-alignment/range-resolver.js +13 -0
- package/dist/services/tts/math-alignment/range-resolver.js.map +1 -0
- package/dist/services/tts/math-alignment/sequence-aligner.d.ts +7 -0
- package/dist/services/tts/math-alignment/sequence-aligner.js +119 -0
- package/dist/services/tts/math-alignment/sequence-aligner.js.map +1 -0
- package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +31 -0
- package/dist/services/tts/math-alignment/speech-tokenizer.js +143 -0
- package/dist/services/tts/math-alignment/speech-tokenizer.js.map +1 -0
- package/dist/services/tts/math-alignment/types.d.ts +58 -0
- package/dist/services/tts/math-alignment/types.js +2 -0
- package/dist/services/tts/math-alignment/types.js.map +1 -0
- package/dist/services/tts/math-aware-text-processing.d.ts +18 -0
- package/dist/services/tts/math-aware-text-processing.js +348 -0
- package/dist/services/tts/math-aware-text-processing.js.map +1 -0
- package/dist/services/tts/math-speech.d.ts +44 -0
- package/dist/services/tts/math-speech.js +147 -0
- package/dist/services/tts/math-speech.js.map +1 -0
- package/dist/services/tts/mathml-sanitization.d.ts +1 -0
- package/dist/services/tts/mathml-sanitization.js +170 -0
- package/dist/services/tts/mathml-sanitization.js.map +1 -0
- package/dist/services/tts/ssml/spoken-text.d.ts +33 -0
- package/dist/services/tts/ssml/spoken-text.js +159 -0
- package/dist/services/tts/ssml/spoken-text.js.map +1 -0
- package/dist/services/tts/text-processing.d.ts +3 -0
- package/dist/services/tts/text-processing.js +30 -4
- package/dist/services/tts/text-processing.js.map +1 -1
- package/dist/services/tts-runtime-config.d.ts +17 -1
- package/dist/services/tts-runtime-config.js +11 -1
- package/dist/services/tts-runtime-config.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.js +0 -1
- package/dist/tools/registrations/accessibility-tools.js.map +1 -1
- package/dist/tools/registrations/calculator.js +46 -5
- package/dist/tools/registrations/calculator.js.map +1 -1
- package/dist/tools/tool-tag-map.js +0 -1
- package/dist/tools/tool-tag-map.js.map +1 -1
- package/package.json +8 -11
- package/dist/services/PNPToolResolver.d.ts +0 -280
- package/dist/services/PNPToolResolver.js +0 -514
- package/dist/services/PNPToolResolver.js.map +0 -1
- package/dist/services/pnp-provenance.d.ts +0 -166
- package/dist/services/pnp-provenance.js +0 -231
- package/dist/services/pnp-provenance.js.map +0 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Part of PIE Assessment Toolkit.
|
|
9
9
|
*/
|
|
10
10
|
import type { I18nServiceApi } from "@pie-players/pie-players-shared/i18n";
|
|
11
|
-
import type { AccessibilityCatalogResolver, CatalogLookupOptions, CatalogStatistics, CatalogType, ResolvedCatalog } from "./AccessibilityCatalogResolver.js";
|
|
11
|
+
import type { AccessibilityCatalogResolver, CatalogLookupContext, CatalogLookupOptions, CatalogOwnerContext, CatalogStatistics, CatalogType, ResolvedCatalog } from "./AccessibilityCatalogResolver.js";
|
|
12
12
|
import type { FrameworkErrorListener } from "./framework-error-bus.js";
|
|
13
13
|
import type { HighlightColor, HighlightType } from "./HighlightCoordinator.js";
|
|
14
14
|
import type { SectionControllerHandle, SectionItemEventSubscriptionArgs, SectionScopedEventSubscriptionArgs, SectionEventSubscriptionArgs, ToolkitCoordinatorHooks, ToolkitInitStatus } from "./ToolkitCoordinator.js";
|
|
@@ -59,6 +59,19 @@ export interface HighlightCoordinatorApi {
|
|
|
59
59
|
* Highlight a word for TTS (temporary)
|
|
60
60
|
*/
|
|
61
61
|
highlightTTSWord(textNode: Text, startOffset: number, endOffset: number): void;
|
|
62
|
+
/**
|
|
63
|
+
* Highlight a single element as the active TTS word (temporary).
|
|
64
|
+
*
|
|
65
|
+
* Marks exactly the supplied element (via `data-pie-tts-word-element`)
|
|
66
|
+
* without walking up to a containing ancestor. Used for atomic targets that
|
|
67
|
+
* have no direct text node to range over — most notably MathJax CHTML tokens
|
|
68
|
+
* (e.g. `<mjx-mi><mjx-c/></mjx-mi>`) and whole-expression fallbacks. Painting
|
|
69
|
+
* the element itself is what lets a resolved math token highlight as a token
|
|
70
|
+
* rather than escalating to the full `<math>` / `<mjx-container>`.
|
|
71
|
+
*
|
|
72
|
+
* Optional so lightweight coordinator mocks can omit it.
|
|
73
|
+
*/
|
|
74
|
+
highlightTTSWordElement?(element: Element): void;
|
|
62
75
|
/**
|
|
63
76
|
* Highlight sentence(s) for TTS (background layer)
|
|
64
77
|
*/
|
|
@@ -161,6 +174,7 @@ export interface TtsServiceApi {
|
|
|
161
174
|
*/
|
|
162
175
|
speak(text: string, options?: {
|
|
163
176
|
catalogId?: string;
|
|
177
|
+
catalogContext?: CatalogLookupContext;
|
|
164
178
|
language?: string;
|
|
165
179
|
contentElement?: Element;
|
|
166
180
|
}): Promise<void>;
|
|
@@ -257,6 +271,10 @@ export interface AccessibilityCatalogResolverApi {
|
|
|
257
271
|
* Add item-level catalogs (called when rendering a new item)
|
|
258
272
|
*/
|
|
259
273
|
addItemCatalogs(catalogs: any[]): void;
|
|
274
|
+
/**
|
|
275
|
+
* Register catalogs scoped to a mounted content owner.
|
|
276
|
+
*/
|
|
277
|
+
registerCatalogs?(context: CatalogOwnerContext, catalogs: any[]): () => void;
|
|
260
278
|
/**
|
|
261
279
|
* Clear item-level catalogs (called when leaving an item)
|
|
262
280
|
*/
|
|
@@ -557,7 +575,7 @@ export interface ToolkitCoordinatorApi {
|
|
|
557
575
|
/**
|
|
558
576
|
* Subscribe to policy-engine change events. Fires whenever the
|
|
559
577
|
* coordinator's bound policy inputs change (`updateToolConfig`,
|
|
560
|
-
* `
|
|
578
|
+
* `updateToolPlacement`, `updateAssessment`, `updateCurrentItemRef`,
|
|
561
579
|
* `setPnpEnforcement`) or a custom `PolicySource` is registered /
|
|
562
580
|
* removed. Listeners that need the new visible tool set should
|
|
563
581
|
* call `decideToolPolicy(...)` with their level / scope.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/services/interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;;AAuzBH,0EAA0E","sourcesContent":["/**\n * Service Interfaces\n *\n * Public interfaces for all core assessment toolkit services.\n * These interfaces define the contracts that service implementations must fulfill,\n * enabling dependency injection, testing, and custom implementations.\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { I18nServiceApi } from \"@pie-players/pie-players-shared/i18n\";\nimport type {\n\tAccessibilityCatalogResolver,\n\tCatalogLookupOptions,\n\tCatalogStatistics,\n\tCatalogType,\n\tResolvedCatalog,\n} from \"./AccessibilityCatalogResolver.js\";\nimport type { FrameworkErrorListener } from \"./framework-error-bus.js\";\nimport type { HighlightColor, HighlightType } from \"./HighlightCoordinator.js\";\nimport type {\n\tSectionControllerHandle,\n\tSectionItemEventSubscriptionArgs,\n\tSectionScopedEventSubscriptionArgs,\n\tSectionEventSubscriptionArgs,\n\tToolkitCoordinatorHooks,\n\tToolkitInitStatus,\n} from \"./ToolkitCoordinator.js\";\nimport type { FontSize, ThemeConfig } from \"./ThemeProvider.js\";\nimport type { ZIndexLayer } from \"./ToolCoordinator.js\";\nimport type { PlaybackState, TTSConfig } from \"./TTSService.js\";\nimport type {\n\tToolPlacementConfig,\n\tToolPlacementLevel,\n\tToolProviderConfig,\n} from \"./tools-config-normalizer.js\";\nimport type { ToolProviderRegistry } from \"./tool-providers/ToolProviderRegistry.js\";\nimport type {\n\tPolicySource,\n\tPnpEnforcementMode,\n\tResolvedEngineInputs,\n\tToolPolicyChangeListener,\n\tToolPolicyDecision,\n\tToolPolicyDecisionRequest,\n} from \"../policy/engine.js\";\nimport type {\n\tAssessmentEntity,\n\tAssessmentItemRef,\n} from \"@pie-players/pie-players-shared/types\";\nimport type {\n\tITTSProvider,\n\tTTSProviderCapabilities,\n} from \"@pie-players/pie-tts\";\nimport type {\n\tResolvedToolContext,\n\tToolContextResolver,\n\tToolContextResolverContext,\n\tToolContextResolverMap,\n} from \"./ToolRegistry.js\";\n\n// Re-export I18nServiceApi from players-shared\nexport type { I18nServiceApi };\n\n/**\n * Theme provider interface\n *\n * Applies consistent accessibility theming across items and tools.\n */\nexport interface ThemeProviderApi {\n\t/**\n\t * Apply theme configuration\n\t */\n\tapplyTheme(config: ThemeConfig): void;\n\n\t/**\n\t * Get current theme configuration\n\t */\n\tgetCurrentTheme(): Required<ThemeConfig>;\n\n\t/**\n\t * Reset to default theme\n\t */\n\treset(): void;\n\n\t/**\n\t * Clean up and remove theme styles\n\t */\n\tdestroy(): void;\n}\n\n/**\n * Highlight coordinator interface\n *\n * Manages content highlighting for TTS, annotations, and selections.\n */\nexport interface HighlightCoordinatorApi {\n\t/**\n\t * Highlight a text range\n\t */\n\thighlightRange(\n\t\trange: Range,\n\t\ttype: HighlightType,\n\t\tcolor: HighlightColor,\n\t): void;\n\n\t/**\n\t * Highlight a word for TTS (temporary)\n\t */\n\thighlightTTSWord(\n\t\ttextNode: Text,\n\t\tstartOffset: number,\n\t\tendOffset: number,\n\t): void;\n\n\t/**\n\t * Highlight sentence(s) for TTS (background layer)\n\t */\n\thighlightTTSSentence(ranges: Range[]): void;\n\n\t/**\n\t * Clear all TTS highlights (word and sentence)\n\t */\n\tclearTTS(): void;\n\n\t/**\n\t * Clear highlights of a specific type\n\t */\n\tclearHighlights(type: HighlightType): void;\n\n\t/**\n\t * Clear all highlights\n\t */\n\tclearAll(): void;\n\n\t/**\n\t * Check if highlighting is supported in current environment\n\t */\n\tisSupported(): boolean;\n\n\t/**\n\t * Update TTS highlight style dynamically\n\t */\n\tupdateTTSHighlightStyle(color: string, opacity: number): void;\n}\n\n/**\n * Tool state interface\n */\nexport interface ToolState {\n\tid: string;\n\tname: string;\n\tisVisible: boolean;\n\telement: HTMLElement | null;\n\tlayer: ZIndexLayer;\n}\n\n/**\n * Tool coordinator interface\n *\n * Manages z-index layering and visibility for floating tools.\n */\nexport interface ToolCoordinatorApi {\n\t/**\n\t * Register a tool with the coordinator\n\t */\n\tregisterTool(\n\t\tid: string,\n\t\tname: string,\n\t\telement?: HTMLElement,\n\t\tlayer?: ZIndexLayer,\n\t): void;\n\n\t/**\n\t * Unregister a tool\n\t */\n\tunregisterTool(id: string): void;\n\n\t/**\n\t * Show a tool\n\t */\n\tshowTool(id: string): void;\n\n\t/**\n\t * Hide a tool\n\t */\n\thideTool(id: string): void;\n\n\t/**\n\t * Toggle tool visibility\n\t */\n\ttoggleTool(id: string): void;\n\n\t/**\n\t * Check if tool is visible\n\t */\n\tisToolVisible(id: string): boolean;\n\n\t/**\n\t * Bring an element to the front of its layer\n\t */\n\tbringToFront(element: HTMLElement): void;\n\n\t/**\n\t * Update the element reference for a tool\n\t */\n\tupdateToolElement(id: string, element: HTMLElement): void;\n\n\t/**\n\t * Get state for a specific tool\n\t */\n\tgetToolState(id: string): ToolState | undefined;\n\n\t/**\n\t * Get all visible tools\n\t */\n\tgetVisibleTools(): ToolState[];\n\n\t/**\n\t * Subscribe to tool state changes\n\t */\n\tsubscribe(listener: () => void): () => void;\n}\n\n/**\n * TTS service interface\n *\n * Provides text-to-speech functionality with provider-based architecture.\n * Supports QTI 3.0 accessibility catalogs for pre-authored spoken content.\n */\nexport interface TtsServiceApi {\n\t/**\n\t * Initialize TTS with a provider\n\t */\n\tinitialize(\n\t\tprovider: ITTSProvider,\n\t\tconfig?: Partial<TTSConfig>,\n\t): Promise<void>;\n\n\t/**\n\t * Speak text with optional catalog support\n\t */\n\tspeak(\n\t\ttext: string,\n\t\toptions?: {\n\t\t\tcatalogId?: string;\n\t\t\tlanguage?: string;\n\t\t\tcontentElement?: Element;\n\t\t},\n\t): Promise<void>;\n\n\t/**\n\t * Speak a text range\n\t */\n\tspeakRange(\n\t\trange: Range,\n\t\toptions?: { contentRoot?: Element | null },\n\t): Promise<void>;\n\n\t/**\n\t * Pause playback\n\t */\n\tpause(): void;\n\n\t/**\n\t * Resume playback\n\t */\n\tresume(): void;\n\n\t/**\n\t * Stop playback\n\t */\n\tstop(): void;\n\n\t/**\n\t * Request active TTS controls to hand off/deactivate their UI state.\n\t *\n\t * This is an orchestration hint for TTS tool chrome and does not replace\n\t * playback controls such as stop/pause/resume.\n\t */\n\trequestControlHandoff(): void;\n\n\t/**\n\t * Seek forward by sentence units\n\t */\n\tseekForward(units?: number): Promise<void>;\n\n\t/**\n\t * Seek backward by sentence units\n\t */\n\tseekBackward(units?: number): Promise<void>;\n\n\t/**\n\t * Check if currently playing\n\t */\n\tisPlaying(): boolean;\n\n\t/**\n\t * Check if paused\n\t */\n\tisPaused(): boolean;\n\n\t/**\n\t * Get current playback state\n\t */\n\tgetState(): PlaybackState;\n\n\t/**\n\t * Get currently speaking text\n\t */\n\tgetCurrentText(): string | null;\n\n\t/**\n\t * Subscribe to state changes\n\t */\n\tonStateChange(id: string, callback: (state: PlaybackState) => void): void;\n\n\t/**\n\t * Unsubscribe from state changes\n\t */\n\toffStateChange(id: string, callback: (state: PlaybackState) => void): void;\n\n\t/**\n\t * Get capabilities of current provider\n\t */\n\tgetCapabilities(): TTSProviderCapabilities | null;\n\n\t/**\n\t * Update TTS settings dynamically (rate, pitch, voice)\n\t */\n\tupdateSettings(settings: Partial<TTSConfig>): Promise<void>;\n\n\t/**\n\t * Set highlight coordinator for word highlighting\n\t */\n\tsetHighlightCoordinator(coordinator: HighlightCoordinatorApi): void;\n\n\t/**\n\t * Set accessibility catalog resolver for spoken content\n\t */\n\tsetCatalogResolver(resolver: AccessibilityCatalogResolver): void;\n}\n\n/**\n * Accessibility catalog resolver interface\n *\n * Manages QTI 3.0 accessibility catalogs at assessment and item levels.\n * Provides lookup and resolution services for alternative content representations.\n */\nexport interface AccessibilityCatalogResolverApi {\n\t/**\n\t * Set the default language for fallback resolution\n\t */\n\tsetDefaultLanguage(language: string): void;\n\n\t/**\n\t * Get the default language\n\t */\n\tgetDefaultLanguage(): string;\n\n\t/**\n\t * Add item-level catalogs (called when rendering a new item)\n\t */\n\taddItemCatalogs(catalogs: any[]): void;\n\n\t/**\n\t * Clear item-level catalogs (called when leaving an item)\n\t */\n\tclearItemCatalogs(): void;\n\n\t/**\n\t * Check if a catalog exists\n\t */\n\thasCatalog(catalogId: string): boolean;\n\n\t/**\n\t * Get alternative content for a catalog identifier\n\t */\n\tgetAlternative(\n\t\tcatalogId: string,\n\t\toptions: CatalogLookupOptions,\n\t): ResolvedCatalog | null;\n\n\t/**\n\t * Get all available alternatives for a catalog identifier\n\t */\n\tgetAllAlternatives(catalogId: string): ResolvedCatalog[];\n\n\t/**\n\t * Get all catalog identifiers available\n\t */\n\tgetAllCatalogIds(): string[];\n\n\t/**\n\t * Get statistics about available catalogs\n\t */\n\tgetStatistics(): CatalogStatistics;\n\n\t/**\n\t * Check if a specific catalog type is available\n\t */\n\thasAlternativeType(catalogId: string, type: CatalogType): boolean;\n\n\t/**\n\t * Get all catalog IDs that have a specific type of alternative\n\t */\n\tgetCatalogsByType(type: CatalogType): string[];\n\n\t/**\n\t * Reset all catalogs\n\t */\n\treset(): void;\n\n\t/**\n\t * Destroy and cleanup\n\t */\n\tdestroy(): void;\n}\n\n/**\n * Element tool state store interface\n *\n * Manages element-level ephemeral tool state using composite keys for global uniqueness.\n * Tool state is client-only and separate from PIE session data (which is sent to server for scoring).\n */\nexport interface ElementToolStateStoreApi {\n\t/**\n\t * Generate a globally unique element ID from components\n\t */\n\tgetGlobalElementId(\n\t\tassessmentId: string,\n\t\tsectionId: string,\n\t\titemId: string,\n\t\telementId: string,\n\t): string;\n\n\t/**\n\t * Parse a global element ID into its components\n\t */\n\tparseGlobalElementId(globalElementId: string): {\n\t\tassessmentId: string;\n\t\tsectionId: string;\n\t\titemId: string;\n\t\telementId: string;\n\t} | null;\n\n\t/**\n\t * Set state for a specific tool on an element\n\t */\n\tsetState(globalElementId: string, toolId: string, state: any): void;\n\n\t/**\n\t * Get state for a specific tool on an element\n\t */\n\tgetState(globalElementId: string, toolId: string): any | undefined;\n\n\t/**\n\t * Get all tool states for a specific element\n\t */\n\tgetElementState(globalElementId: string): Record<string, any>;\n\n\t/**\n\t * Get all element states\n\t */\n\tgetAllState(): Record<string, Record<string, any>>;\n\n\t/**\n\t * Subscribe to state changes\n\t */\n\tsubscribe(\n\t\tcallback: (state: Map<string, Map<string, any>>) => void,\n\t): () => void;\n\n\t/**\n\t * Set callback for persistence integration\n\t */\n\tsetOnStateChange(\n\t\tcallback: (state: Record<string, Record<string, any>>) => void,\n\t): void;\n\n\t/**\n\t * Load state from persistence\n\t */\n\tloadState(state: Record<string, Record<string, any>>): void;\n\n\t/**\n\t * Clear state for a specific element\n\t */\n\tclearElement(globalElementId: string): void;\n\n\t/**\n\t * Clear state for a specific tool across all elements\n\t */\n\tclearTool(toolId: string): void;\n\n\t/**\n\t * Clear all elements in a specific section\n\t */\n\tclearSection(assessmentId: string, sectionId: string): void;\n\n\t/**\n\t * Clear all state\n\t */\n\tclearAll(): void;\n}\n\n/**\n * Toolkit coordinator interface\n *\n * Orchestrates all toolkit services (TTS, tools, accessibility, state management) from a single entry point.\n * Provides centralized configuration for tool availability and settings.\n */\nexport interface ToolkitCoordinatorApi {\n\t/**\n\t * Assessment identifier\n\t */\n\treadonly assessmentId: string;\n\n\t/**\n\t * Configuration\n\t */\n\treadonly config: {\n\t\ttools?: {\n\t\t\tproviders?: Record<string, ToolProviderConfig | undefined>;\n\t\t};\n\t};\n\n\t/**\n\t * TTS service\n\t */\n\treadonly ttsService: TtsServiceApi;\n\n\t/**\n\t * Tool coordinator\n\t */\n\treadonly toolCoordinator: ToolCoordinatorApi;\n\n\t/**\n\t * Highlight coordinator\n\t */\n\treadonly highlightCoordinator: HighlightCoordinatorApi;\n\n\t/**\n\t * Element tool state store\n\t */\n\treadonly elementToolStateStore: ElementToolStateStoreApi;\n\n\t/**\n\t * Catalog resolver\n\t */\n\treadonly catalogResolver: AccessibilityCatalogResolverApi;\n\n\t/**\n\t * Tool provider registry\n\t */\n\treadonly toolProviderRegistry: ToolProviderRegistry;\n\n\t/**\n\t * Get all services as a bundle\n\t */\n\tgetServiceBundle(): {\n\t\tttsService: TtsServiceApi;\n\t\ttoolCoordinator: ToolCoordinatorApi;\n\t\thighlightCoordinator: HighlightCoordinatorApi;\n\t\telementToolStateStore: ElementToolStateStoreApi;\n\t\tcatalogResolver: AccessibilityCatalogResolverApi;\n\t\ttoolProviderRegistry: ToolProviderRegistry;\n\t};\n\n\t/**\n\t * Ensure TTS service is initialized and ready.\n\t */\n\tensureTTSReady(config?: Record<string, unknown>): Promise<void>;\n\n\t/**\n\t * Ensure a provider is initialized and ready.\n\t */\n\tensureProviderReady(providerId: string): Promise<unknown>;\n\n\t/**\n\t * Wait until coordinator initialization is complete.\n\t */\n\twaitUntilReady(): Promise<void>;\n\n\t/**\n\t * Check if coordinator has completed initialization.\n\t */\n\tisReady(): boolean;\n\n\t/**\n\t * Read current initialization status.\n\t */\n\tgetInitStatus(): ToolkitInitStatus;\n\n\t/**\n\t * Check if a tool is enabled\n\t */\n\tisToolEnabled(toolId: string): boolean;\n\n\t/**\n\t * Get tool configuration\n\t */\n\tgetToolConfig(toolId: string): ToolProviderConfig | null;\n\n\t/**\n\t * Update tool configuration\n\t */\n\tupdateToolConfig(toolId: string, updates: Partial<ToolProviderConfig>): void;\n\n\t/**\n\t * Update the enabled tool list for one placement level.\n\t */\n\tupdateToolPlacement(level: ToolPlacementLevel, toolIds: string[]): void;\n\n\t/**\n\t * Patch one or more placement levels in the canonical tools config.\n\t */\n\tupdateToolsPlacement(partial: ToolPlacementConfig): void;\n\n\t/**\n\t * Register or update lifecycle hooks at runtime.\n\t */\n\tsetHooks(hooks: ToolkitCoordinatorHooks): void;\n\n\t/**\n\t * Return a section controller if already created.\n\t */\n\tgetSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t}): SectionControllerHandle | undefined;\n\n\t/**\n\t * Subscribe to section controller events.\n\t *\n\t * The listener is bound to the toolkit's *active section cohort* and\n\t * automatically migrates across cohort transitions\n\t * (`getOrCreateSectionController` for a different cohort). On every\n\t * migration the listener receives a snapshot replay of the new\n\t * cohort's already-loaded `content-loaded` events followed by the\n\t * aggregate `section-loading-complete`, in the canonical order a\n\t * fresh subscriber would have observed.\n\t *\n\t * Throws if no active section cohort exists; host code must call\n\t * `getOrCreateSectionController(...)` at least once before\n\t * subscribing. (`toolkit-ready` alone is not sufficient — it fires\n\t * once toolkit state has loaded but before any section controller\n\t * has been created.) The typical pattern is to subscribe once\n\t * immediately after the first `getOrCreateSectionController(...)`\n\t * resolves; the subscription then follows the active cohort across\n\t * all subsequent navigation without further wiring.\n\t *\n\t * Subscribing the same `listener` reference twice replaces the prior\n\t * subscription with the new one (filter args from the second call\n\t * win); calling the returned disposer twice is a no-op.\n\t *\n\t * A listener that throws is caught and `console.warn`-logged; the\n\t * throw does not interrupt fan-out to the remaining listeners.\n\t */\n\tsubscribeSectionEvents(args: SectionEventSubscriptionArgs): () => void;\n\n\t/**\n\t * Subscribe to item-scoped section controller events.\n\t *\n\t * Same active-cohort binding contract as {@link subscribeSectionEvents};\n\t * defaults `eventTypes` to the item-scoped subset.\n\t */\n\tsubscribeItemEvents(args: SectionItemEventSubscriptionArgs): () => void;\n\n\t/**\n\t * Subscribe to section-scoped lifecycle/loading/completion/error events.\n\t *\n\t * Same active-cohort binding contract as {@link subscribeSectionEvents};\n\t * defaults `eventTypes` to the section-scoped subset.\n\t */\n\tsubscribeSectionLifecycleEvents(\n\t\targs: SectionScopedEventSubscriptionArgs,\n\t): () => void;\n\n\t/**\n\t * Create or reuse a section controller with single-flight deduplication.\n\t */\n\tgetOrCreateSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t\tinput?: unknown;\n\t\tupdateExisting?: boolean;\n\t\tcreateDefaultController: () =>\n\t\t\t| SectionControllerHandle\n\t\t\t| Promise<SectionControllerHandle>;\n\t}): Promise<SectionControllerHandle>;\n\n\t/**\n\t * Dispose an existing section controller.\n\t */\n\tdisposeSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t\tpersistBeforeDispose?: boolean;\n\t\tclearPersistence?: boolean;\n\t}): Promise<void>;\n\n\t/**\n\t * Subscribe to framework-error events emitted by the coordinator.\n\t *\n\t * Mirrors the shape of {@link subscribeTelemetry} (see\n\t * `ToolkitCoordinator.subscribeTelemetry`): a synchronous,\n\t * multi-subscriber stream where each call to the internal bus's\n\t * `reportFrameworkError` fans out to every active listener exactly once.\n\t *\n\t * The returned function detaches the listener; calling it twice is a\n\t * no-op. A listener that throws is caught and logged; the throw does\n\t * not break fan-out to the remaining listeners.\n\t *\n\t * Use this to wire framework errors into Sentry / Datadog / a custom\n\t * banner without listening on a DOM event. The DOM event\n\t * (`framework-error`) and the canonical `onFrameworkError` prop on\n\t * `<pie-assessment-toolkit>` consume the same bus.\n\t */\n\tsubscribeFrameworkErrors(listener: FrameworkErrorListener): () => void;\n\n\t// ----------------------------------------------------------------\n\t// Tool Policy Engine — public surface (M8 PR 2 / PR 3).\n\t//\n\t// The coordinator owns a single `ToolPolicyEngine` instance and\n\t// exposes its decision and subscription surface through the API\n\t// so that toolbar custom elements (`pie-item-toolbar`,\n\t// `pie-section-toolbar`), the base section player, and bespoke\n\t// host instrumentation (PNP debugger, etc.) all flow through the\n\t// same engine. Hosts that want to drive PNP/profile inputs imperatively\n\t// (instead of binding props on `<pie-assessment-toolkit>`) call\n\t// `updateAssessment` / `updateCurrentItemRef` /\n\t// `setPnpEnforcement` directly.\n\t// ----------------------------------------------------------------\n\n\t/**\n\t * Resolve the visible tool set for a given placement level + scope.\n\t * Returns the engine's full decision (visible tools, diagnostics,\n\t * provenance). Hosts that only need the IDs may map\n\t * `decision.visibleTools` themselves.\n\t */\n\tdecideToolPolicy(request: ToolPolicyDecisionRequest): ToolPolicyDecision;\n\n\t/**\n\t * Subscribe to policy-engine change events. Fires whenever the\n\t * coordinator's bound policy inputs change (`updateToolConfig`,\n\t * `updateFloatingTools`, `updateAssessment`, `updateCurrentItemRef`,\n\t * `setPnpEnforcement`) or a custom `PolicySource` is registered /\n\t * removed. Listeners that need the new visible tool set should\n\t * call `decideToolPolicy(...)` with their level / scope.\n\t */\n\tonPolicyChange(listener: ToolPolicyChangeListener): () => void;\n\n\t/**\n\t * Bind (or clear) the active assessment for PNP/profile policy decisions.\n\t *\n\t * Under auto-mode (no host override via {@link setPnpEnforcement}),\n\t * the engine flips to `pnpEnforcement: \"on\"` iff the assessment\n\t * carries profile precedence material (`personalNeedsProfile`,\n\t * `settings.districtPolicy`, `settings.testAdministration`) or the\n\t * currently-bound item ref carries item-level profile inputs. A bare\n\t * assessment record (just `id` / `name`) keeps `\"off\"`.\n\t *\n\t * The host override set via {@link setPnpEnforcement} is sticky\n\t * across assessment swaps.\n\t */\n\tupdateAssessment(assessment: AssessmentEntity | null): void;\n\n\t/**\n\t * Bind (or clear) the current item reference for policy decisions.\n\t * Used by item-level profile gates (item `requiredTools` /\n\t * `restrictedTools` / `toolParameters`). Item-level profile material\n\t * also feeds the auto-mode helper — navigating to an item with\n\t * profile settings can flip auto-mode to `\"on\"` even when the parent\n\t * assessment carries no profile block of its own.\n\t */\n\tupdateCurrentItemRef(itemRef: AssessmentItemRef | null): void;\n\n\t/**\n\t * Override the auto-mode PNP/profile enforcement decision. Pass `\"on\"` /\n\t * `\"off\"` to pin the mode, or `null` to clear the override and\n\t * return to auto-mode (`\"on\"` iff the bound assessment / item ref\n\t * carries profile material, otherwise `\"off\"`).\n\t */\n\tsetPnpEnforcement(mode: PnpEnforcementMode | null): void;\n\n\t/**\n\t * Read the engine inputs currently driving decisions. Useful for\n\t * debugging / instrumentation; do not mutate.\n\t */\n\tgetPolicyInputs(): Readonly<ResolvedEngineInputs>;\n\n\t/**\n\t * Register a custom `PolicySource`. The source participates in\n\t * every subsequent `decideToolPolicy(...)` call until disposed\n\t * (the returned function detaches).\n\t */\n\tregisterPolicySource(source: PolicySource): () => void;\n\n\t/**\n\t * Register a host-owned resolver for scoped tool render context.\n\t */\n\tregisterToolContextResolver(\n\t\ttoolId: string,\n\t\tresolver: ToolContextResolver,\n\t): () => void;\n\n\t/**\n\t * Replace all host-owned render-context resolvers.\n\t */\n\tsetToolContextResolvers(\n\t\tresolvers: ToolContextResolverMap | null | undefined,\n\t): void;\n\n\t/**\n\t * Whether a host resolver is registered for this tool.\n\t */\n\thasToolContextResolver(toolId: string): boolean;\n\n\t/**\n\t * Resolve render visibility/params for a tool that already survived policy gates.\n\t */\n\tresolveToolContext(\n\t\tcontext: ToolContextResolverContext,\n\t): ResolvedToolContext | null;\n\n\t/**\n\t * Subscribe to resolver registration/removal changes.\n\t */\n\tonToolContextResolverChange(listener: () => void): () => void;\n}\n\n// I18nServiceApi is re-exported from @pie-players/pie-players-shared/i18n\n"]}
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/services/interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;;AAg1BH,0EAA0E","sourcesContent":["/**\n * Service Interfaces\n *\n * Public interfaces for all core assessment toolkit services.\n * These interfaces define the contracts that service implementations must fulfill,\n * enabling dependency injection, testing, and custom implementations.\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { I18nServiceApi } from \"@pie-players/pie-players-shared/i18n\";\nimport type {\n\tAccessibilityCatalogResolver,\n\tCatalogLookupContext,\n\tCatalogLookupOptions,\n\tCatalogOwnerContext,\n\tCatalogStatistics,\n\tCatalogType,\n\tResolvedCatalog,\n} from \"./AccessibilityCatalogResolver.js\";\nimport type { FrameworkErrorListener } from \"./framework-error-bus.js\";\nimport type { HighlightColor, HighlightType } from \"./HighlightCoordinator.js\";\nimport type {\n\tSectionControllerHandle,\n\tSectionItemEventSubscriptionArgs,\n\tSectionScopedEventSubscriptionArgs,\n\tSectionEventSubscriptionArgs,\n\tToolkitCoordinatorHooks,\n\tToolkitInitStatus,\n} from \"./ToolkitCoordinator.js\";\nimport type { FontSize, ThemeConfig } from \"./ThemeProvider.js\";\nimport type { ZIndexLayer } from \"./ToolCoordinator.js\";\nimport type { PlaybackState, TTSConfig } from \"./TTSService.js\";\nimport type {\n\tToolPlacementConfig,\n\tToolPlacementLevel,\n\tToolProviderConfig,\n} from \"./tools-config-normalizer.js\";\nimport type { ToolProviderRegistry } from \"./tool-providers/ToolProviderRegistry.js\";\nimport type {\n\tPolicySource,\n\tPnpEnforcementMode,\n\tResolvedEngineInputs,\n\tToolPolicyChangeListener,\n\tToolPolicyDecision,\n\tToolPolicyDecisionRequest,\n} from \"../policy/engine.js\";\nimport type {\n\tAssessmentEntity,\n\tAssessmentItemRef,\n} from \"@pie-players/pie-players-shared/types\";\nimport type {\n\tITTSProvider,\n\tTTSProviderCapabilities,\n} from \"@pie-players/pie-tts\";\nimport type {\n\tResolvedToolContext,\n\tToolContextResolver,\n\tToolContextResolverContext,\n\tToolContextResolverMap,\n} from \"./ToolRegistry.js\";\n\n// Re-export I18nServiceApi from players-shared\nexport type { I18nServiceApi };\n\n/**\n * Theme provider interface\n *\n * Applies consistent accessibility theming across items and tools.\n */\nexport interface ThemeProviderApi {\n\t/**\n\t * Apply theme configuration\n\t */\n\tapplyTheme(config: ThemeConfig): void;\n\n\t/**\n\t * Get current theme configuration\n\t */\n\tgetCurrentTheme(): Required<ThemeConfig>;\n\n\t/**\n\t * Reset to default theme\n\t */\n\treset(): void;\n\n\t/**\n\t * Clean up and remove theme styles\n\t */\n\tdestroy(): void;\n}\n\n/**\n * Highlight coordinator interface\n *\n * Manages content highlighting for TTS, annotations, and selections.\n */\nexport interface HighlightCoordinatorApi {\n\t/**\n\t * Highlight a text range\n\t */\n\thighlightRange(\n\t\trange: Range,\n\t\ttype: HighlightType,\n\t\tcolor: HighlightColor,\n\t): void;\n\n\t/**\n\t * Highlight a word for TTS (temporary)\n\t */\n\thighlightTTSWord(\n\t\ttextNode: Text,\n\t\tstartOffset: number,\n\t\tendOffset: number,\n\t): void;\n\n\t/**\n\t * Highlight a single element as the active TTS word (temporary).\n\t *\n\t * Marks exactly the supplied element (via `data-pie-tts-word-element`)\n\t * without walking up to a containing ancestor. Used for atomic targets that\n\t * have no direct text node to range over — most notably MathJax CHTML tokens\n\t * (e.g. `<mjx-mi><mjx-c/></mjx-mi>`) and whole-expression fallbacks. Painting\n\t * the element itself is what lets a resolved math token highlight as a token\n\t * rather than escalating to the full `<math>` / `<mjx-container>`.\n\t *\n\t * Optional so lightweight coordinator mocks can omit it.\n\t */\n\thighlightTTSWordElement?(element: Element): void;\n\n\t/**\n\t * Highlight sentence(s) for TTS (background layer)\n\t */\n\thighlightTTSSentence(ranges: Range[]): void;\n\n\t/**\n\t * Clear all TTS highlights (word and sentence)\n\t */\n\tclearTTS(): void;\n\n\t/**\n\t * Clear highlights of a specific type\n\t */\n\tclearHighlights(type: HighlightType): void;\n\n\t/**\n\t * Clear all highlights\n\t */\n\tclearAll(): void;\n\n\t/**\n\t * Check if highlighting is supported in current environment\n\t */\n\tisSupported(): boolean;\n\n\t/**\n\t * Update TTS highlight style dynamically\n\t */\n\tupdateTTSHighlightStyle(color: string, opacity: number): void;\n}\n\n/**\n * Tool state interface\n */\nexport interface ToolState {\n\tid: string;\n\tname: string;\n\tisVisible: boolean;\n\telement: HTMLElement | null;\n\tlayer: ZIndexLayer;\n}\n\n/**\n * Tool coordinator interface\n *\n * Manages z-index layering and visibility for floating tools.\n */\nexport interface ToolCoordinatorApi {\n\t/**\n\t * Register a tool with the coordinator\n\t */\n\tregisterTool(\n\t\tid: string,\n\t\tname: string,\n\t\telement?: HTMLElement,\n\t\tlayer?: ZIndexLayer,\n\t): void;\n\n\t/**\n\t * Unregister a tool\n\t */\n\tunregisterTool(id: string): void;\n\n\t/**\n\t * Show a tool\n\t */\n\tshowTool(id: string): void;\n\n\t/**\n\t * Hide a tool\n\t */\n\thideTool(id: string): void;\n\n\t/**\n\t * Toggle tool visibility\n\t */\n\ttoggleTool(id: string): void;\n\n\t/**\n\t * Check if tool is visible\n\t */\n\tisToolVisible(id: string): boolean;\n\n\t/**\n\t * Bring an element to the front of its layer\n\t */\n\tbringToFront(element: HTMLElement): void;\n\n\t/**\n\t * Update the element reference for a tool\n\t */\n\tupdateToolElement(id: string, element: HTMLElement): void;\n\n\t/**\n\t * Get state for a specific tool\n\t */\n\tgetToolState(id: string): ToolState | undefined;\n\n\t/**\n\t * Get all visible tools\n\t */\n\tgetVisibleTools(): ToolState[];\n\n\t/**\n\t * Subscribe to tool state changes\n\t */\n\tsubscribe(listener: () => void): () => void;\n}\n\n/**\n * TTS service interface\n *\n * Provides text-to-speech functionality with provider-based architecture.\n * Supports QTI 3.0 accessibility catalogs for pre-authored spoken content.\n */\nexport interface TtsServiceApi {\n\t/**\n\t * Initialize TTS with a provider\n\t */\n\tinitialize(\n\t\tprovider: ITTSProvider,\n\t\tconfig?: Partial<TTSConfig>,\n\t): Promise<void>;\n\n\t/**\n\t * Speak text with optional catalog support\n\t */\n\tspeak(\n\t\ttext: string,\n\t\toptions?: {\n\t\t\tcatalogId?: string;\n\t\t\tcatalogContext?: CatalogLookupContext;\n\t\t\tlanguage?: string;\n\t\t\tcontentElement?: Element;\n\t\t},\n\t): Promise<void>;\n\n\t/**\n\t * Speak a text range\n\t */\n\tspeakRange(\n\t\trange: Range,\n\t\toptions?: { contentRoot?: Element | null },\n\t): Promise<void>;\n\n\t/**\n\t * Pause playback\n\t */\n\tpause(): void;\n\n\t/**\n\t * Resume playback\n\t */\n\tresume(): void;\n\n\t/**\n\t * Stop playback\n\t */\n\tstop(): void;\n\n\t/**\n\t * Request active TTS controls to hand off/deactivate their UI state.\n\t *\n\t * This is an orchestration hint for TTS tool chrome and does not replace\n\t * playback controls such as stop/pause/resume.\n\t */\n\trequestControlHandoff(): void;\n\n\t/**\n\t * Seek forward by sentence units\n\t */\n\tseekForward(units?: number): Promise<void>;\n\n\t/**\n\t * Seek backward by sentence units\n\t */\n\tseekBackward(units?: number): Promise<void>;\n\n\t/**\n\t * Check if currently playing\n\t */\n\tisPlaying(): boolean;\n\n\t/**\n\t * Check if paused\n\t */\n\tisPaused(): boolean;\n\n\t/**\n\t * Get current playback state\n\t */\n\tgetState(): PlaybackState;\n\n\t/**\n\t * Get currently speaking text\n\t */\n\tgetCurrentText(): string | null;\n\n\t/**\n\t * Subscribe to state changes\n\t */\n\tonStateChange(id: string, callback: (state: PlaybackState) => void): void;\n\n\t/**\n\t * Unsubscribe from state changes\n\t */\n\toffStateChange(id: string, callback: (state: PlaybackState) => void): void;\n\n\t/**\n\t * Get capabilities of current provider\n\t */\n\tgetCapabilities(): TTSProviderCapabilities | null;\n\n\t/**\n\t * Update TTS settings dynamically (rate, pitch, voice)\n\t */\n\tupdateSettings(settings: Partial<TTSConfig>): Promise<void>;\n\n\t/**\n\t * Set highlight coordinator for word highlighting\n\t */\n\tsetHighlightCoordinator(coordinator: HighlightCoordinatorApi): void;\n\n\t/**\n\t * Set accessibility catalog resolver for spoken content\n\t */\n\tsetCatalogResolver(resolver: AccessibilityCatalogResolver): void;\n}\n\n/**\n * Accessibility catalog resolver interface\n *\n * Manages QTI 3.0 accessibility catalogs at assessment and item levels.\n * Provides lookup and resolution services for alternative content representations.\n */\nexport interface AccessibilityCatalogResolverApi {\n\t/**\n\t * Set the default language for fallback resolution\n\t */\n\tsetDefaultLanguage(language: string): void;\n\n\t/**\n\t * Get the default language\n\t */\n\tgetDefaultLanguage(): string;\n\n\t/**\n\t * Add item-level catalogs (called when rendering a new item)\n\t */\n\taddItemCatalogs(catalogs: any[]): void;\n\n\t/**\n\t * Register catalogs scoped to a mounted content owner.\n\t */\n\tregisterCatalogs?(\n\t\tcontext: CatalogOwnerContext,\n\t\tcatalogs: any[],\n\t): () => void;\n\n\t/**\n\t * Clear item-level catalogs (called when leaving an item)\n\t */\n\tclearItemCatalogs(): void;\n\n\t/**\n\t * Check if a catalog exists\n\t */\n\thasCatalog(catalogId: string): boolean;\n\n\t/**\n\t * Get alternative content for a catalog identifier\n\t */\n\tgetAlternative(\n\t\tcatalogId: string,\n\t\toptions: CatalogLookupOptions,\n\t): ResolvedCatalog | null;\n\n\t/**\n\t * Get all available alternatives for a catalog identifier\n\t */\n\tgetAllAlternatives(catalogId: string): ResolvedCatalog[];\n\n\t/**\n\t * Get all catalog identifiers available\n\t */\n\tgetAllCatalogIds(): string[];\n\n\t/**\n\t * Get statistics about available catalogs\n\t */\n\tgetStatistics(): CatalogStatistics;\n\n\t/**\n\t * Check if a specific catalog type is available\n\t */\n\thasAlternativeType(catalogId: string, type: CatalogType): boolean;\n\n\t/**\n\t * Get all catalog IDs that have a specific type of alternative\n\t */\n\tgetCatalogsByType(type: CatalogType): string[];\n\n\t/**\n\t * Reset all catalogs\n\t */\n\treset(): void;\n\n\t/**\n\t * Destroy and cleanup\n\t */\n\tdestroy(): void;\n}\n\n/**\n * Element tool state store interface\n *\n * Manages element-level ephemeral tool state using composite keys for global uniqueness.\n * Tool state is client-only and separate from PIE session data (which is sent to server for scoring).\n */\nexport interface ElementToolStateStoreApi {\n\t/**\n\t * Generate a globally unique element ID from components\n\t */\n\tgetGlobalElementId(\n\t\tassessmentId: string,\n\t\tsectionId: string,\n\t\titemId: string,\n\t\telementId: string,\n\t): string;\n\n\t/**\n\t * Parse a global element ID into its components\n\t */\n\tparseGlobalElementId(globalElementId: string): {\n\t\tassessmentId: string;\n\t\tsectionId: string;\n\t\titemId: string;\n\t\telementId: string;\n\t} | null;\n\n\t/**\n\t * Set state for a specific tool on an element\n\t */\n\tsetState(globalElementId: string, toolId: string, state: any): void;\n\n\t/**\n\t * Get state for a specific tool on an element\n\t */\n\tgetState(globalElementId: string, toolId: string): any | undefined;\n\n\t/**\n\t * Get all tool states for a specific element\n\t */\n\tgetElementState(globalElementId: string): Record<string, any>;\n\n\t/**\n\t * Get all element states\n\t */\n\tgetAllState(): Record<string, Record<string, any>>;\n\n\t/**\n\t * Subscribe to state changes\n\t */\n\tsubscribe(\n\t\tcallback: (state: Map<string, Map<string, any>>) => void,\n\t): () => void;\n\n\t/**\n\t * Set callback for persistence integration\n\t */\n\tsetOnStateChange(\n\t\tcallback: (state: Record<string, Record<string, any>>) => void,\n\t): void;\n\n\t/**\n\t * Load state from persistence\n\t */\n\tloadState(state: Record<string, Record<string, any>>): void;\n\n\t/**\n\t * Clear state for a specific element\n\t */\n\tclearElement(globalElementId: string): void;\n\n\t/**\n\t * Clear state for a specific tool across all elements\n\t */\n\tclearTool(toolId: string): void;\n\n\t/**\n\t * Clear all elements in a specific section\n\t */\n\tclearSection(assessmentId: string, sectionId: string): void;\n\n\t/**\n\t * Clear all state\n\t */\n\tclearAll(): void;\n}\n\n/**\n * Toolkit coordinator interface\n *\n * Orchestrates all toolkit services (TTS, tools, accessibility, state management) from a single entry point.\n * Provides centralized configuration for tool availability and settings.\n */\nexport interface ToolkitCoordinatorApi {\n\t/**\n\t * Assessment identifier\n\t */\n\treadonly assessmentId: string;\n\n\t/**\n\t * Configuration\n\t */\n\treadonly config: {\n\t\ttools?: {\n\t\t\tproviders?: Record<string, ToolProviderConfig | undefined>;\n\t\t};\n\t};\n\n\t/**\n\t * TTS service\n\t */\n\treadonly ttsService: TtsServiceApi;\n\n\t/**\n\t * Tool coordinator\n\t */\n\treadonly toolCoordinator: ToolCoordinatorApi;\n\n\t/**\n\t * Highlight coordinator\n\t */\n\treadonly highlightCoordinator: HighlightCoordinatorApi;\n\n\t/**\n\t * Element tool state store\n\t */\n\treadonly elementToolStateStore: ElementToolStateStoreApi;\n\n\t/**\n\t * Catalog resolver\n\t */\n\treadonly catalogResolver: AccessibilityCatalogResolverApi;\n\n\t/**\n\t * Tool provider registry\n\t */\n\treadonly toolProviderRegistry: ToolProviderRegistry;\n\n\t/**\n\t * Get all services as a bundle\n\t */\n\tgetServiceBundle(): {\n\t\tttsService: TtsServiceApi;\n\t\ttoolCoordinator: ToolCoordinatorApi;\n\t\thighlightCoordinator: HighlightCoordinatorApi;\n\t\telementToolStateStore: ElementToolStateStoreApi;\n\t\tcatalogResolver: AccessibilityCatalogResolverApi;\n\t\ttoolProviderRegistry: ToolProviderRegistry;\n\t};\n\n\t/**\n\t * Ensure TTS service is initialized and ready.\n\t */\n\tensureTTSReady(config?: Record<string, unknown>): Promise<void>;\n\n\t/**\n\t * Ensure a provider is initialized and ready.\n\t */\n\tensureProviderReady(providerId: string): Promise<unknown>;\n\n\t/**\n\t * Wait until coordinator initialization is complete.\n\t */\n\twaitUntilReady(): Promise<void>;\n\n\t/**\n\t * Check if coordinator has completed initialization.\n\t */\n\tisReady(): boolean;\n\n\t/**\n\t * Read current initialization status.\n\t */\n\tgetInitStatus(): ToolkitInitStatus;\n\n\t/**\n\t * Check if a tool is enabled\n\t */\n\tisToolEnabled(toolId: string): boolean;\n\n\t/**\n\t * Get tool configuration\n\t */\n\tgetToolConfig(toolId: string): ToolProviderConfig | null;\n\n\t/**\n\t * Update tool configuration\n\t */\n\tupdateToolConfig(toolId: string, updates: Partial<ToolProviderConfig>): void;\n\n\t/**\n\t * Update the enabled tool list for one placement level.\n\t */\n\tupdateToolPlacement(level: ToolPlacementLevel, toolIds: string[]): void;\n\n\t/**\n\t * Patch one or more placement levels in the canonical tools config.\n\t */\n\tupdateToolsPlacement(partial: ToolPlacementConfig): void;\n\n\t/**\n\t * Register or update lifecycle hooks at runtime.\n\t */\n\tsetHooks(hooks: ToolkitCoordinatorHooks): void;\n\n\t/**\n\t * Return a section controller if already created.\n\t */\n\tgetSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t}): SectionControllerHandle | undefined;\n\n\t/**\n\t * Subscribe to section controller events.\n\t *\n\t * The listener is bound to the toolkit's *active section cohort* and\n\t * automatically migrates across cohort transitions\n\t * (`getOrCreateSectionController` for a different cohort). On every\n\t * migration the listener receives a snapshot replay of the new\n\t * cohort's already-loaded `content-loaded` events followed by the\n\t * aggregate `section-loading-complete`, in the canonical order a\n\t * fresh subscriber would have observed.\n\t *\n\t * Throws if no active section cohort exists; host code must call\n\t * `getOrCreateSectionController(...)` at least once before\n\t * subscribing. (`toolkit-ready` alone is not sufficient — it fires\n\t * once toolkit state has loaded but before any section controller\n\t * has been created.) The typical pattern is to subscribe once\n\t * immediately after the first `getOrCreateSectionController(...)`\n\t * resolves; the subscription then follows the active cohort across\n\t * all subsequent navigation without further wiring.\n\t *\n\t * Subscribing the same `listener` reference twice replaces the prior\n\t * subscription with the new one (filter args from the second call\n\t * win); calling the returned disposer twice is a no-op.\n\t *\n\t * A listener that throws is caught and `console.warn`-logged; the\n\t * throw does not interrupt fan-out to the remaining listeners.\n\t */\n\tsubscribeSectionEvents(args: SectionEventSubscriptionArgs): () => void;\n\n\t/**\n\t * Subscribe to item-scoped section controller events.\n\t *\n\t * Same active-cohort binding contract as {@link subscribeSectionEvents};\n\t * defaults `eventTypes` to the item-scoped subset.\n\t */\n\tsubscribeItemEvents(args: SectionItemEventSubscriptionArgs): () => void;\n\n\t/**\n\t * Subscribe to section-scoped lifecycle/loading/completion/error events.\n\t *\n\t * Same active-cohort binding contract as {@link subscribeSectionEvents};\n\t * defaults `eventTypes` to the section-scoped subset.\n\t */\n\tsubscribeSectionLifecycleEvents(\n\t\targs: SectionScopedEventSubscriptionArgs,\n\t): () => void;\n\n\t/**\n\t * Create or reuse a section controller with single-flight deduplication.\n\t */\n\tgetOrCreateSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t\tinput?: unknown;\n\t\tupdateExisting?: boolean;\n\t\tcreateDefaultController: () =>\n\t\t\t| SectionControllerHandle\n\t\t\t| Promise<SectionControllerHandle>;\n\t}): Promise<SectionControllerHandle>;\n\n\t/**\n\t * Dispose an existing section controller.\n\t */\n\tdisposeSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t\tpersistBeforeDispose?: boolean;\n\t\tclearPersistence?: boolean;\n\t}): Promise<void>;\n\n\t/**\n\t * Subscribe to framework-error events emitted by the coordinator.\n\t *\n\t * Mirrors the shape of {@link subscribeTelemetry} (see\n\t * `ToolkitCoordinator.subscribeTelemetry`): a synchronous,\n\t * multi-subscriber stream where each call to the internal bus's\n\t * `reportFrameworkError` fans out to every active listener exactly once.\n\t *\n\t * The returned function detaches the listener; calling it twice is a\n\t * no-op. A listener that throws is caught and logged; the throw does\n\t * not break fan-out to the remaining listeners.\n\t *\n\t * Use this to wire framework errors into Sentry / Datadog / a custom\n\t * banner without listening on a DOM event. The DOM event\n\t * (`framework-error`) and the canonical `onFrameworkError` prop on\n\t * `<pie-assessment-toolkit>` consume the same bus.\n\t */\n\tsubscribeFrameworkErrors(listener: FrameworkErrorListener): () => void;\n\n\t// ----------------------------------------------------------------\n\t// Tool Policy Engine — public surface (M8 PR 2 / PR 3).\n\t//\n\t// The coordinator owns a single `ToolPolicyEngine` instance and\n\t// exposes its decision and subscription surface through the API\n\t// so that toolbar custom elements (`pie-item-toolbar`,\n\t// `pie-section-toolbar`), the base section player, and bespoke\n\t// host instrumentation (PNP debugger, etc.) all flow through the\n\t// same engine. Hosts that want to drive PNP/profile inputs imperatively\n\t// (instead of binding props on `<pie-assessment-toolkit>`) call\n\t// `updateAssessment` / `updateCurrentItemRef` /\n\t// `setPnpEnforcement` directly.\n\t// ----------------------------------------------------------------\n\n\t/**\n\t * Resolve the visible tool set for a given placement level + scope.\n\t * Returns the engine's full decision (visible tools, diagnostics,\n\t * provenance). Hosts that only need the IDs may map\n\t * `decision.visibleTools` themselves.\n\t */\n\tdecideToolPolicy(request: ToolPolicyDecisionRequest): ToolPolicyDecision;\n\n\t/**\n\t * Subscribe to policy-engine change events. Fires whenever the\n\t * coordinator's bound policy inputs change (`updateToolConfig`,\n\t * `updateToolPlacement`, `updateAssessment`, `updateCurrentItemRef`,\n\t * `setPnpEnforcement`) or a custom `PolicySource` is registered /\n\t * removed. Listeners that need the new visible tool set should\n\t * call `decideToolPolicy(...)` with their level / scope.\n\t */\n\tonPolicyChange(listener: ToolPolicyChangeListener): () => void;\n\n\t/**\n\t * Bind (or clear) the active assessment for PNP/profile policy decisions.\n\t *\n\t * Under auto-mode (no host override via {@link setPnpEnforcement}),\n\t * the engine flips to `pnpEnforcement: \"on\"` iff the assessment\n\t * carries profile precedence material (`personalNeedsProfile`,\n\t * `settings.districtPolicy`, `settings.testAdministration`) or the\n\t * currently-bound item ref carries item-level profile inputs. A bare\n\t * assessment record (just `id` / `name`) keeps `\"off\"`.\n\t *\n\t * The host override set via {@link setPnpEnforcement} is sticky\n\t * across assessment swaps.\n\t */\n\tupdateAssessment(assessment: AssessmentEntity | null): void;\n\n\t/**\n\t * Bind (or clear) the current item reference for policy decisions.\n\t * Used by item-level profile gates (item `requiredTools` /\n\t * `restrictedTools` / `toolParameters`). Item-level profile material\n\t * also feeds the auto-mode helper — navigating to an item with\n\t * profile settings can flip auto-mode to `\"on\"` even when the parent\n\t * assessment carries no profile block of its own.\n\t */\n\tupdateCurrentItemRef(itemRef: AssessmentItemRef | null): void;\n\n\t/**\n\t * Override the auto-mode PNP/profile enforcement decision. Pass `\"on\"` /\n\t * `\"off\"` to pin the mode, or `null` to clear the override and\n\t * return to auto-mode (`\"on\"` iff the bound assessment / item ref\n\t * carries profile material, otherwise `\"off\"`).\n\t */\n\tsetPnpEnforcement(mode: PnpEnforcementMode | null): void;\n\n\t/**\n\t * Read the engine inputs currently driving decisions. Useful for\n\t * debugging / instrumentation; do not mutate.\n\t */\n\tgetPolicyInputs(): Readonly<ResolvedEngineInputs>;\n\n\t/**\n\t * Register a custom `PolicySource`. The source participates in\n\t * every subsequent `decideToolPolicy(...)` call until disposed\n\t * (the returned function detaches).\n\t */\n\tregisterPolicySource(source: PolicySource): () => void;\n\n\t/**\n\t * Register a host-owned resolver for scoped tool render context.\n\t */\n\tregisterToolContextResolver(\n\t\ttoolId: string,\n\t\tresolver: ToolContextResolver,\n\t): () => void;\n\n\t/**\n\t * Replace all host-owned render-context resolvers.\n\t */\n\tsetToolContextResolvers(\n\t\tresolvers: ToolContextResolverMap | null | undefined,\n\t): void;\n\n\t/**\n\t * Whether a host resolver is registered for this tool.\n\t */\n\thasToolContextResolver(toolId: string): boolean;\n\n\t/**\n\t * Resolve render visibility/params for a tool that already survived policy gates.\n\t */\n\tresolveToolContext(\n\t\tcontext: ToolContextResolverContext,\n\t): ResolvedToolContext | null;\n\n\t/**\n\t * Subscribe to resolver registration/removal changes.\n\t */\n\tonToolContextResolverChange(listener: () => void): () => void;\n}\n\n// I18nServiceApi is re-exported from @pie-players/pie-players-shared/i18n\n"]}
|
|
@@ -4,7 +4,7 @@ import type { ToolRegistry } from "./ToolRegistry.js";
|
|
|
4
4
|
export type ToolConfigStrictness = "off" | "warn" | "error";
|
|
5
5
|
export type ToolConfigDiagnosticSeverity = "warning" | "error";
|
|
6
6
|
export interface ToolConfigDiagnostic {
|
|
7
|
-
code: "tools.unknownToolId" | "tools.unsupportedLevel" | "tools.unknownProviderKey" | "tools.
|
|
7
|
+
code: "tools.unknownToolId" | "tools.unsupportedLevel" | "tools.unknownProviderKey" | "tools.removedProviderKey" | "tools.providerSanitizeFailed" | "tools.providerValidateFailed" | "tools.invalidProviderValidation";
|
|
8
8
|
severity: ToolConfigDiagnosticSeverity;
|
|
9
9
|
path: string;
|
|
10
10
|
message: string;
|
|
@@ -161,10 +161,10 @@ function collectPolicyDiagnostics(config, toolMap, diagnostics) {
|
|
|
161
161
|
}));
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
function collectProviderKeyDiagnostics(config, toolMap,
|
|
165
|
-
if (
|
|
164
|
+
function collectProviderKeyDiagnostics(config, toolMap, hasRemovedTtsKey, diagnostics) {
|
|
165
|
+
if (hasRemovedTtsKey) {
|
|
166
166
|
diagnostics.push(createDiagnostic({
|
|
167
|
-
code: "tools.
|
|
167
|
+
code: "tools.removedProviderKey",
|
|
168
168
|
severity: "error",
|
|
169
169
|
path: "providers.tts",
|
|
170
170
|
message: `Provider key "tts" is no longer supported. Use "providers.textToSpeech".`,
|
|
@@ -205,7 +205,7 @@ export function normalizeAndValidateToolsConfig(input, options = {}) {
|
|
|
205
205
|
const registryTools = getRegistryToolMap(options.toolRegistry ?? createPackagedToolRegistry());
|
|
206
206
|
const normalized = normalizeToolsConfig(input);
|
|
207
207
|
const diagnostics = [];
|
|
208
|
-
const
|
|
208
|
+
const hasRemovedTtsKey = Object.hasOwn(normalized.providers, "tts");
|
|
209
209
|
const nextProviders = {
|
|
210
210
|
...(normalized.providers || {}),
|
|
211
211
|
};
|
|
@@ -224,9 +224,9 @@ export function normalizeAndValidateToolsConfig(input, options = {}) {
|
|
|
224
224
|
};
|
|
225
225
|
collectPlacementDiagnostics(nextConfig, registryTools, diagnostics);
|
|
226
226
|
collectPolicyDiagnostics(nextConfig, registryTools, diagnostics);
|
|
227
|
-
collectProviderKeyDiagnostics(nextConfig, registryTools,
|
|
228
|
-
if (
|
|
229
|
-
throwValidationError(diagnostics.filter((entry) => entry.code === "tools.
|
|
227
|
+
collectProviderKeyDiagnostics(nextConfig, registryTools, hasRemovedTtsKey, diagnostics);
|
|
228
|
+
if (hasRemovedTtsKey) {
|
|
229
|
+
throwValidationError(diagnostics.filter((entry) => entry.code === "tools.removedProviderKey"), source);
|
|
230
230
|
}
|
|
231
231
|
if (strictness === "warn") {
|
|
232
232
|
emitWarnings(diagnostics, source);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-config-validation.js","sourceRoot":"","sources":["../../src/services/tool-config-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tnormalizeToolsConfig,\n\ttype CanonicalToolsConfig,\n\ttype ToolPlacementLevel,\n\ttype ToolProviderConfig,\n} from \"./tools-config-normalizer.js\";\nimport {\n\tframeworkErrorFromToolConfigDiagnostics,\n\tframeworkErrorFromUnknown,\n\ttype FrameworkErrorModel,\n} from \"./framework-error.js\";\nimport type { ToolRegistration, ToolRegistry } from \"./ToolRegistry.js\";\nimport { createPackagedToolRegistry } from \"./createDefaultToolRegistry.js\";\n\nexport type ToolConfigStrictness = \"off\" | \"warn\" | \"error\";\n\nexport type ToolConfigDiagnosticSeverity = \"warning\" | \"error\";\n\nexport interface ToolConfigDiagnostic {\n\tcode:\n\t\t| \"tools.unknownToolId\"\n\t\t| \"tools.unsupportedLevel\"\n\t\t| \"tools.unknownProviderKey\"\n\t\t| \"tools.deprecatedProviderKey\"\n\t\t| \"tools.providerSanitizeFailed\"\n\t\t| \"tools.providerValidateFailed\"\n\t\t| \"tools.invalidProviderValidation\";\n\tseverity: ToolConfigDiagnosticSeverity;\n\tpath: string;\n\tmessage: string;\n\ttoolId?: string;\n\tproviderId?: string;\n}\n\nexport interface ToolConfigValidationOptions {\n\tstrictness?: ToolConfigStrictness;\n\tsource?: string;\n\ttoolRegistry?: ToolRegistry | null;\n}\n\nexport interface ToolConfigValidationResult {\n\tconfig: CanonicalToolsConfig;\n\tdiagnostics: ToolConfigDiagnostic[];\n}\n\nexport function frameworkErrorFromToolConfigValidation(args: {\n\tsource: string;\n\tdiagnostics?: ToolConfigDiagnostic[] | null;\n\terror?: unknown;\n\trecoverable?: boolean;\n}): FrameworkErrorModel {\n\tif (Array.isArray(args.diagnostics) && args.diagnostics.length > 0) {\n\t\treturn frameworkErrorFromToolConfigDiagnostics({\n\t\t\tsource: args.source,\n\t\t\tdiagnostics: args.diagnostics,\n\t\t\trecoverable: args.recoverable,\n\t\t});\n\t}\n\treturn frameworkErrorFromUnknown({\n\t\tkind: \"tool-config\",\n\t\tsource: args.source,\n\t\terror: args.error ?? new Error(\"Invalid tools config\"),\n\t\trecoverable: args.recoverable,\n\t});\n}\n\nconst DEFAULT_STRICTNESS: ToolConfigStrictness = \"error\";\n\nexport function normalizeToolConfigStrictness(\n\tvalue: unknown,\n): ToolConfigStrictness {\n\tif (value === \"off\" || value === \"warn\" || value === \"error\") {\n\t\treturn value;\n\t}\n\treturn DEFAULT_STRICTNESS;\n}\n\nfunction createDiagnostic(\n\tdiagnostic: ToolConfigDiagnostic,\n): ToolConfigDiagnostic {\n\treturn diagnostic;\n}\n\nfunction getRegistryToolMap(\n\ttoolRegistry: ToolRegistry | null | undefined,\n): Map<string, ToolRegistration> {\n\tif (!toolRegistry) return new Map();\n\treturn new Map(\n\t\ttoolRegistry\n\t\t\t.getAllTools()\n\t\t\t.slice()\n\t\t\t.sort((left, right) => left.toolId.localeCompare(right.toolId))\n\t\t\t.map((tool) => [tool.toolId, tool]),\n\t);\n}\n\nfunction sanitizeProviderConfig(\n\tproviderId: string,\n\tproviderConfig: ToolProviderConfig | undefined,\n\ttool: ToolRegistration | undefined,\n\tdiagnostics: ToolConfigDiagnostic[],\n): ToolProviderConfig | undefined {\n\tif (!providerConfig || !tool?.provider?.sanitizeConfig) return providerConfig;\n\ttry {\n\t\tconst sanitized = tool.provider.sanitizeConfig(providerConfig);\n\t\tif (!sanitized || typeof sanitized !== \"object\" || Array.isArray(sanitized)) {\n\t\t\tdiagnostics.push(\n\t\t\t\tcreateDiagnostic({\n\t\t\t\t\tcode: \"tools.providerSanitizeFailed\",\n\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\t\tmessage: `Provider sanitizer for \"${providerId}\" must return an object.`,\n\t\t\t\t\tproviderId,\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn providerConfig;\n\t\t}\n\t\treturn sanitized as ToolProviderConfig;\n\t} catch (error) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.providerSanitizeFailed\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Provider sanitizer failed for \"${providerId}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t\treturn providerConfig;\n\t}\n}\n\nfunction validateProviderConfig(\n\tproviderId: string,\n\tproviderConfig: ToolProviderConfig | undefined,\n\ttool: ToolRegistration | undefined,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (!providerConfig || !tool?.provider?.validateConfig) return;\n\ttry {\n\t\tconst providerDiagnostics = tool.provider.validateConfig(providerConfig);\n\t\tif (!Array.isArray(providerDiagnostics)) {\n\t\t\tdiagnostics.push(\n\t\t\t\tcreateDiagnostic({\n\t\t\t\t\tcode: \"tools.invalidProviderValidation\",\n\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\t\tmessage: `Provider validator for \"${providerId}\" must return an array.`,\n\t\t\t\t\tproviderId,\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tfor (const diagnostic of providerDiagnostics) {\n\t\t\tdiagnostics.push({\n\t\t\t\t...diagnostic,\n\t\t\t\tpath: diagnostic.path || `providers.${providerId}`,\n\t\t\t\tproviderId: diagnostic.providerId || providerId,\n\t\t\t\ttoolId: diagnostic.toolId || providerId,\n\t\t\t});\n\t\t}\n\t} catch (error) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.providerValidateFailed\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Provider validator failed for \"${providerId}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction collectPlacementDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (toolMap.size === 0) return;\n\tconst levelOrder: ToolPlacementLevel[] = [\"section\", \"item\", \"passage\"];\n\tfor (const level of levelOrder) {\n\t\tfor (const toolId of config.placement[level]) {\n\t\t\tconst tool = toolMap.get(toolId);\n\t\t\tif (!tool) {\n\t\t\t\tdiagnostics.push(\n\t\t\t\t\tcreateDiagnostic({\n\t\t\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tpath: `placement.${level}`,\n\t\t\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in placement.${level}.`,\n\t\t\t\t\t\ttoolId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst supportsLevel = tool.supportedLevels.includes(level);\n\t\t\t// Leniency policy: section-capable tools are tolerated in item placement.\n\t\t\t// This avoids strict failures for common host config patterns while still\n\t\t\t// enforcing upward-invalid placements (for example item-only -> section).\n\t\t\tconst allowSectionToolInItemPlacement =\n\t\t\t\tlevel === \"item\" && tool.supportedLevels.includes(\"section\");\n\t\t\tif (!supportsLevel && !allowSectionToolInItemPlacement) {\n\t\t\t\tdiagnostics.push(\n\t\t\t\t\tcreateDiagnostic({\n\t\t\t\t\t\tcode: \"tools.unsupportedLevel\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tpath: `placement.${level}`,\n\t\t\t\t\t\tmessage: `Tool \"${toolId}\" does not support level \"${level}\".`,\n\t\t\t\t\t\ttoolId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction collectPolicyDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (toolMap.size === 0) return;\n\tfor (const toolId of config.policy.allowed || []) {\n\t\tif (toolMap.has(toolId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"policy.allowed\",\n\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in policy.allowed.`,\n\t\t\t\ttoolId,\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const toolId of config.policy.blocked || []) {\n\t\tif (toolMap.has(toolId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"policy.blocked\",\n\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in policy.blocked.`,\n\t\t\t\ttoolId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction collectProviderKeyDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\thasDeprecatedTtsKey: boolean,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (hasDeprecatedTtsKey) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.deprecatedProviderKey\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"providers.tts\",\n\t\t\t\tmessage:\n\t\t\t\t\t`Provider key \"tts\" is no longer supported. Use \"providers.textToSpeech\".`,\n\t\t\t\tproviderId: \"tts\",\n\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const providerId of Object.keys(config.providers).sort()) {\n\t\tif (toolMap.size === 0 || toolMap.has(providerId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownProviderKey\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Unknown provider key \"${providerId}\".`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction emitWarnings(\n\tdiagnostics: ToolConfigDiagnostic[],\n\tsource: string,\n): void {\n\tif (diagnostics.length === 0) return;\n\tfor (const diagnostic of diagnostics) {\n\t\tconst prefix = `[tool-config-validation:${source}]`;\n\t\tconsole.warn(`${prefix} ${diagnostic.path} - ${diagnostic.message}`);\n\t}\n}\n\nfunction throwValidationError(\n\tdiagnostics: ToolConfigDiagnostic[],\n\tsource: string,\n): never {\n\tconst formatted = diagnostics\n\t\t.map((diagnostic) => `- ${diagnostic.path}: ${diagnostic.message}`)\n\t\t.join(\"\\n\");\n\tthrow new Error(`[tool-config-validation:${source}] Invalid tools config:\\n${formatted}`);\n}\n\nexport function normalizeAndValidateToolsConfig(\n\tinput?: Partial<CanonicalToolsConfig> | null,\n\toptions: ToolConfigValidationOptions = {},\n): ToolConfigValidationResult {\n\tconst strictness = normalizeToolConfigStrictness(options.strictness);\n\tconst source = options.source ?? \"tools\";\n\tconst registryTools = getRegistryToolMap(\n\t\toptions.toolRegistry ?? createPackagedToolRegistry(),\n\t);\n\tconst normalized = normalizeToolsConfig(input);\n\tconst diagnostics: ToolConfigDiagnostic[] = [];\n\tconst hasDeprecatedTtsKey = Object.hasOwn(\n\t\tnormalized.providers,\n\t\t\"tts\",\n\t);\n\n\tconst nextProviders: CanonicalToolsConfig[\"providers\"] = {\n\t\t...(normalized.providers || {}),\n\t};\n\tfor (const providerId of Object.keys(nextProviders).sort()) {\n\t\tif (providerId === \"tts\") {\n\t\t\tdelete nextProviders.tts;\n\t\t\tcontinue;\n\t\t}\n\t\tconst tool = registryTools.get(providerId);\n\t\tnextProviders[providerId] = sanitizeProviderConfig(\n\t\t\tproviderId,\n\t\t\tnextProviders[providerId],\n\t\t\ttool,\n\t\t\tdiagnostics,\n\t\t);\n\t\tvalidateProviderConfig(providerId, nextProviders[providerId], tool, diagnostics);\n\t}\n\tconst nextConfig: CanonicalToolsConfig = {\n\t\t...normalized,\n\t\tproviders: nextProviders,\n\t};\n\n\tcollectPlacementDiagnostics(nextConfig, registryTools, diagnostics);\n\tcollectPolicyDiagnostics(nextConfig, registryTools, diagnostics);\n\tcollectProviderKeyDiagnostics(\n\t\tnextConfig,\n\t\tregistryTools,\n\t\thasDeprecatedTtsKey,\n\t\tdiagnostics,\n\t);\n\tif (hasDeprecatedTtsKey) {\n\t\tthrowValidationError(\n\t\t\tdiagnostics.filter(\n\t\t\t\t(entry) => entry.code === \"tools.deprecatedProviderKey\",\n\t\t\t),\n\t\t\tsource,\n\t\t);\n\t}\n\n\tif (strictness === \"warn\") {\n\t\temitWarnings(diagnostics, source);\n\t}\n\tif (strictness === \"error\" && diagnostics.length > 0) {\n\t\tthrowValidationError(diagnostics, source);\n\t}\n\n\treturn {\n\t\tconfig: nextConfig,\n\t\tdiagnostics,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool-config-validation.js","sourceRoot":"","sources":["../../src/services/tool-config-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tnormalizeToolsConfig,\n\ttype CanonicalToolsConfig,\n\ttype ToolPlacementLevel,\n\ttype ToolProviderConfig,\n\ttype TextToSpeechToolProviderConfig,\n} from \"./tools-config-normalizer.js\";\nimport {\n\tframeworkErrorFromToolConfigDiagnostics,\n\tframeworkErrorFromUnknown,\n\ttype FrameworkErrorModel,\n} from \"./framework-error.js\";\nimport type { ToolRegistration, ToolRegistry } from \"./ToolRegistry.js\";\nimport { createPackagedToolRegistry } from \"./createDefaultToolRegistry.js\";\n\nexport type ToolConfigStrictness = \"off\" | \"warn\" | \"error\";\n\nexport type ToolConfigDiagnosticSeverity = \"warning\" | \"error\";\n\nexport interface ToolConfigDiagnostic {\n\tcode:\n\t\t| \"tools.unknownToolId\"\n\t\t| \"tools.unsupportedLevel\"\n\t\t| \"tools.unknownProviderKey\"\n\t\t| \"tools.removedProviderKey\"\n\t\t| \"tools.providerSanitizeFailed\"\n\t\t| \"tools.providerValidateFailed\"\n\t\t| \"tools.invalidProviderValidation\";\n\tseverity: ToolConfigDiagnosticSeverity;\n\tpath: string;\n\tmessage: string;\n\ttoolId?: string;\n\tproviderId?: string;\n}\n\nexport interface ToolConfigValidationOptions {\n\tstrictness?: ToolConfigStrictness;\n\tsource?: string;\n\ttoolRegistry?: ToolRegistry | null;\n}\n\nexport interface ToolConfigValidationResult {\n\tconfig: CanonicalToolsConfig;\n\tdiagnostics: ToolConfigDiagnostic[];\n}\n\nexport function frameworkErrorFromToolConfigValidation(args: {\n\tsource: string;\n\tdiagnostics?: ToolConfigDiagnostic[] | null;\n\terror?: unknown;\n\trecoverable?: boolean;\n}): FrameworkErrorModel {\n\tif (Array.isArray(args.diagnostics) && args.diagnostics.length > 0) {\n\t\treturn frameworkErrorFromToolConfigDiagnostics({\n\t\t\tsource: args.source,\n\t\t\tdiagnostics: args.diagnostics,\n\t\t\trecoverable: args.recoverable,\n\t\t});\n\t}\n\treturn frameworkErrorFromUnknown({\n\t\tkind: \"tool-config\",\n\t\tsource: args.source,\n\t\terror: args.error ?? new Error(\"Invalid tools config\"),\n\t\trecoverable: args.recoverable,\n\t});\n}\n\nconst DEFAULT_STRICTNESS: ToolConfigStrictness = \"error\";\n\nexport function normalizeToolConfigStrictness(\n\tvalue: unknown,\n): ToolConfigStrictness {\n\tif (value === \"off\" || value === \"warn\" || value === \"error\") {\n\t\treturn value;\n\t}\n\treturn DEFAULT_STRICTNESS;\n}\n\nfunction createDiagnostic(\n\tdiagnostic: ToolConfigDiagnostic,\n): ToolConfigDiagnostic {\n\treturn diagnostic;\n}\n\nfunction getRegistryToolMap(\n\ttoolRegistry: ToolRegistry | null | undefined,\n): Map<string, ToolRegistration> {\n\tif (!toolRegistry) return new Map();\n\treturn new Map(\n\t\ttoolRegistry\n\t\t\t.getAllTools()\n\t\t\t.slice()\n\t\t\t.sort((left, right) => left.toolId.localeCompare(right.toolId))\n\t\t\t.map((tool) => [tool.toolId, tool]),\n\t);\n}\n\nfunction sanitizeProviderConfig(\n\tproviderId: string,\n\tproviderConfig: ToolProviderConfig | TextToSpeechToolProviderConfig | undefined,\n\ttool: ToolRegistration | undefined,\n\tdiagnostics: ToolConfigDiagnostic[],\n): ToolProviderConfig | TextToSpeechToolProviderConfig | undefined {\n\tif (!providerConfig || !tool?.provider?.sanitizeConfig) return providerConfig;\n\ttry {\n\t\tconst sanitized = tool.provider.sanitizeConfig(\n\t\t\tproviderConfig as ToolProviderConfig,\n\t\t);\n\t\tif (!sanitized || typeof sanitized !== \"object\" || Array.isArray(sanitized)) {\n\t\t\tdiagnostics.push(\n\t\t\t\tcreateDiagnostic({\n\t\t\t\t\tcode: \"tools.providerSanitizeFailed\",\n\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\t\tmessage: `Provider sanitizer for \"${providerId}\" must return an object.`,\n\t\t\t\t\tproviderId,\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn providerConfig;\n\t\t}\n\t\treturn sanitized as ToolProviderConfig | TextToSpeechToolProviderConfig;\n\t} catch (error) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.providerSanitizeFailed\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Provider sanitizer failed for \"${providerId}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t\treturn providerConfig;\n\t}\n}\n\nfunction validateProviderConfig(\n\tproviderId: string,\n\tproviderConfig: ToolProviderConfig | TextToSpeechToolProviderConfig | undefined,\n\ttool: ToolRegistration | undefined,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (!providerConfig || !tool?.provider?.validateConfig) return;\n\ttry {\n\t\tconst providerDiagnostics = tool.provider.validateConfig(\n\t\t\tproviderConfig as ToolProviderConfig,\n\t\t);\n\t\tif (!Array.isArray(providerDiagnostics)) {\n\t\t\tdiagnostics.push(\n\t\t\t\tcreateDiagnostic({\n\t\t\t\t\tcode: \"tools.invalidProviderValidation\",\n\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\t\tmessage: `Provider validator for \"${providerId}\" must return an array.`,\n\t\t\t\t\tproviderId,\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tfor (const diagnostic of providerDiagnostics) {\n\t\t\tdiagnostics.push({\n\t\t\t\t...diagnostic,\n\t\t\t\tpath: diagnostic.path || `providers.${providerId}`,\n\t\t\t\tproviderId: diagnostic.providerId || providerId,\n\t\t\t\ttoolId: diagnostic.toolId || providerId,\n\t\t\t});\n\t\t}\n\t} catch (error) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.providerValidateFailed\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Provider validator failed for \"${providerId}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction collectPlacementDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (toolMap.size === 0) return;\n\tconst levelOrder: ToolPlacementLevel[] = [\"section\", \"item\", \"passage\"];\n\tfor (const level of levelOrder) {\n\t\tfor (const toolId of config.placement[level]) {\n\t\t\tconst tool = toolMap.get(toolId);\n\t\t\tif (!tool) {\n\t\t\t\tdiagnostics.push(\n\t\t\t\t\tcreateDiagnostic({\n\t\t\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tpath: `placement.${level}`,\n\t\t\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in placement.${level}.`,\n\t\t\t\t\t\ttoolId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst supportsLevel = tool.supportedLevels.includes(level);\n\t\t\t// Leniency policy: section-capable tools are tolerated in item placement.\n\t\t\t// This avoids strict failures for common host config patterns while still\n\t\t\t// enforcing upward-invalid placements (for example item-only -> section).\n\t\t\tconst allowSectionToolInItemPlacement =\n\t\t\t\tlevel === \"item\" && tool.supportedLevels.includes(\"section\");\n\t\t\tif (!supportsLevel && !allowSectionToolInItemPlacement) {\n\t\t\t\tdiagnostics.push(\n\t\t\t\t\tcreateDiagnostic({\n\t\t\t\t\t\tcode: \"tools.unsupportedLevel\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tpath: `placement.${level}`,\n\t\t\t\t\t\tmessage: `Tool \"${toolId}\" does not support level \"${level}\".`,\n\t\t\t\t\t\ttoolId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction collectPolicyDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (toolMap.size === 0) return;\n\tfor (const toolId of config.policy.allowed || []) {\n\t\tif (toolMap.has(toolId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"policy.allowed\",\n\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in policy.allowed.`,\n\t\t\t\ttoolId,\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const toolId of config.policy.blocked || []) {\n\t\tif (toolMap.has(toolId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"policy.blocked\",\n\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in policy.blocked.`,\n\t\t\t\ttoolId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction collectProviderKeyDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\thasRemovedTtsKey: boolean,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (hasRemovedTtsKey) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.removedProviderKey\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"providers.tts\",\n\t\t\t\tmessage:\n\t\t\t\t\t`Provider key \"tts\" is no longer supported. Use \"providers.textToSpeech\".`,\n\t\t\t\tproviderId: \"tts\",\n\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const providerId of Object.keys(config.providers).sort()) {\n\t\tif (toolMap.size === 0 || toolMap.has(providerId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownProviderKey\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Unknown provider key \"${providerId}\".`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction emitWarnings(\n\tdiagnostics: ToolConfigDiagnostic[],\n\tsource: string,\n): void {\n\tif (diagnostics.length === 0) return;\n\tfor (const diagnostic of diagnostics) {\n\t\tconst prefix = `[tool-config-validation:${source}]`;\n\t\tconsole.warn(`${prefix} ${diagnostic.path} - ${diagnostic.message}`);\n\t}\n}\n\nfunction throwValidationError(\n\tdiagnostics: ToolConfigDiagnostic[],\n\tsource: string,\n): never {\n\tconst formatted = diagnostics\n\t\t.map((diagnostic) => `- ${diagnostic.path}: ${diagnostic.message}`)\n\t\t.join(\"\\n\");\n\tthrow new Error(`[tool-config-validation:${source}] Invalid tools config:\\n${formatted}`);\n}\n\nexport function normalizeAndValidateToolsConfig(\n\tinput?: Partial<CanonicalToolsConfig> | null,\n\toptions: ToolConfigValidationOptions = {},\n): ToolConfigValidationResult {\n\tconst strictness = normalizeToolConfigStrictness(options.strictness);\n\tconst source = options.source ?? \"tools\";\n\tconst registryTools = getRegistryToolMap(\n\t\toptions.toolRegistry ?? createPackagedToolRegistry(),\n\t);\n\tconst normalized = normalizeToolsConfig(input);\n\tconst diagnostics: ToolConfigDiagnostic[] = [];\n\tconst hasRemovedTtsKey = Object.hasOwn(\n\t\tnormalized.providers,\n\t\t\"tts\",\n\t);\n\n\tconst nextProviders: CanonicalToolsConfig[\"providers\"] = {\n\t\t...(normalized.providers || {}),\n\t};\n\tfor (const providerId of Object.keys(nextProviders).sort()) {\n\t\tif (providerId === \"tts\") {\n\t\t\tdelete nextProviders.tts;\n\t\t\tcontinue;\n\t\t}\n\t\tconst tool = registryTools.get(providerId);\n\t\tnextProviders[providerId] = sanitizeProviderConfig(\n\t\t\tproviderId,\n\t\t\tnextProviders[providerId],\n\t\t\ttool,\n\t\t\tdiagnostics,\n\t\t);\n\t\tvalidateProviderConfig(providerId, nextProviders[providerId], tool, diagnostics);\n\t}\n\tconst nextConfig: CanonicalToolsConfig = {\n\t\t...normalized,\n\t\tproviders: nextProviders,\n\t};\n\n\tcollectPlacementDiagnostics(nextConfig, registryTools, diagnostics);\n\tcollectPolicyDiagnostics(nextConfig, registryTools, diagnostics);\n\tcollectProviderKeyDiagnostics(\n\t\tnextConfig,\n\t\tregistryTools,\n\t\thasRemovedTtsKey,\n\t\tdiagnostics,\n\t);\n\tif (hasRemovedTtsKey) {\n\t\tthrowValidationError(\n\t\t\tdiagnostics.filter(\n\t\t\t\t(entry) => entry.code === \"tools.removedProviderKey\",\n\t\t\t),\n\t\t\tsource,\n\t\t);\n\t}\n\n\tif (strictness === \"warn\") {\n\t\temitWarnings(diagnostics, source);\n\t}\n\tif (strictness === \"error\" && diagnostics.length > 0) {\n\t\tthrowValidationError(diagnostics, source);\n\t}\n\n\treturn {\n\t\tconfig: nextConfig,\n\t\tdiagnostics,\n\t};\n}\n"]}
|
|
@@ -32,7 +32,7 @@ export function createScopedToolId(baseToolId, scopeLevel, scopeId) {
|
|
|
32
32
|
}
|
|
33
33
|
export function parseScopedToolId(id) {
|
|
34
34
|
const parts = id.split(":");
|
|
35
|
-
if (parts.length !== 3
|
|
35
|
+
if (parts.length !== 3)
|
|
36
36
|
return null;
|
|
37
37
|
const [baseToolId, scopeLevelRaw, scopeId] = parts;
|
|
38
38
|
if (!baseToolId || !scopeId)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-instance-id.js","sourceRoot":"","sources":["../../src/services/tool-instance-id.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,YAAY;IACZ,SAAS;IACT,MAAM;IACN,SAAS;IACT,QAAQ;CACC,CAAC;AAWX,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS,yBAAyB,CAAC,CAAC;AAE7E,MAAM,UAAU,4BAA4B;IAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACxD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAChE,CAAC;IACD,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,0BAA0B,CACzC,UAAkB;IAElB,OAAO,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,kBAAkB,CACjC,UAAkB,EAClB,UAA0B,EAC1B,OAAe;IAEf,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,cAAc,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACd,6BAA6B,UAAU,0DAA0D,CACjG,CAAC;IACH,CAAC;IACD,OAAO,GAAG,cAAc,IAAI,UAAU,IAAI,iBAAiB,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC3C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC
|
|
1
|
+
{"version":3,"file":"tool-instance-id.js","sourceRoot":"","sources":["../../src/services/tool-instance-id.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,YAAY;IACZ,SAAS;IACT,MAAM;IACN,SAAS;IACT,QAAQ;CACC,CAAC;AAWX,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS,yBAAyB,CAAC,CAAC;AAE7E,MAAM,UAAU,4BAA4B;IAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACxD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAChE,CAAC;IACD,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,0BAA0B,CACzC,UAAkB;IAElB,OAAO,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,kBAAkB,CACjC,UAAkB,EAClB,UAA0B,EAC1B,OAAe;IAEf,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,cAAc,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACd,6BAA6B,UAAU,0DAA0D,CACjG,CAAC;IACH,CAAC;IACD,OAAO,GAAG,cAAc,IAAI,UAAU,IAAI,iBAAiB,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC3C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;IACnD,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,CAAC,0BAA0B,CAAC,aAAa,CAAC,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO;QACN,UAAU;QACV,UAAU,EAAE,aAAa;QACzB,OAAO;KACP,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,OAAO,kBAAkB,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;AACjF,CAAC","sourcesContent":["export const DEFAULT_TOOL_SCOPE_LEVELS = [\n\t\"assessment\",\n\t\"section\",\n\t\"item\",\n\t\"passage\",\n\t\"rubric\",\n] as const;\n\nexport type BuiltinToolScopeLevel = (typeof DEFAULT_TOOL_SCOPE_LEVELS)[number];\nexport type ToolScopeLevel = BuiltinToolScopeLevel | (string & {});\n\nexport interface ParsedToolInstanceId {\n\tbaseToolId: string;\n\tscopeLevel: ToolScopeLevel;\n\tscopeId: string;\n}\n\nconst registeredToolScopeLevels = new Set<string>(DEFAULT_TOOL_SCOPE_LEVELS);\n\nexport function getRegisteredToolScopeLevels(): string[] {\n\treturn Array.from(registeredToolScopeLevels.values());\n}\n\nexport function registerToolScopeLevel(scopeLevel: string): void {\n\tconst normalized = scopeLevel.trim();\n\tif (!normalized) {\n\t\tthrow new Error(\"Tool scope level must be a non-empty string\");\n\t}\n\tregisteredToolScopeLevels.add(normalized);\n}\n\nexport function isRegisteredToolScopeLevel(\n\tscopeLevel: string,\n): scopeLevel is ToolScopeLevel {\n\treturn registeredToolScopeLevels.has(scopeLevel);\n}\n\nexport function createScopedToolId(\n\tbaseToolId: string,\n\tscopeLevel: ToolScopeLevel,\n\tscopeId: string,\n): string {\n\tconst normalizedBase = baseToolId.trim();\n\tconst normalizedScopeId = scopeId.trim();\n\tif (!normalizedBase || !normalizedScopeId) {\n\t\tthrow new Error(\"Tool instance ids require non-empty tool and scope ids\");\n\t}\n\tif (!isRegisteredToolScopeLevel(scopeLevel)) {\n\t\tthrow new Error(\n\t\t\t`Unknown tool scope level '${scopeLevel}'. Register custom levels with registerToolScopeLevel().`,\n\t\t);\n\t}\n\treturn `${normalizedBase}:${scopeLevel}:${normalizedScopeId}`;\n}\n\nexport function parseScopedToolId(id: string): ParsedToolInstanceId | null {\n\tconst parts = id.split(\":\");\n\tif (parts.length !== 3) return null;\n\tconst [baseToolId, scopeLevelRaw, scopeId] = parts;\n\tif (!baseToolId || !scopeId) return null;\n\tif (!isRegisteredToolScopeLevel(scopeLevelRaw)) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tbaseToolId,\n\t\tscopeLevel: scopeLevelRaw,\n\t\tscopeId,\n\t};\n}\n\nexport function toOverlayToolId(id: string): string {\n\tconst parsed = parseScopedToolId(id);\n\tif (!parsed) return id;\n\treturn createScopedToolId(parsed.baseToolId, parsed.scopeLevel, parsed.scopeId);\n}\n"]}
|
|
@@ -106,7 +106,7 @@ export class TTSToolProvider {
|
|
|
106
106
|
// Server backends are optional; load implementation only when requested.
|
|
107
107
|
const moduleLoadStartedAt = Date.now();
|
|
108
108
|
await this.emitTelemetry("pie-tool-library-load-start", {
|
|
109
|
-
toolId: "
|
|
109
|
+
toolId: "textToSpeech",
|
|
110
110
|
operation: "server-provider-module-import",
|
|
111
111
|
backend: config.serverProvider || config.backend,
|
|
112
112
|
});
|
|
@@ -114,7 +114,7 @@ export class TTSToolProvider {
|
|
|
114
114
|
try {
|
|
115
115
|
const loaded = (await import("@pie-players/tts-client-server"));
|
|
116
116
|
await this.emitTelemetry("pie-tool-library-load-success", {
|
|
117
|
-
toolId: "
|
|
117
|
+
toolId: "textToSpeech",
|
|
118
118
|
operation: "server-provider-module-import",
|
|
119
119
|
backend: config.serverProvider || config.backend,
|
|
120
120
|
duration: Date.now() - moduleLoadStartedAt,
|
|
@@ -123,7 +123,7 @@ export class TTSToolProvider {
|
|
|
123
123
|
}
|
|
124
124
|
catch (error) {
|
|
125
125
|
await this.emitTelemetry("pie-tool-library-load-error", {
|
|
126
|
-
toolId: "
|
|
126
|
+
toolId: "textToSpeech",
|
|
127
127
|
operation: "server-provider-module-import",
|
|
128
128
|
backend: config.serverProvider || config.backend,
|
|
129
129
|
duration: Date.now() - moduleLoadStartedAt,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TTSToolProvider.js","sourceRoot":"","sources":["../../../src/services/tool-providers/TTSToolProvider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TTS Tool Provider\n *\n * Unified provider for all TTS backends:\n * - Browser Web Speech API (no auth)\n * - AWS Polly (requires auth via server proxy)\n * - Google Cloud TTS (requires auth via server proxy)\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { ITTSProvider, TTSConfig } from \"@pie-players/pie-tts\";\nimport { BrowserTTSProvider } from \"../../services/tts/browser-provider.js\";\nimport type { ToolProviderApi, ToolProviderCapabilities } from \"./ToolProviderApi.js\";\n\n/**\n * TTS backend type\n */\nexport type TTSBackend =\n\t| \"browser\"\n\t| \"polly\"\n\t| \"google\"\n\t| \"server\";\n\n/**\n * TTS tool provider configuration\n */\nexport interface TTSToolProviderConfig extends Partial<TTSConfig> {\n\t/**\n\t * TTS backend to use\n\t */\n\tbackend: TTSBackend;\n\n\t/**\n\t * Server API endpoint (for server backends)\n\t * @example '/api/tts/synthesize'\n\t * @example 'https://api.example.com/tts/synthesize'\n\t */\n\tapiEndpoint?: string;\n\n\t/**\n\t * Provider to use on server ('polly', 'google')\n\t * Only used when backend is 'server', 'polly', or 'google'\n\t */\n\tserverProvider?: \"polly\" | \"google\" | \"custom\";\n\n\t/**\n\t * Explicit transport mode for server provider payload translation.\n\t */\n\ttransportMode?: \"pie\" | \"custom\";\n\n\t/**\n\t * Endpoint style used by the server-backed provider.\n\t */\n\tendpointMode?: \"synthesizePath\" | \"rootPost\";\n\n\t/**\n\t * Endpoint validation strategy when validateEndpoint=true.\n\t */\n\tendpointValidationMode?: \"voices\" | \"endpoint\" | \"none\";\n\n\t/**\n\t * Include auth header when fetching remote audio/speech-mark assets.\n\t */\n\tincludeAuthOnAssetFetch?: boolean;\n\n\t/**\n\t * Auth token (if required)\n\t * Typically fetched via authFetcher in ToolProviderRegistry\n\t */\n\tauthToken?: string;\n\n\t/**\n\t * Organization ID for multi-tenant applications\n\t */\n\torganizationId?: string;\n\n\t/**\n\t * Default voice to use\n\t */\n\tvoice?: string;\n\n\t/**\n\t * Speech rate (0.25 to 4.0, default 1.0)\n\t */\n\trate?: number;\n\n\t/**\n\t * Speech pitch (0 to 2, default 1.0)\n\t * Note: Only browser backend supports pitch\n\t */\n\tpitch?: number;\n\n\t/**\n\t * Optional telemetry callback for tool/backend instrumentation.\n\t */\n\tonTelemetry?: (\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t) => void | Promise<void>;\n}\n\n/**\n * TTS Tool Provider\n *\n * Wraps TTS providers (Browser, Polly, Google) with the ToolProviderApi interface\n * for use in the ToolProviderRegistry.\n *\n * @example Browser TTS (no auth)\n * ```typescript\n * const provider = new TTSToolProvider();\n * await provider.initialize({ backend: 'browser' });\n * const ttsProvider = await provider.createInstance();\n * ```\n *\n * @example Server TTS (with auth)\n * ```typescript\n * const provider = new TTSToolProvider();\n * await provider.initialize({\n * backend: 'polly',\n * apiEndpoint: '/api/tts/synthesize',\n * authToken: 'bearer-token', // Fetched via authFetcher\n * });\n * const ttsProvider = await provider.createInstance();\n * ```\n */\nexport class TTSToolProvider\n\timplements ToolProviderApi<TTSToolProviderConfig, ITTSProvider>\n{\n\treadonly providerId = \"tts-service\";\n\treadonly providerName = \"Text-to-Speech\";\n\treadonly category = \"tts\" as const;\n\treadonly version = \"1.0\";\n\treadonly requiresAuth: boolean;\n\n\tprivate ttsProvider: ITTSProvider | null = null;\n\tprivate config: TTSToolProviderConfig | null = null;\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.config?.onTelemetry?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[TTSToolProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Create TTS tool provider\n\t *\n\t * @param backend TTS backend to use (default: 'browser')\n\t */\n\tconstructor(backend: TTSBackend = \"browser\") {\n\t\tthis.requiresAuth = backend !== \"browser\";\n\t}\n\n\t/**\n\t * Initialize TTS provider\n\t *\n\t * Sets up the appropriate TTS backend.\n\t *\n\t * @param config Configuration with backend type and credentials\n\t * @throws Error if initialization fails or required config missing\n\t */\n\tasync initialize(config: TTSToolProviderConfig): Promise<void> {\n\t\tif (this.ttsProvider) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[TTSToolProvider] Already initialized, skipping reinitialization\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.config = config;\n\n\t\tswitch (config.backend) {\n\t\t\tcase \"browser\":\n\t\t\t\tawait this._initializeBrowserTTS(config);\n\t\t\t\tbreak;\n\n\t\t\tcase \"polly\":\n\t\t\tcase \"google\":\n\t\t\tcase \"server\":\n\t\t\t\tawait this._initializeServerTTS(config);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`[TTSToolProvider] Unknown backend: ${config.backend}`);\n\t\t}\n\n\t\tconsole.log(\n\t\t\t`[TTSToolProvider] Initialized successfully (backend: ${config.backend})`,\n\t\t);\n\t}\n\n\t/**\n\t * Initialize browser TTS (Web Speech API)\n\t */\n\tprivate async _initializeBrowserTTS(\n\t\tconfig: TTSToolProviderConfig,\n\t): Promise<void> {\n\t\t// Check if Web Speech API is available\n\t\tif (typeof window === \"undefined\" || !(\"speechSynthesis\" in window)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] Browser TTS not supported (Web Speech API not available)\",\n\t\t\t);\n\t\t}\n\n\t\tthis.ttsProvider = new BrowserTTSProvider();\n\t\tconsole.log(\"[TTSToolProvider] Browser TTS initialized (Web Speech API)\");\n\t}\n\n\t/**\n\t * Initialize server-based TTS (Polly, Google)\n\t */\n\tprivate async _initializeServerTTS(\n\t\tconfig: TTSToolProviderConfig,\n\t): Promise<void> {\n\t\tif (!config.apiEndpoint) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] apiEndpoint required for server-based TTS backends\",\n\t\t\t);\n\t\t}\n\n\t\t// Server backends are optional; load implementation only when requested.\n\t\tconst moduleLoadStartedAt = Date.now();\n\t\tawait this.emitTelemetry(\"pie-tool-library-load-start\", {\n\t\t\ttoolId: \"tts\",\n\t\t\toperation: \"server-provider-module-import\",\n\t\t\tbackend: config.serverProvider || config.backend,\n\t\t});\n\t\tconst serverModule = await (async () => {\n\t\t\ttry {\n\t\t\t\tconst loaded = (await import(\"@pie-players/tts-client-server\")) as {\n\t\t\t\t\tServerTTSProvider: new () => ITTSProvider;\n\t\t\t\t};\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-success\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\toperation: \"server-provider-module-import\",\n\t\t\t\t\tbackend: config.serverProvider || config.backend,\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t});\n\t\t\t\treturn loaded;\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\toperation: \"server-provider-module-import\",\n\t\t\t\t\tbackend: config.serverProvider || config.backend,\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t\terrorType: \"ToolLibraryLoadError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\t\tthis.ttsProvider = new serverModule.ServerTTSProvider();\n\n\t\tconsole.log(\n\t\t\t`[TTSToolProvider] Server TTS initialized (provider: ${config.serverProvider || config.backend})`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a TTS provider instance\n\t *\n\t * Returns the initialized TTS provider.\n\t *\n\t * @param config Optional instance-specific configuration (currently unused)\n\t * @returns TTS provider\n\t * @throws Error if provider not initialized\n\t */\n\tasync createInstance(\n\t\tconfig?: Partial<TTSToolProviderConfig>,\n\t): Promise<ITTSProvider> {\n\t\tif (!this.ttsProvider) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] Provider not initialized. Call initialize() first.\",\n\t\t\t);\n\t\t}\n\n\t\treturn this.ttsProvider;\n\t}\n\n\t/**\n\t * Get provider capabilities\n\t *\n\t * @returns TTS capabilities based on backend\n\t */\n\tgetCapabilities(): ToolProviderCapabilities {\n\t\tconst isBrowser = this.config?.backend === \"browser\";\n\n\t\treturn {\n\t\t\tsupportsOffline: isBrowser,\n\t\t\trequiresAuth: !isBrowser,\n\t\t\tmaxInstances: 1, // Single TTS instance (playback is sequential)\n\t\t\tfeatures: {\n\t\t\t\twordBoundary: true, // All backends support word highlighting\n\t\t\t\tpause: true,\n\t\t\t\tresume: true,\n\t\t\t\trateControl: true,\n\t\t\t\tpitchControl: isBrowser, // Only browser supports pitch\n\t\t\t\tvoiceSelection: true,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Check if provider is ready\n\t *\n\t * @returns true if provider is initialized\n\t */\n\tisReady(): boolean {\n\t\treturn this.ttsProvider !== null;\n\t}\n\n\t/**\n\t * Clean up provider resources\n\t *\n\t * Destroys the TTS provider and releases resources.\n\t */\n\tdestroy(): void {\n\t\tif (this.ttsProvider) {\n\t\t\tthis.ttsProvider.destroy();\n\t\t\tthis.ttsProvider = null;\n\t\t}\n\t\tthis.config = null;\n\t\tconsole.log(\"[TTSToolProvider] Destroyed\");\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"TTSToolProvider.js","sourceRoot":"","sources":["../../../src/services/tool-providers/TTSToolProvider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TTS Tool Provider\n *\n * Unified provider for all TTS backends:\n * - Browser Web Speech API (no auth)\n * - AWS Polly (requires auth via server proxy)\n * - Google Cloud TTS (requires auth via server proxy)\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { ITTSProvider, TTSConfig } from \"@pie-players/pie-tts\";\nimport { BrowserTTSProvider } from \"../../services/tts/browser-provider.js\";\nimport type { ToolProviderApi, ToolProviderCapabilities } from \"./ToolProviderApi.js\";\n\n/**\n * TTS backend type\n */\nexport type TTSBackend =\n\t| \"browser\"\n\t| \"polly\"\n\t| \"google\"\n\t| \"server\";\n\n/**\n * TTS tool provider configuration\n */\nexport interface TTSToolProviderConfig extends Partial<TTSConfig> {\n\t/**\n\t * TTS backend to use\n\t */\n\tbackend: TTSBackend;\n\n\t/**\n\t * Server API endpoint (for server backends)\n\t * @example '/api/tts/synthesize'\n\t * @example 'https://api.example.com/tts/synthesize'\n\t */\n\tapiEndpoint?: string;\n\n\t/**\n\t * Provider to use on server ('polly', 'google')\n\t * Only used when backend is 'server', 'polly', or 'google'\n\t */\n\tserverProvider?: \"polly\" | \"google\" | \"custom\";\n\n\t/**\n\t * Explicit transport mode for server provider payload translation.\n\t */\n\ttransportMode?: \"pie\" | \"custom\";\n\n\t/**\n\t * Endpoint style used by the server-backed provider.\n\t */\n\tendpointMode?: \"synthesizePath\" | \"rootPost\";\n\n\t/**\n\t * Endpoint validation strategy when validateEndpoint=true.\n\t */\n\tendpointValidationMode?: \"voices\" | \"endpoint\" | \"none\";\n\n\t/**\n\t * Include auth header when fetching remote audio/speech-mark assets.\n\t */\n\tincludeAuthOnAssetFetch?: boolean;\n\n\t/**\n\t * Auth token (if required)\n\t * Typically fetched via authFetcher in ToolProviderRegistry\n\t */\n\tauthToken?: string;\n\n\t/**\n\t * Organization ID for multi-tenant applications\n\t */\n\torganizationId?: string;\n\n\t/**\n\t * Default voice to use\n\t */\n\tvoice?: string;\n\n\t/**\n\t * Speech rate (0.25 to 4.0, default 1.0)\n\t */\n\trate?: number;\n\n\t/**\n\t * Speech pitch (0 to 2, default 1.0)\n\t * Note: Only browser backend supports pitch\n\t */\n\tpitch?: number;\n\n\t/**\n\t * Optional telemetry callback for tool/backend instrumentation.\n\t */\n\tonTelemetry?: (\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t) => void | Promise<void>;\n}\n\n/**\n * TTS Tool Provider\n *\n * Wraps TTS providers (Browser, Polly, Google) with the ToolProviderApi interface\n * for use in the ToolProviderRegistry.\n *\n * @example Browser TTS (no auth)\n * ```typescript\n * const provider = new TTSToolProvider();\n * await provider.initialize({ backend: 'browser' });\n * const ttsProvider = await provider.createInstance();\n * ```\n *\n * @example Server TTS (with auth)\n * ```typescript\n * const provider = new TTSToolProvider();\n * await provider.initialize({\n * backend: 'polly',\n * apiEndpoint: '/api/tts/synthesize',\n * authToken: 'bearer-token', // Fetched via authFetcher\n * });\n * const ttsProvider = await provider.createInstance();\n * ```\n */\nexport class TTSToolProvider\n\timplements ToolProviderApi<TTSToolProviderConfig, ITTSProvider>\n{\n\treadonly providerId = \"tts-service\";\n\treadonly providerName = \"Text-to-Speech\";\n\treadonly category = \"tts\" as const;\n\treadonly version = \"1.0\";\n\treadonly requiresAuth: boolean;\n\n\tprivate ttsProvider: ITTSProvider | null = null;\n\tprivate config: TTSToolProviderConfig | null = null;\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.config?.onTelemetry?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[TTSToolProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Create TTS tool provider\n\t *\n\t * @param backend TTS backend to use (default: 'browser')\n\t */\n\tconstructor(backend: TTSBackend = \"browser\") {\n\t\tthis.requiresAuth = backend !== \"browser\";\n\t}\n\n\t/**\n\t * Initialize TTS provider\n\t *\n\t * Sets up the appropriate TTS backend.\n\t *\n\t * @param config Configuration with backend type and credentials\n\t * @throws Error if initialization fails or required config missing\n\t */\n\tasync initialize(config: TTSToolProviderConfig): Promise<void> {\n\t\tif (this.ttsProvider) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[TTSToolProvider] Already initialized, skipping reinitialization\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.config = config;\n\n\t\tswitch (config.backend) {\n\t\t\tcase \"browser\":\n\t\t\t\tawait this._initializeBrowserTTS(config);\n\t\t\t\tbreak;\n\n\t\t\tcase \"polly\":\n\t\t\tcase \"google\":\n\t\t\tcase \"server\":\n\t\t\t\tawait this._initializeServerTTS(config);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`[TTSToolProvider] Unknown backend: ${config.backend}`);\n\t\t}\n\n\t\tconsole.log(\n\t\t\t`[TTSToolProvider] Initialized successfully (backend: ${config.backend})`,\n\t\t);\n\t}\n\n\t/**\n\t * Initialize browser TTS (Web Speech API)\n\t */\n\tprivate async _initializeBrowserTTS(\n\t\tconfig: TTSToolProviderConfig,\n\t): Promise<void> {\n\t\t// Check if Web Speech API is available\n\t\tif (typeof window === \"undefined\" || !(\"speechSynthesis\" in window)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] Browser TTS not supported (Web Speech API not available)\",\n\t\t\t);\n\t\t}\n\n\t\tthis.ttsProvider = new BrowserTTSProvider();\n\t\tconsole.log(\"[TTSToolProvider] Browser TTS initialized (Web Speech API)\");\n\t}\n\n\t/**\n\t * Initialize server-based TTS (Polly, Google)\n\t */\n\tprivate async _initializeServerTTS(\n\t\tconfig: TTSToolProviderConfig,\n\t): Promise<void> {\n\t\tif (!config.apiEndpoint) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] apiEndpoint required for server-based TTS backends\",\n\t\t\t);\n\t\t}\n\n\t\t// Server backends are optional; load implementation only when requested.\n\t\tconst moduleLoadStartedAt = Date.now();\n\t\tawait this.emitTelemetry(\"pie-tool-library-load-start\", {\n\t\t\ttoolId: \"textToSpeech\",\n\t\t\toperation: \"server-provider-module-import\",\n\t\t\tbackend: config.serverProvider || config.backend,\n\t\t});\n\t\tconst serverModule = await (async () => {\n\t\t\ttry {\n\t\t\t\tconst loaded = (await import(\"@pie-players/tts-client-server\")) as {\n\t\t\t\t\tServerTTSProvider: new () => ITTSProvider;\n\t\t\t\t};\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-success\", {\n\t\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t\t\toperation: \"server-provider-module-import\",\n\t\t\t\t\tbackend: config.serverProvider || config.backend,\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t});\n\t\t\t\treturn loaded;\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-error\", {\n\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t\t\toperation: \"server-provider-module-import\",\n\t\t\t\t\tbackend: config.serverProvider || config.backend,\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t\terrorType: \"ToolLibraryLoadError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\t\tthis.ttsProvider = new serverModule.ServerTTSProvider();\n\n\t\tconsole.log(\n\t\t\t`[TTSToolProvider] Server TTS initialized (provider: ${config.serverProvider || config.backend})`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a TTS provider instance\n\t *\n\t * Returns the initialized TTS provider.\n\t *\n\t * @param config Optional instance-specific configuration (currently unused)\n\t * @returns TTS provider\n\t * @throws Error if provider not initialized\n\t */\n\tasync createInstance(\n\t\tconfig?: Partial<TTSToolProviderConfig>,\n\t): Promise<ITTSProvider> {\n\t\tif (!this.ttsProvider) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] Provider not initialized. Call initialize() first.\",\n\t\t\t);\n\t\t}\n\n\t\treturn this.ttsProvider;\n\t}\n\n\t/**\n\t * Get provider capabilities\n\t *\n\t * @returns TTS capabilities based on backend\n\t */\n\tgetCapabilities(): ToolProviderCapabilities {\n\t\tconst isBrowser = this.config?.backend === \"browser\";\n\n\t\treturn {\n\t\t\tsupportsOffline: isBrowser,\n\t\t\trequiresAuth: !isBrowser,\n\t\t\tmaxInstances: 1, // Single TTS instance (playback is sequential)\n\t\t\tfeatures: {\n\t\t\t\twordBoundary: true, // All backends support word highlighting\n\t\t\t\tpause: true,\n\t\t\t\tresume: true,\n\t\t\t\trateControl: true,\n\t\t\t\tpitchControl: isBrowser, // Only browser supports pitch\n\t\t\t\tvoiceSelection: true,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Check if provider is ready\n\t *\n\t * @returns true if provider is initialized\n\t */\n\tisReady(): boolean {\n\t\treturn this.ttsProvider !== null;\n\t}\n\n\t/**\n\t * Clean up provider resources\n\t *\n\t * Destroys the TTS provider and releases resources.\n\t */\n\tdestroy(): void {\n\t\tif (this.ttsProvider) {\n\t\t\tthis.ttsProvider.destroy();\n\t\t\tthis.ttsProvider = null;\n\t\t}\n\t\tthis.config = null;\n\t\tconsole.log(\"[TTSToolProvider] Destroyed\");\n\t}\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SREMathSpeechOptions } from "./tts/math-speech.js";
|
|
1
2
|
export type ToolPlacementLevel = "section" | "item" | "passage";
|
|
2
3
|
export interface ToolPolicyConfig {
|
|
3
4
|
allowed?: string[];
|
|
@@ -41,14 +42,25 @@ export interface ToolRuntimeProviderConfig {
|
|
|
41
42
|
init?: Record<string, unknown>;
|
|
42
43
|
runtime?: ToolRuntimeProviderBridge;
|
|
43
44
|
}
|
|
45
|
+
export type TextToSpeechRuntimeProviderId = "polly" | "google" | "custom";
|
|
44
46
|
export interface ToolProviderConfig {
|
|
45
47
|
enabled?: boolean;
|
|
46
48
|
provider?: ToolRuntimeProviderConfig;
|
|
47
49
|
settings?: Record<string, unknown>;
|
|
48
50
|
[key: string]: unknown;
|
|
49
51
|
}
|
|
52
|
+
export interface TextToSpeechToolProviderSettings extends Record<string, unknown> {
|
|
53
|
+
mathSpeech?: SREMathSpeechOptions;
|
|
54
|
+
}
|
|
55
|
+
export interface TextToSpeechToolProviderConfig extends Omit<ToolProviderConfig, "provider" | "settings"> {
|
|
56
|
+
provider?: ToolRuntimeProviderConfig | TextToSpeechRuntimeProviderId;
|
|
57
|
+
serverProvider?: TextToSpeechRuntimeProviderId;
|
|
58
|
+
mathSpeech?: SREMathSpeechOptions;
|
|
59
|
+
settings?: TextToSpeechToolProviderSettings;
|
|
60
|
+
}
|
|
50
61
|
export interface ToolProvidersConfig {
|
|
51
|
-
|
|
62
|
+
textToSpeech?: TextToSpeechToolProviderConfig;
|
|
63
|
+
[key: string]: ToolProviderConfig | TextToSpeechToolProviderConfig | undefined;
|
|
52
64
|
}
|
|
53
65
|
/**
|
|
54
66
|
* PNP/profile enforcement mode mirrored on `runtime.tools.pnpEnforcement`
|
|
@@ -81,4 +93,3 @@ export declare function normalizeToolAlias(toolId: string): string;
|
|
|
81
93
|
export declare function normalizeToolList(toolIds: string[] | undefined | null): string[];
|
|
82
94
|
export declare function parseToolList(input: string | undefined | null): string[];
|
|
83
95
|
export declare function normalizeToolsConfig(input?: Partial<CanonicalToolsConfig> | null): CanonicalToolsConfig;
|
|
84
|
-
export declare function resolveToolsForLevel(config: CanonicalToolsConfig, level: ToolPlacementLevel): string[];
|
|
@@ -42,6 +42,7 @@ function assertProviderConfig(providerId, value) {
|
|
|
42
42
|
throw new Error(`Invalid tools config at "providers.${providerId}": expected an object.`);
|
|
43
43
|
}
|
|
44
44
|
const config = value;
|
|
45
|
+
const isTTSRuntimeProviderId = (provider) => provider === "polly" || provider === "google" || provider === "custom";
|
|
45
46
|
if ("enabled" in config &&
|
|
46
47
|
config.enabled !== undefined &&
|
|
47
48
|
typeof config.enabled !== "boolean") {
|
|
@@ -54,7 +55,8 @@ function assertProviderConfig(providerId, value) {
|
|
|
54
55
|
}
|
|
55
56
|
if ("provider" in config &&
|
|
56
57
|
config.provider !== undefined &&
|
|
57
|
-
!isPlainObject(config.provider)
|
|
58
|
+
!isPlainObject(config.provider) &&
|
|
59
|
+
!(providerId === "textToSpeech" && isTTSRuntimeProviderId(config.provider))) {
|
|
58
60
|
throw new Error(`Invalid tools config at "providers.${providerId}.provider": expected an object.`);
|
|
59
61
|
}
|
|
60
62
|
return config;
|
|
@@ -139,11 +141,4 @@ export function normalizeToolsConfig(input) {
|
|
|
139
141
|
}
|
|
140
142
|
return config;
|
|
141
143
|
}
|
|
142
|
-
export function resolveToolsForLevel(config, level) {
|
|
143
|
-
const placement = normalizeToolList(config.placement[level]);
|
|
144
|
-
const allowed = normalizeToolList(config.policy.allowed);
|
|
145
|
-
const blocked = new Set(normalizeToolList(config.policy.blocked));
|
|
146
|
-
const passAllowed = allowed.length === 0 ? placement : placement.filter((toolId) => allowed.includes(toolId));
|
|
147
|
-
return passAllowed.filter((toolId) => !blocked.has(toolId));
|
|
148
|
-
}
|
|
149
144
|
//# sourceMappingURL=tools-config-normalizer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools-config-normalizer.js","sourceRoot":"","sources":["../../src/services/tools-config-normalizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEFAULT_TOOL_PLACEMENT } from \"./tool-config-defaults.js\";\n\nexport type ToolPlacementLevel = \"section\" | \"item\" | \"passage\";\n\nexport interface ToolPolicyConfig {\n\tallowed?: string[];\n\tblocked?: string[];\n}\n\nexport interface ToolPlacementConfig {\n\tsection?: string[];\n\titem?: string[];\n\tpassage?: string[];\n}\n\nexport interface ToolRuntimeBackendRequest {\n\tpath: string;\n\tmethod?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\theaders?: Record<string, string>;\n\tquery?: Record<string, string | number | boolean>;\n\tbody?: unknown;\n}\n\nexport interface ToolRuntimeProviderBridge {\n\t/**\n\t * Optional auth fetch hook for provider initialization.\n\t * Useful for retrieving temporary credentials from a host backend.\n\t */\n\tauthFetcher?: () => Promise<Record<string, unknown>>;\n\t/**\n\t * Optional backend request bridge for rich tool interactions\n\t * beyond one-time auth fetching.\n\t */\n\trequest?: (request: ToolRuntimeBackendRequest) => Promise<unknown>;\n\t/**\n\t * Optional host event emitter.\n\t */\n\temit?: (eventName: string, payload?: Record<string, unknown>) => void | Promise<void>;\n\t/**\n\t * Optional host event subscription bridge.\n\t */\n\tsubscribe?: (\n\t\teventName: string,\n\t\thandler: (payload: unknown) => void,\n\t) => (() => void) | undefined;\n\t[key: string]: unknown;\n}\n\nexport interface ToolRuntimeProviderConfig {\n\tid?: string;\n\tinit?: Record<string, unknown>;\n\truntime?: ToolRuntimeProviderBridge;\n}\n\nexport interface ToolProviderConfig {\n\tenabled?: boolean;\n\tprovider?: ToolRuntimeProviderConfig;\n\tsettings?: Record<string, unknown>;\n\t[key: string]: unknown;\n}\n\nexport interface ToolProvidersConfig {\n\t[key: string]: ToolProviderConfig | undefined;\n}\n\n/**\n * PNP/profile enforcement mode mirrored on `runtime.tools.pnpEnforcement`\n * (M5 mirror rule). Carried alongside `policy` / `placement` /\n * `providers` so hosts can pin or opt out of PNP/profile precedence via\n * the runtime config without reaching for the toolkit prop directly.\n *\n * - `\"on\"` — force enforcement (engine applies PNP/profile gates).\n * - `\"off\"` — opt out (engine ignores PNP/profile inputs).\n * - omitted / `undefined` — auto-mode (the default). The toolkit\n * coordinator computes the effective mode from PNP/profile policy material on the\n * bound `AssessmentEntity` / `AssessmentItemRef`. See\n * `resolveDefaultPnpEnforcement` in `policy/core/pnp-policy-inputs.ts`.\n */\nexport type ToolsPnpEnforcement = \"on\" | \"off\";\n\nexport interface CanonicalToolsConfig {\n\tpolicy: ToolPolicyConfig;\n\tplacement: Required<ToolPlacementConfig>;\n\tproviders: ToolProvidersConfig;\n\t/**\n\t * Optional PNP/profile enforcement override. Mirrored on\n\t * `runtime.tools.pnpEnforcement`; consumed by\n\t * `<pie-assessment-toolkit>` and `ToolkitCoordinator` as the\n\t * embedded path's equivalent of the standalone `pnp-enforcement`\n\t * attribute. See {@link ToolsPnpEnforcement} for semantics.\n\t */\n\tpnpEnforcement?: ToolsPnpEnforcement;\n}\n\nconst DEFAULT_PLACEMENT: Required<ToolPlacementConfig> = {\n\tsection: [...DEFAULT_TOOL_PLACEMENT.section],\n\titem: [...DEFAULT_TOOL_PLACEMENT.item],\n\tpassage: [...DEFAULT_TOOL_PLACEMENT.passage],\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction assertStringArray(value: unknown, fieldPath: string): string[] {\n\tif (value == null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"${fieldPath}\": expected an array of tool ids.`,\n\t\t);\n\t}\n\tconst invalidEntry = value.find((entry) => typeof entry !== \"string\");\n\tif (invalidEntry !== undefined) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"${fieldPath}\": all entries must be strings.`,\n\t\t);\n\t}\n\treturn value as string[];\n}\n\nfunction assertPlacementConfig(value: unknown): ToolPlacementConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"placement\": expected an object with section/item/passage arrays.',\n\t\t);\n\t}\n\treturn value as ToolPlacementConfig;\n}\n\nfunction assertPolicyConfig(value: unknown): ToolPolicyConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"policy\": expected an object with allowed/blocked arrays.',\n\t\t);\n\t}\n\treturn value as ToolPolicyConfig;\n}\n\nfunction assertProviderConfig(\n\tproviderId: string,\n\tvalue: unknown,\n): ToolProviderConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}\": expected an object.`,\n\t\t);\n\t}\n\tconst config = value as ToolProviderConfig;\n\tif (\n\t\t\"enabled\" in config &&\n\t\tconfig.enabled !== undefined &&\n\t\ttypeof config.enabled !== \"boolean\"\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.enabled\": expected a boolean.`,\n\t\t);\n\t}\n\tif (\n\t\t\"settings\" in config &&\n\t\tconfig.settings !== undefined &&\n\t\t!isPlainObject(config.settings)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.settings\": expected an object.`,\n\t\t);\n\t}\n\tif (\n\t\t\"provider\" in config &&\n\t\tconfig.provider !== undefined &&\n\t\t!isPlainObject(config.provider)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.provider\": expected an object.`,\n\t\t);\n\t}\n\treturn config;\n}\n\nfunction assertProvidersConfig(value: unknown): ToolProvidersConfig {\n\tif (value == null) return {};\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"providers\": expected an object keyed by tool id.',\n\t\t);\n\t}\n\tconst normalized: ToolProvidersConfig = {};\n\tfor (const [providerId, providerConfig] of Object.entries(value)) {\n\t\tnormalized[providerId] = assertProviderConfig(providerId, providerConfig);\n\t}\n\treturn normalized;\n}\n\nexport function normalizeToolAlias(toolId: string): string {\n\tconst trimmed = toolId.trim();\n\tif (!trimmed) return \"\";\n\treturn trimmed;\n}\n\nexport function normalizeToolList(toolIds: string[] | undefined | null): string[] {\n\tif (!toolIds || toolIds.length === 0) return [];\n\tconst deduped = new Set<string>();\n\tfor (const rawToolId of toolIds) {\n\t\tif (typeof rawToolId !== \"string\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"Invalid tools config: tool list entries must be strings.\",\n\t\t\t);\n\t\t}\n\t\tconst normalized = normalizeToolAlias(rawToolId);\n\t\tif (!normalized) continue;\n\t\tdeduped.add(normalized);\n\t}\n\treturn Array.from(deduped);\n}\n\nexport function parseToolList(input: string | undefined | null): string[] {\n\tif (!input) return [];\n\treturn normalizeToolList(\n\t\tinput\n\t\t\t.split(\",\")\n\t\t\t.map((entry) => entry.trim())\n\t\t\t.filter(Boolean),\n\t);\n}\n\nfunction assertPnpEnforcement(\n\tvalue: unknown,\n\tfieldPath: \"pnpEnforcement\",\n): ToolsPnpEnforcement | undefined {\n\tif (value == null) return undefined;\n\tif (value === \"on\" || value === \"off\") return value;\n\tthrow new Error(\n\t\t`Invalid tools config at \"${fieldPath}\": expected \"on\" or \"off\", got ${JSON.stringify(value)}.`,\n\t);\n}\n\nexport function normalizeToolsConfig(\n\tinput?: Partial<CanonicalToolsConfig> | null,\n): CanonicalToolsConfig {\n\tif (input != null && !isPlainObject(input)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config: expected an object with \"policy\", \"placement\", and \"providers\".',\n\t\t);\n\t}\n\tconst policy = assertPolicyConfig(input?.policy);\n\tconst placement = assertPlacementConfig(input?.placement);\n\tconst providers = assertProvidersConfig(input?.providers);\n\tconst pnpEnforcement = assertPnpEnforcement(\n\t\tinput?.pnpEnforcement,\n\t\t\"pnpEnforcement\",\n\t);\n\n\tconst config: CanonicalToolsConfig = {\n\t\tpolicy: {\n\t\t\tallowed: normalizeToolList(assertStringArray(policy?.allowed, \"policy.allowed\")),\n\t\t\tblocked: normalizeToolList(assertStringArray(policy?.blocked, \"policy.blocked\")),\n\t\t},\n\t\tplacement: {\n\t\t\tsection: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.section, \"placement.section\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(assertStringArray(placement?.section, \"placement.section\"))\n\t\t\t\t: [...DEFAULT_PLACEMENT.section],\n\t\t\titem: normalizeToolList(assertStringArray(placement?.item, \"placement.item\"))\n\t\t\t\t.length\n\t\t\t\t? normalizeToolList(assertStringArray(placement?.item, \"placement.item\"))\n\t\t\t\t: [...DEFAULT_PLACEMENT.item],\n\t\t\tpassage: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.passage, \"placement.passage\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(assertStringArray(placement?.passage, \"placement.passage\"))\n\t\t\t\t: [...DEFAULT_PLACEMENT.passage],\n\t\t},\n\t\tproviders,\n\t};\n\tif (pnpEnforcement) {\n\t\tconfig.pnpEnforcement = pnpEnforcement;\n\t}\n\treturn config;\n}\n\nexport function resolveToolsForLevel(\n\tconfig: CanonicalToolsConfig,\n\tlevel: ToolPlacementLevel,\n): string[] {\n\tconst placement = normalizeToolList(config.placement[level]);\n\tconst allowed = normalizeToolList(config.policy.allowed);\n\tconst blocked = new Set(normalizeToolList(config.policy.blocked));\n\tconst passAllowed =\n\t\tallowed.length === 0 ? placement : placement.filter((toolId) => allowed.includes(toolId));\n\treturn passAllowed.filter((toolId) => !blocked.has(toolId));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tools-config-normalizer.js","sourceRoot":"","sources":["../../src/services/tools-config-normalizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEFAULT_TOOL_PLACEMENT } from \"./tool-config-defaults.js\";\nimport type { SREMathSpeechOptions } from \"./tts/math-speech.js\";\n\nexport type ToolPlacementLevel = \"section\" | \"item\" | \"passage\";\n\nexport interface ToolPolicyConfig {\n\tallowed?: string[];\n\tblocked?: string[];\n}\n\nexport interface ToolPlacementConfig {\n\tsection?: string[];\n\titem?: string[];\n\tpassage?: string[];\n}\n\nexport interface ToolRuntimeBackendRequest {\n\tpath: string;\n\tmethod?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\theaders?: Record<string, string>;\n\tquery?: Record<string, string | number | boolean>;\n\tbody?: unknown;\n}\n\nexport interface ToolRuntimeProviderBridge {\n\t/**\n\t * Optional auth fetch hook for provider initialization.\n\t * Useful for retrieving temporary credentials from a host backend.\n\t */\n\tauthFetcher?: () => Promise<Record<string, unknown>>;\n\t/**\n\t * Optional backend request bridge for rich tool interactions\n\t * beyond one-time auth fetching.\n\t */\n\trequest?: (request: ToolRuntimeBackendRequest) => Promise<unknown>;\n\t/**\n\t * Optional host event emitter.\n\t */\n\temit?: (eventName: string, payload?: Record<string, unknown>) => void | Promise<void>;\n\t/**\n\t * Optional host event subscription bridge.\n\t */\n\tsubscribe?: (\n\t\teventName: string,\n\t\thandler: (payload: unknown) => void,\n\t) => (() => void) | undefined;\n\t[key: string]: unknown;\n}\n\nexport interface ToolRuntimeProviderConfig {\n\tid?: string;\n\tinit?: Record<string, unknown>;\n\truntime?: ToolRuntimeProviderBridge;\n}\n\nexport type TextToSpeechRuntimeProviderId = \"polly\" | \"google\" | \"custom\";\n\nexport interface ToolProviderConfig {\n\tenabled?: boolean;\n\tprovider?: ToolRuntimeProviderConfig;\n\tsettings?: Record<string, unknown>;\n\t[key: string]: unknown;\n}\n\nexport interface TextToSpeechToolProviderSettings\n\textends Record<string, unknown> {\n\tmathSpeech?: SREMathSpeechOptions;\n}\n\nexport interface TextToSpeechToolProviderConfig\n\textends Omit<ToolProviderConfig, \"provider\" | \"settings\"> {\n\tprovider?: ToolRuntimeProviderConfig | TextToSpeechRuntimeProviderId;\n\tserverProvider?: TextToSpeechRuntimeProviderId;\n\tmathSpeech?: SREMathSpeechOptions;\n\tsettings?: TextToSpeechToolProviderSettings;\n}\n\nexport interface ToolProvidersConfig {\n\ttextToSpeech?: TextToSpeechToolProviderConfig;\n\t[key: string]: ToolProviderConfig | TextToSpeechToolProviderConfig | undefined;\n}\n\n/**\n * PNP/profile enforcement mode mirrored on `runtime.tools.pnpEnforcement`\n * (M5 mirror rule). Carried alongside `policy` / `placement` /\n * `providers` so hosts can pin or opt out of PNP/profile precedence via\n * the runtime config without reaching for the toolkit prop directly.\n *\n * - `\"on\"` — force enforcement (engine applies PNP/profile gates).\n * - `\"off\"` — opt out (engine ignores PNP/profile inputs).\n * - omitted / `undefined` — auto-mode (the default). The toolkit\n * coordinator computes the effective mode from PNP/profile policy material on the\n * bound `AssessmentEntity` / `AssessmentItemRef`. See\n * `resolveDefaultPnpEnforcement` in `policy/core/pnp-policy-inputs.ts`.\n */\nexport type ToolsPnpEnforcement = \"on\" | \"off\";\n\nexport interface CanonicalToolsConfig {\n\tpolicy: ToolPolicyConfig;\n\tplacement: Required<ToolPlacementConfig>;\n\tproviders: ToolProvidersConfig;\n\t/**\n\t * Optional PNP/profile enforcement override. Mirrored on\n\t * `runtime.tools.pnpEnforcement`; consumed by\n\t * `<pie-assessment-toolkit>` and `ToolkitCoordinator` as the\n\t * embedded path's equivalent of the standalone `pnp-enforcement`\n\t * attribute. See {@link ToolsPnpEnforcement} for semantics.\n\t */\n\tpnpEnforcement?: ToolsPnpEnforcement;\n}\n\nconst DEFAULT_PLACEMENT: Required<ToolPlacementConfig> = {\n\tsection: [...DEFAULT_TOOL_PLACEMENT.section],\n\titem: [...DEFAULT_TOOL_PLACEMENT.item],\n\tpassage: [...DEFAULT_TOOL_PLACEMENT.passage],\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction assertStringArray(value: unknown, fieldPath: string): string[] {\n\tif (value == null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"${fieldPath}\": expected an array of tool ids.`,\n\t\t);\n\t}\n\tconst invalidEntry = value.find((entry) => typeof entry !== \"string\");\n\tif (invalidEntry !== undefined) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"${fieldPath}\": all entries must be strings.`,\n\t\t);\n\t}\n\treturn value as string[];\n}\n\nfunction assertPlacementConfig(value: unknown): ToolPlacementConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"placement\": expected an object with section/item/passage arrays.',\n\t\t);\n\t}\n\treturn value as ToolPlacementConfig;\n}\n\nfunction assertPolicyConfig(value: unknown): ToolPolicyConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"policy\": expected an object with allowed/blocked arrays.',\n\t\t);\n\t}\n\treturn value as ToolPolicyConfig;\n}\n\nfunction assertProviderConfig(\n\tproviderId: string,\n\tvalue: unknown,\n): ToolProviderConfig | TextToSpeechToolProviderConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}\": expected an object.`,\n\t\t);\n\t}\n\tconst config = value as ToolProviderConfig;\n\tconst isTTSRuntimeProviderId = (provider: unknown): boolean =>\n\t\tprovider === \"polly\" || provider === \"google\" || provider === \"custom\";\n\tif (\n\t\t\"enabled\" in config &&\n\t\tconfig.enabled !== undefined &&\n\t\ttypeof config.enabled !== \"boolean\"\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.enabled\": expected a boolean.`,\n\t\t);\n\t}\n\tif (\n\t\t\"settings\" in config &&\n\t\tconfig.settings !== undefined &&\n\t\t!isPlainObject(config.settings)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.settings\": expected an object.`,\n\t\t);\n\t}\n\tif (\n\t\t\"provider\" in config &&\n\t\tconfig.provider !== undefined &&\n\t\t!isPlainObject(config.provider) &&\n\t\t!(providerId === \"textToSpeech\" && isTTSRuntimeProviderId(config.provider))\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.provider\": expected an object.`,\n\t\t);\n\t}\n\treturn config;\n}\n\nfunction assertProvidersConfig(value: unknown): ToolProvidersConfig {\n\tif (value == null) return {};\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"providers\": expected an object keyed by tool id.',\n\t\t);\n\t}\n\tconst normalized: ToolProvidersConfig = {};\n\tfor (const [providerId, providerConfig] of Object.entries(value)) {\n\t\tnormalized[providerId] = assertProviderConfig(providerId, providerConfig);\n\t}\n\treturn normalized;\n}\n\nexport function normalizeToolAlias(toolId: string): string {\n\tconst trimmed = toolId.trim();\n\tif (!trimmed) return \"\";\n\treturn trimmed;\n}\n\nexport function normalizeToolList(toolIds: string[] | undefined | null): string[] {\n\tif (!toolIds || toolIds.length === 0) return [];\n\tconst deduped = new Set<string>();\n\tfor (const rawToolId of toolIds) {\n\t\tif (typeof rawToolId !== \"string\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"Invalid tools config: tool list entries must be strings.\",\n\t\t\t);\n\t\t}\n\t\tconst normalized = normalizeToolAlias(rawToolId);\n\t\tif (!normalized) continue;\n\t\tdeduped.add(normalized);\n\t}\n\treturn Array.from(deduped);\n}\n\nexport function parseToolList(input: string | undefined | null): string[] {\n\tif (!input) return [];\n\treturn normalizeToolList(\n\t\tinput\n\t\t\t.split(\",\")\n\t\t\t.map((entry) => entry.trim())\n\t\t\t.filter(Boolean),\n\t);\n}\n\nfunction assertPnpEnforcement(\n\tvalue: unknown,\n\tfieldPath: \"pnpEnforcement\",\n): ToolsPnpEnforcement | undefined {\n\tif (value == null) return undefined;\n\tif (value === \"on\" || value === \"off\") return value;\n\tthrow new Error(\n\t\t`Invalid tools config at \"${fieldPath}\": expected \"on\" or \"off\", got ${JSON.stringify(value)}.`,\n\t);\n}\n\nexport function normalizeToolsConfig(\n\tinput?: Partial<CanonicalToolsConfig> | null,\n): CanonicalToolsConfig {\n\tif (input != null && !isPlainObject(input)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config: expected an object with \"policy\", \"placement\", and \"providers\".',\n\t\t);\n\t}\n\tconst policy = assertPolicyConfig(input?.policy);\n\tconst placement = assertPlacementConfig(input?.placement);\n\tconst providers = assertProvidersConfig(input?.providers);\n\tconst pnpEnforcement = assertPnpEnforcement(\n\t\tinput?.pnpEnforcement,\n\t\t\"pnpEnforcement\",\n\t);\n\n\tconst config: CanonicalToolsConfig = {\n\t\tpolicy: {\n\t\t\tallowed: normalizeToolList(assertStringArray(policy?.allowed, \"policy.allowed\")),\n\t\t\tblocked: normalizeToolList(assertStringArray(policy?.blocked, \"policy.blocked\")),\n\t\t},\n\t\tplacement: {\n\t\t\tsection: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.section, \"placement.section\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(assertStringArray(placement?.section, \"placement.section\"))\n\t\t\t\t: [...DEFAULT_PLACEMENT.section],\n\t\t\titem: normalizeToolList(assertStringArray(placement?.item, \"placement.item\"))\n\t\t\t\t.length\n\t\t\t\t? normalizeToolList(assertStringArray(placement?.item, \"placement.item\"))\n\t\t\t\t: [...DEFAULT_PLACEMENT.item],\n\t\t\tpassage: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.passage, \"placement.passage\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(assertStringArray(placement?.passage, \"placement.passage\"))\n\t\t\t\t: [...DEFAULT_PLACEMENT.passage],\n\t\t},\n\t\tproviders,\n\t};\n\tif (pnpEnforcement) {\n\t\tconfig.pnpEnforcement = pnpEnforcement;\n\t}\n\treturn config;\n}\n\n"]}
|