@pie-players/pie-assessment-toolkit 0.3.72 → 0.3.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +171 -56
  2. package/dist/attempt/AssessmentSession.d.ts +1 -5
  3. package/dist/attempt/AssessmentSession.js +0 -29
  4. package/dist/components/ItemToolBar.custom-element.d.ts +1 -0
  5. package/dist/components/ItemToolBar.custom-element.js +1 -1
  6. package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
  8. package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
  9. package/dist/components/SectionToolBar.custom-element.js +1 -1
  10. package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +40 -0
  11. package/dist/components/chunks/ItemToolBar-gmrryv2v.js +2 -0
  12. package/dist/components/chunks/ItemToolBar-h4xap0t0.js +4 -0
  13. package/dist/components/chunks/ItemToolBar-wr2myaah.js +2 -0
  14. package/dist/components/chunks/sre-engine-wf9s43gg.js +1 -0
  15. package/dist/context/assessment-toolkit-context.d.ts +5 -0
  16. package/dist/context/runtime-context-consumer.js +5 -38
  17. package/dist/index.d.ts +6 -6
  18. package/dist/index.js +8 -9
  19. package/dist/policy/core/ToolPolicyEngine.d.ts +8 -2
  20. package/dist/policy/core/ToolPolicyEngine.js +12 -0
  21. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +3 -3
  22. package/dist/runtime/adapter/SectionEngineAdapter.js +3 -3
  23. package/dist/runtime/registration-events.d.ts +3 -0
  24. package/dist/runtime/section-runtime-engine-host-context.d.ts +3 -9
  25. package/dist/runtime/section-runtime-engine-host-context.js +5 -43
  26. package/dist/runtime/tool-host-contract.d.ts +20 -0
  27. package/dist/runtime/tool-host-contract.js +21 -0
  28. package/dist/services/HighlightCoordinator.d.ts +1 -7
  29. package/dist/services/HighlightCoordinator.js +1 -3
  30. package/dist/services/TTSService.d.ts +15 -1
  31. package/dist/services/TTSService.js +88 -35
  32. package/dist/services/ToolCoordinator.d.ts +1 -7
  33. package/dist/services/ToolCoordinator.js +1 -3
  34. package/dist/services/ToolRegistry.d.ts +13 -6
  35. package/dist/services/ToolRegistry.js +19 -7
  36. package/dist/services/ToolkitCoordinator.d.ts +110 -18
  37. package/dist/services/ToolkitCoordinator.js +273 -85
  38. package/dist/services/catalog-owner.d.ts +11 -0
  39. package/dist/services/catalog-owner.js +25 -0
  40. package/dist/services/create-tools-config.d.ts +2 -2
  41. package/dist/services/framework-error.d.ts +8 -1
  42. package/dist/services/interfaces.d.ts +9 -9
  43. package/dist/services/pnp-standard-features.d.ts +1 -1
  44. package/dist/services/section-controller-types.d.ts +30 -0
  45. package/dist/services/spoken-audio-cards.js +1 -1
  46. package/dist/services/tool-config-validation.d.ts +13 -2
  47. package/dist/services/tool-config-validation.js +33 -16
  48. package/dist/services/tool-context.js +29 -13
  49. package/dist/services/tool-providers/TTSToolProvider.d.ts +27 -65
  50. package/dist/services/tool-providers/TTSToolProvider.js +47 -9
  51. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +4 -4
  52. package/dist/services/tool-providers/ToolProviderRegistry.js +4 -16
  53. package/dist/services/tool-providers/index.d.ts +0 -9
  54. package/dist/services/tool-providers/index.js +2 -4
  55. package/dist/services/tools-config-normalizer.d.ts +20 -1
  56. package/dist/services/tts/catalog-span-alignment.d.ts +2 -17
  57. package/dist/services/tts/catalog-span-alignment.js +17 -142
  58. package/dist/services/tts/generated-speech/types.d.ts +0 -12
  59. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
  60. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +26 -1
  61. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
  62. package/dist/services/tts/math-alignment/index.d.ts +0 -5
  63. package/dist/services/tts/math-alignment/index.js +5 -25
  64. package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
  65. package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
  66. package/dist/services/tts/math-alignment/types.d.ts +4 -2
  67. package/dist/services/tts/math-aware-text-processing.js +44 -2
  68. package/dist/services/tts/math-speech.d.ts +5 -1
  69. package/dist/services/tts/math-speech.js +9 -6
  70. package/dist/services/tts/sre-engine.d.ts +7 -0
  71. package/dist/services/tts/sre-engine.js +11 -0
  72. package/dist/services/tts/sre-locales.d.ts +14 -0
  73. package/dist/services/tts/sre-locales.js +57 -0
  74. package/dist/services/tts/text-processing.d.ts +0 -1
  75. package/dist/services/tts/text-processing.js +0 -1
  76. package/dist/services/tts-runtime-config.d.ts +21 -3
  77. package/dist/services/tts-runtime-config.js +11 -3
  78. package/dist/tools/client.d.ts +1 -3
  79. package/dist/tools/client.js +1 -5
  80. package/dist/tools/internal.d.ts +6 -5
  81. package/dist/tools/internal.js +6 -8
  82. package/package.json +17 -36
  83. package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
  84. package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
  85. package/dist/services/ToolConfigResolver.d.ts +0 -161
  86. package/dist/services/ToolConfigResolver.js +0 -229
  87. package/dist/services/catalog-media.d.ts +0 -56
  88. package/dist/services/catalog-media.js +0 -190
  89. package/dist/services/createDefaultToolRegistry.d.ts +0 -53
  90. package/dist/services/createDefaultToolRegistry.js +0 -45
  91. package/dist/services/tool-providers/CortexToolProvider.d.ts +0 -13
  92. package/dist/services/tool-providers/CortexToolProvider.js +0 -32
  93. package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
  94. package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
  95. package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
  96. package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
  97. package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
  98. package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
  99. package/dist/services/tts/visual-line-ranges.d.ts +0 -18
  100. package/dist/services/tts/visual-line-ranges.js +0 -226
  101. package/dist/tools/index.d.ts +0 -8
  102. package/dist/tools/index.js +0 -10
  103. package/dist/tools/response-discovery.d.ts +0 -61
  104. package/dist/tools/response-discovery.js +0 -113
  105. package/dist/tools/types.d.ts +0 -337
  106. package/dist/tools/types.js +0 -12
  107. package/dist/tools/variant-resolver.d.ts +0 -41
  108. package/dist/tools/variant-resolver.js +0 -207
@@ -1,8 +1,8 @@
1
1
  import { extractSpokenText } from "../ssml/spoken-text.js";
2
2
  import { createSpeechAlignmentTokenPattern } from "../text-processing.js";
3
- // Shared Unicode-aware tokenizer, identical to the catalog span aligner's, so
4
- // spoken text tokenizes the same way on both paths (see
5
- // `createSpeechAlignmentTokenPattern`).
3
+ // The one tokenizer and normalization table for TTS alignment. Spoken text,
4
+ // visible catalog text and provider boundary words all tokenize here, so a word
5
+ // normalizes the same way on the catalog, math and generated-speech paths.
6
6
  const TOKEN_PATTERN = createSpeechAlignmentTokenPattern();
7
7
  const NUMERIC_WORDS = new Map([
8
8
  ["zero", "0"],
@@ -21,24 +21,27 @@ const normalizedTokenValue = (value) => {
21
21
  const normalized = value.toLowerCase();
22
22
  return NUMERIC_WORDS.get(normalized) || normalized;
23
23
  };
24
- const tokenizeSpokenText = (spokenText) => {
24
+ export const tokenizeAlignmentText = (text) => {
25
25
  const tokens = [];
26
- for (const match of spokenText.matchAll(TOKEN_PATTERN)) {
27
- const text = match[0];
26
+ for (const match of text.matchAll(TOKEN_PATTERN)) {
27
+ const value = match[0];
28
28
  const start = match.index ?? 0;
29
29
  tokens.push({
30
- id: `speech-token-${tokens.length}`,
31
- text,
32
- normalized: normalizedTokenValue(text),
30
+ text: value,
31
+ normalized: normalizedTokenValue(value),
33
32
  start,
34
- end: start + text.length,
35
- sourceStart: start,
36
- sourceEnd: start + text.length,
37
- coordinateSystem: "normalized-speech",
33
+ end: start + value.length,
38
34
  });
39
35
  }
40
36
  return tokens;
41
37
  };
38
+ const tokenizeSpokenText = (spokenText) => tokenizeAlignmentText(spokenText).map((token, index) => ({
39
+ ...token,
40
+ id: `speech-token-${index}`,
41
+ sourceStart: token.start,
42
+ sourceEnd: token.end,
43
+ coordinateSystem: "normalized-speech",
44
+ }));
42
45
  export const tokenizeSpeechSource = (args) => {
43
46
  const extracted = extractSpokenText(args.speechText);
44
47
  return {
@@ -55,15 +58,17 @@ export const tokenizeSpeechSource = (args) => {
55
58
  hasMarkup: extracted.hasMarkup,
56
59
  };
57
60
  };
58
- const normalizeBoundaryWord = (word) => {
59
- if (!word)
61
+ const isControlTagWord = (word) => Boolean(word && /^<[^>]+>$/.test(word.trim()));
62
+ /**
63
+ * A provider boundary word in the form spoken tokens compare in, or null when
64
+ * the word carries no token (empty, a control tag, punctuation). The word goes
65
+ * through the same SSML extraction as the chunk, so an entity-encoded word
66
+ * normalizes to the text it encodes.
67
+ */
68
+ export const normalizeBoundaryWord = (word) => {
69
+ if (!word || isControlTagWord(word))
60
70
  return null;
61
- if (/^<[^>]+>$/.test(word.trim()))
62
- return null;
63
- const match = word.match(TOKEN_PATTERN);
64
- if (!match?.[0])
65
- return null;
66
- return normalizedTokenValue(match[0]);
71
+ return (tokenizeSpeechSource({ speechText: word }).tokens[0]?.normalized ?? null);
67
72
  };
68
73
  const findTokenAtOffset = (tokens, offset) => tokens.find((token) => token.start <= offset && offset < token.end) || null;
69
74
  const mapRawOffsetToSpokenOffset = (tokenization, position) => {
@@ -92,32 +97,29 @@ const candidateForOffset = (tokenization, offset, length) => {
92
97
  confidence: 0.75,
93
98
  };
94
99
  };
95
- const candidateMatchesWord = (candidate, boundaryWord) => {
96
- const normalizedBoundary = normalizeBoundaryWord(boundaryWord);
97
- return Boolean(candidate &&
98
- normalizedBoundary &&
99
- candidate.token.normalized === normalizedBoundary);
100
- };
100
+ const candidateMatchesWord = (candidate, normalizedWord) => Boolean(candidate &&
101
+ normalizedWord &&
102
+ candidate.token.normalized === normalizedWord);
101
103
  export const resolveBoundaryToSpeechToken = (args) => {
102
104
  if (args.tokenization.boundaryOffsetSpace === "unsupported" ||
103
105
  !Number.isFinite(args.position) ||
104
- (args.boundaryWord && /^<[^>]+>$/.test(args.boundaryWord.trim()))) {
106
+ isControlTagWord(args.boundaryWord)) {
105
107
  return null;
106
108
  }
107
109
  const safeLength = Math.max(1, Number.isFinite(args.length) ? args.length : 1);
110
+ const normalizedWord = normalizeBoundaryWord(args.boundaryWord);
108
111
  const rawCandidate = candidateForOffset(args.tokenization, mapRawOffsetToSpokenOffset(args.tokenization, args.position), safeLength);
109
112
  const plainCandidate = candidateForOffset(args.tokenization, args.position >= 0 && args.position < args.tokenization.spokenText.length
110
113
  ? args.position
111
114
  : null, safeLength);
112
- if (candidateMatchesWord(rawCandidate, args.boundaryWord)) {
115
+ if (candidateMatchesWord(rawCandidate, normalizedWord)) {
113
116
  return { ...rawCandidate, confidence: 1 };
114
117
  }
115
- if (candidateMatchesWord(plainCandidate, args.boundaryWord)) {
118
+ if (candidateMatchesWord(plainCandidate, normalizedWord)) {
116
119
  return { ...plainCandidate, confidence: 1 };
117
120
  }
118
- if (args.boundaryWord && normalizeBoundaryWord(args.boundaryWord)) {
121
+ if (normalizedWord)
119
122
  return null;
120
- }
121
123
  return args.tokenization.boundaryOffsetSpace === "raw-ssml"
122
124
  ? rawCandidate || plainCandidate
123
125
  : plainCandidate || rawCandidate;
@@ -125,18 +127,18 @@ export const resolveBoundaryToSpeechToken = (args) => {
125
127
  export const resolveSpokenOffsetToSpeechToken = (args) => {
126
128
  if (args.tokenization.boundaryOffsetSpace === "unsupported" ||
127
129
  !Number.isFinite(args.position) ||
128
- (args.boundaryWord && /^<[^>]+>$/.test(args.boundaryWord.trim()))) {
130
+ isControlTagWord(args.boundaryWord)) {
129
131
  return null;
130
132
  }
131
133
  const safeLength = Math.max(1, Number.isFinite(args.length) ? args.length : 1);
134
+ const normalizedWord = normalizeBoundaryWord(args.boundaryWord);
132
135
  const spokenCandidate = candidateForOffset(args.tokenization, args.position >= 0 && args.position < args.tokenization.spokenText.length
133
136
  ? args.position
134
137
  : null, safeLength);
135
- if (candidateMatchesWord(spokenCandidate, args.boundaryWord)) {
138
+ if (candidateMatchesWord(spokenCandidate, normalizedWord)) {
136
139
  return { ...spokenCandidate, confidence: 1 };
137
140
  }
138
- if (args.boundaryWord && normalizeBoundaryWord(args.boundaryWord)) {
141
+ if (normalizedWord)
139
142
  return null;
140
- }
141
143
  return spokenCandidate;
142
144
  };
@@ -34,12 +34,14 @@ export interface MathMLTokenizationResult {
34
34
  }
35
35
  export type BoundaryCoordinateSystem = "normalized-speech" | "raw-ssml" | "provider-speech-marks";
36
36
  export type BoundaryOffsetSpace = "plain-spoken-text" | "raw-ssml" | "unsupported";
37
- export interface SpeechAlignmentToken {
38
- id: string;
37
+ export interface AlignmentTextToken {
39
38
  text: string;
40
39
  normalized: string;
41
40
  start: number;
42
41
  end: number;
42
+ }
43
+ export interface SpeechAlignmentToken extends AlignmentTextToken {
44
+ id: string;
43
45
  sourceStart: number;
44
46
  sourceEnd: number;
45
47
  coordinateSystem: BoundaryCoordinateSystem;
@@ -44,7 +44,6 @@ const findCanonicalMathML = (element) => {
44
44
  }
45
45
  return null;
46
46
  };
47
- const isMathContainer = (element) => Boolean(findCanonicalMathML(element));
48
47
  const TEXT_CHUNK_SOURCE_TAGS = new Set([
49
48
  "P",
50
49
  "H1",
@@ -223,6 +222,49 @@ const textFallbackFromMathML = (mathml) => {
223
222
  visit(parsed.documentElement);
224
223
  return normalizeTextForSpeech(parts.join(" "));
225
224
  };
225
+ // MathJax CHTML paints its glyphs from CSS, so a typeset equation's only text
226
+ // nodes are the source MathML it keeps, clipped, in mjx-assistive-mml. Mapping
227
+ // the fallback text onto them places a catalog word inside its equation, where
228
+ // the rendered-math resolver finds the glyph. Any divergence between the two
229
+ // texts leaves the fallback unmapped.
230
+ const mapFallbackToAssistiveMath = (element, fallbackText) => {
231
+ const map = new Map();
232
+ const sourceMath = isMathJaxElement(element)
233
+ ? element.querySelector("mjx-assistive-mml math")
234
+ : null;
235
+ if (!sourceMath)
236
+ return map;
237
+ const characters = [];
238
+ const visit = (node) => {
239
+ if (node.nodeType === 3) {
240
+ const text = node.textContent || "";
241
+ for (let offset = 0; offset < text.length; offset++) {
242
+ if (!/\s/.test(text[offset])) {
243
+ characters.push({ node: node, offset });
244
+ }
245
+ }
246
+ return;
247
+ }
248
+ if (node.nodeType !== 1 || isAnnotationElement(node))
249
+ return;
250
+ for (const child of Array.from(node.childNodes)) {
251
+ visit(child);
252
+ }
253
+ };
254
+ visit(sourceMath);
255
+ let next = 0;
256
+ for (let index = 0; index < fallbackText.length; index++) {
257
+ if (/\s/.test(fallbackText[index]))
258
+ continue;
259
+ const mapping = characters[next];
260
+ if (mapping?.node.textContent?.[mapping.offset] !== fallbackText[index]) {
261
+ return new Map();
262
+ }
263
+ map.set(index, mapping);
264
+ next++;
265
+ }
266
+ return next === characters.length ? map : new Map();
267
+ };
226
268
  const collectVisibleMathFallback = (element, canonicalMathML, options) => {
227
269
  const mathAcc = createAccumulator(options);
228
270
  const visit = (node) => {
@@ -255,7 +297,7 @@ const collectVisibleMathFallback = (element, canonicalMathML, options) => {
255
297
  }
256
298
  return {
257
299
  text: fallbackText,
258
- map: new Map(),
300
+ map: mapFallbackToAssistiveMath(element, fallbackText),
259
301
  };
260
302
  };
261
303
  const trimTrailingWhitespace = (acc) => {
@@ -25,7 +25,11 @@ export interface SREMathSpeechOptions {
25
25
  domain?: string;
26
26
  /** Raw SRE style/preference string. ClearSpeak preferences are ":"-joined. */
27
27
  style?: string;
28
- /** Extra SRE setupEngine options for hosts that need a newly exposed SRE knob. */
28
+ /**
29
+ * Extra SRE setupEngine options for hosts that need a newly exposed SRE knob.
30
+ * In a browser, SRE's locale tables load from the host's bundle unless `json`
31
+ * (a base URL or path) or `custom` (a loader) names another source.
32
+ */
29
33
  engineOptions?: Record<string, unknown>;
30
34
  }
31
35
  export interface ResolvedMathSpeech {
@@ -1,12 +1,12 @@
1
+ import { setSreStartupLocaleSource, sreLocaleSource } from "./sre-locales.js";
1
2
  import { normalizeTextForSpeech } from "./text-processing.js";
2
3
  let sreLoadPromise = null;
3
4
  let sreOperationQueue = Promise.resolve();
4
5
  const normalizeLocale = (language) => (language || "en").split("-")[0].toLowerCase() || "en";
5
- const defaultLoadSre = async () => {
6
+ const defaultLoadSre = async (engineOptions) => {
6
7
  if (!sreLoadPromise) {
7
- sreLoadPromise = import("speech-rule-engine").then((module) => {
8
- const candidate = module.default ||
9
- module;
8
+ setSreStartupLocaleSource(engineOptions);
9
+ sreLoadPromise = import("./sre-engine.js").then(({ default: candidate }) => {
10
10
  if (!candidate || typeof candidate.toSpeech !== "function") {
11
11
  throw new Error("speech-rule-engine did not expose toSpeech");
12
12
  }
@@ -41,8 +41,10 @@ export const normalizeSREMathSpeechOptions = (options) => {
41
41
  const setupSre = async (sre, language, markup, mathSpeech) => {
42
42
  const locale = normalizeLocale(language);
43
43
  const normalizedMathSpeech = normalizeSREMathSpeechOptions(mathSpeech);
44
+ const engineOptions = normalizedMathSpeech?.engineOptions || {};
44
45
  await sre.setupEngine?.({
45
- ...(normalizedMathSpeech?.engineOptions || {}),
46
+ ...sreLocaleSource(engineOptions),
47
+ ...engineOptions,
46
48
  locale,
47
49
  domain: normalizedMathSpeech?.domain || domainForLocale(locale),
48
50
  ...(normalizedMathSpeech?.style
@@ -97,7 +99,8 @@ export const resolveMathSpeechFromChunks = async (chunks, options = {}) => {
97
99
  let sre = null;
98
100
  let usedMathSpeech = false;
99
101
  let usedFallback = false;
100
- const loadSre = options.loadSre || defaultLoadSre;
102
+ const loadSre = options.loadSre ||
103
+ (() => defaultLoadSre(normalizeSREMathSpeechOptions(options.mathSpeech)?.engineOptions));
101
104
  const speechParts = [];
102
105
  for (const chunk of chunks) {
103
106
  if (chunk.type === "text") {
@@ -0,0 +1,7 @@
1
+ import * as speechRuleEngine from "speech-rule-engine";
2
+ declare const sre: {
3
+ setupEngine: typeof speechRuleEngine.setupEngine;
4
+ engineReady: typeof speechRuleEngine.engineReady;
5
+ toSpeech: typeof speechRuleEngine.toSpeech;
6
+ };
7
+ export default sre;
@@ -0,0 +1,11 @@
1
+ import * as speechRuleEngine from "speech-rule-engine";
2
+ import { sreStartupLocaleSource } from "./sre-locales.js";
3
+ const sre = speechRuleEngine.default ?? speechRuleEngine;
4
+ // SRE starts itself up while this module's import of it evaluates, then loads
5
+ // `base` and `en` a few microtasks later from whatever locale source is set by
6
+ // then. In a bundle this call runs synchronously after that evaluation, so it
7
+ // lands first; configuring SRE after awaiting an `import()` of it lands after
8
+ // those loads. Bun drains microtasks between evaluating a CommonJS module and
9
+ // its importer, so there SRE reads `base` from its own package directory.
10
+ void sre.setupEngine(sreStartupLocaleSource());
11
+ export default sre;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The `setupEngine` options naming where SRE reads locale tables from: the
3
+ * host's `json` (a base URL or path) or `custom` (a loader) when its
4
+ * `engineOptions` set either, otherwise the packaged tables wherever SRE would
5
+ * fetch them. SRE prefers `custom` over `json`, so the packaged loader is left
6
+ * out whenever the host names a source.
7
+ */
8
+ export declare const sreLocaleSource: (engineOptions?: Record<string, unknown>) => Record<string, unknown>;
9
+ /**
10
+ * Sets the source SRE loads its start-up locales from; see `./sre-engine.ts`.
11
+ * Only a call made before that module first loads has any effect.
12
+ */
13
+ export declare const setSreStartupLocaleSource: (engineOptions?: Record<string, unknown>) => void;
14
+ export declare const sreStartupLocaleSource: () => Record<string, unknown>;
@@ -0,0 +1,57 @@
1
+ // SRE loads each locale's rule tables at runtime, from a public CDN unless it is
2
+ // handed a source. An import per table lets the host's bundler emit each as a
3
+ // lazy chunk served from the host's own origin, but a build that inlines every
4
+ // `import()` into one file carries every table listed here, at 0.2 to 0.4 MB
5
+ // each. So only `base`, which every locale builds on, and the `en` and `es`
6
+ // locales are packaged; any other locale needs a source from the host's
7
+ // `engineOptions`. No `with { type: "json" }`: bundlers infer JSON from the
8
+ // extension, while Vite's dev server and Bun's bundler turn a table into
9
+ // JavaScript and keep the attribute, which a browser then rejects.
10
+ const SRE_LOCALE_TABLES = {
11
+ base: () => import("speech-rule-engine/lib/mathmaps/base.json"),
12
+ en: () => import("speech-rule-engine/lib/mathmaps/en.json"),
13
+ es: () => import("speech-rule-engine/lib/mathmaps/es.json"),
14
+ };
15
+ // SRE's `custom` loader contract: called with a locale name, resolving to that
16
+ // locale's table as a JSON string or an already-parsed object. SRE falls back
17
+ // to English when a table fails to load.
18
+ const loadPackagedSreLocale = async (locale) => {
19
+ if (!Object.hasOwn(SRE_LOCALE_TABLES, locale)) {
20
+ throw new Error(`no speech-rule-engine locale table is packaged for "${locale}"; name a source with mathSpeech.engineOptions.json or .custom`);
21
+ }
22
+ return (await SRE_LOCALE_TABLES[locale]()).default;
23
+ };
24
+ // SRE's own test for fetching tables over HTTP (`common/system_external.js`).
25
+ // Elsewhere it reads them from its package directory, which Node's ESM loader
26
+ // could not replace with the imports above: it requires the attribute.
27
+ const sreFetchesTables = () => (typeof window !== "undefined" && typeof window.document !== "undefined") ||
28
+ typeof globalThis
29
+ .DedicatedWorkerGlobalScope !== "undefined";
30
+ /**
31
+ * The `setupEngine` options naming where SRE reads locale tables from: the
32
+ * host's `json` (a base URL or path) or `custom` (a loader) when its
33
+ * `engineOptions` set either, otherwise the packaged tables wherever SRE would
34
+ * fetch them. SRE prefers `custom` over `json`, so the packaged loader is left
35
+ * out whenever the host names a source.
36
+ */
37
+ export const sreLocaleSource = (engineOptions = {}) => {
38
+ const { json, custom } = engineOptions;
39
+ if (json || custom) {
40
+ return { ...(json ? { json } : {}), ...(custom ? { custom } : {}) };
41
+ }
42
+ return sreFetchesTables() ? { custom: loadPackagedSreLocale } : {};
43
+ };
44
+ // Resolved on first read: a call at module scope would keep the table imports
45
+ // in every bundle that includes this module, math speech used or not.
46
+ let startupLocaleSource;
47
+ /**
48
+ * Sets the source SRE loads its start-up locales from; see `./sre-engine.ts`.
49
+ * Only a call made before that module first loads has any effect.
50
+ */
51
+ export const setSreStartupLocaleSource = (engineOptions) => {
52
+ startupLocaleSource = sreLocaleSource(engineOptions);
53
+ };
54
+ export const sreStartupLocaleSource = () => {
55
+ startupLocaleSource ??= sreLocaleSource();
56
+ return startupLocaleSource;
57
+ };
@@ -67,4 +67,3 @@ export declare const collectVisibleTextAndMap: (element: Element, options?: Text
67
67
  text: string;
68
68
  map: NormalizedTextMap;
69
69
  };
70
- export declare const extractVisibleText: (element: Element, options?: TextProcessingOptions) => string;
@@ -282,4 +282,3 @@ export const collectVisibleTextAndMap = (element, options) => {
282
282
  }
283
283
  return { text, map };
284
284
  };
285
- export const extractVisibleText = (element, options) => collectVisibleTextAndMap(element, options).text;
@@ -1,5 +1,5 @@
1
1
  import type { TTSConfig } from "./TTSService.js";
2
- import type { ToolProviderConfig } from "./tools-config-normalizer.js";
2
+ import type { TextToSpeechToolProviderConfig } from "./tools-config-normalizer.js";
3
3
  import { type SREMathSpeechOptions } from "./tts/math-speech.js";
4
4
  export type TTSLayoutMode = "reserved-row" | "expanding-row" | "floating-overlay" | "left-aligned";
5
5
  export interface TTSHostToolbarLayout {
@@ -42,6 +42,12 @@ export interface TTSRuntimeSettings {
42
42
  endpointMode?: "synthesizePath" | "rootPost";
43
43
  endpointValidationMode?: "voices" | "endpoint" | "none";
44
44
  includeAuthOnAssetFetch?: boolean;
45
+ /** Origins trusted with the `Authorization` header on asset fetches. */
46
+ assetOrigins?: string[];
47
+ /** Fetch `credentials` mode for requests to the TTS server. */
48
+ credentials?: "omit" | "same-origin" | "include";
49
+ /** Headers sent with every request to the TTS server. */
50
+ headers?: Record<string, string>;
45
51
  validateEndpoint?: boolean;
46
52
  cache?: boolean;
47
53
  speedRate?: "slow" | "medium" | "fast";
@@ -75,6 +81,11 @@ export interface TTSRuntimeSettings {
75
81
  * instead of relying on toolkit-specific speech rewrites.
76
82
  */
77
83
  mathSpeech?: SREMathSpeechOptions;
84
+ /**
85
+ * Options passed through to the provider. The fields derived from the
86
+ * settings above, such as `locale` and the Polly engine, win over these.
87
+ */
88
+ providerOptions?: Record<string, unknown>;
78
89
  }
79
90
  export declare const normalizeTTSLayoutMode: (value: unknown, fallback?: TTSLayoutMode) => TTSLayoutMode;
80
91
  /** Legacy numeric helper defaults. Rendered controls add visible Normal separately. */
@@ -96,10 +107,17 @@ export declare const formatTTSSpeedOptionsAsText: (values: number[]) => string;
96
107
  * - Non-empty text with no parseable finite numbers → same as invalid-only array config: defaults.
97
108
  */
98
109
  export declare const parseTTSSpeedOptionsFromText: (text: string) => number[];
99
- export declare const resolveTTSRuntimeSettings: (config: ToolProviderConfig | TTSRuntimeSettings | undefined) => TTSRuntimeSettings;
110
+ export declare const resolveTTSRuntimeSettings: (config: TextToSpeechToolProviderConfig | TTSRuntimeSettings | undefined) => TTSRuntimeSettings;
100
111
  export declare const resolveTTSBackend: (config: TTSRuntimeSettings) => NonNullable<TTSRuntimeSettings["backend"]>;
101
112
  export declare const resolveRuntimeProvider: (config: TTSRuntimeSettings, backend: NonNullable<TTSRuntimeSettings["backend"]>) => TTSRuntimeSettings["serverProvider"];
102
113
  export declare const resolveTransportMode: (config: TTSRuntimeSettings, runtimeProvider: TTSRuntimeSettings["serverProvider"]) => NonNullable<TTSRuntimeSettings["transportMode"]>;
103
- export declare const buildRuntimeTTSConfig: (config: TTSRuntimeSettings) => Partial<TTSConfig>;
114
+ /**
115
+ * The provider configuration built from host settings: the contract's portable
116
+ * fields plus the server adapter's, under the adapter's names.
117
+ */
118
+ export type RuntimeTTSConfig = Pick<TTSConfig, "voice" | "rate" | "pitch" | "providerOptions" | "mathTokenHighlighting"> & Pick<TTSRuntimeSettings, "apiEndpoint" | "language" | "transportMode" | "endpointMode" | "endpointValidationMode" | "includeAuthOnAssetFetch" | "assetOrigins" | "credentials" | "headers" | "validateEndpoint"> & {
119
+ provider?: TTSRuntimeSettings["serverProvider"];
120
+ };
121
+ export declare const buildRuntimeTTSConfig: (config: TTSRuntimeSettings) => RuntimeTTSConfig;
104
122
  export declare const resolveTTSLayoutMode: (config: TTSRuntimeSettings) => TTSLayoutMode;
105
123
  export declare const resolveTTSHostToolbarLayout: (config: TTSRuntimeSettings) => TTSHostToolbarLayout;
@@ -236,9 +236,12 @@ const applyRuntimeDefaults = (config) => {
236
236
  export const resolveTTSRuntimeSettings = (config) => {
237
237
  const configRecord = toRecord(config);
238
238
  const settingsRecord = toRecord(configRecord.settings);
239
+ const { provider, ...merged } = { ...configRecord, ...settingsRecord };
240
+ // A runtime provider object in `provider` is for the tool registration; the
241
+ // runtime settings carry only a server provider id.
239
242
  return applyRuntimeDefaults({
240
- ...configRecord,
241
- ...settingsRecord,
243
+ ...merged,
244
+ ...(typeof provider === "string" ? { provider } : {}),
242
245
  });
243
246
  };
244
247
  export const resolveTTSBackend = (config) => config.backend || "browser";
@@ -256,11 +259,12 @@ export const buildRuntimeTTSConfig = (config) => {
256
259
  const runtimeProvider = resolveRuntimeProvider(config, backend);
257
260
  const transportMode = resolveTransportMode(config, runtimeProvider);
258
261
  const mathSpeech = normalizeSREMathSpeechOptions(config.mathSpeech);
259
- return {
262
+ const runtimeConfig = {
260
263
  voice: config.defaultVoice,
261
264
  rate: config.rate,
262
265
  pitch: config.pitch,
263
266
  providerOptions: {
267
+ ...toRecord(config.providerOptions),
264
268
  ...(config.language ? { locale: config.language } : {}),
265
269
  ...(backend === "polly" && config.engine
266
270
  ? { engine: config.engine }
@@ -296,6 +300,9 @@ export const buildRuntimeTTSConfig = (config) => {
296
300
  endpointMode: config.endpointMode,
297
301
  endpointValidationMode: config.endpointValidationMode,
298
302
  includeAuthOnAssetFetch: config.includeAuthOnAssetFetch,
303
+ assetOrigins: config.assetOrigins,
304
+ credentials: config.credentials,
305
+ headers: config.headers,
299
306
  validateEndpoint: config.validateEndpoint,
300
307
  // Toolkit-level highlight setting carried through the config channel
301
308
  // (like apiEndpoint/transportMode); consumed by the highlight pipeline,
@@ -305,6 +312,7 @@ export const buildRuntimeTTSConfig = (config) => {
305
312
  ? { mathTokenHighlighting: config.mathTokenHighlighting }
306
313
  : {}),
307
314
  };
315
+ return runtimeConfig;
308
316
  };
309
317
  export const resolveTTSLayoutMode = (config) => normalizeTTSLayoutMode(config.layoutMode);
310
318
  export const resolveTTSHostToolbarLayout = (config) => {
@@ -2,11 +2,9 @@
2
2
  * Client-only tool system exports
3
3
  *
4
4
  * This entry point exports only browser-safe code for use in custom elements.
5
- * It excludes server-side services like accommodationResolver, variantResolver, etc.
6
5
  *
7
6
  * Tools should import from '@pie-players/pie-assessment-toolkit/tools/client' instead of
8
7
  * '@pie-players/pie-assessment-toolkit' to ensure
9
8
  * they don't accidentally pull in server-side dependencies.
10
9
  */
11
- export { ResponseDiscoveryServiceImpl, responseDiscovery, } from "./response-discovery.js";
12
- export * from "./types.js";
10
+ export type { CalculationHistoryEntry, Calculator, CalculatorProvider, CalculatorProviderCapabilities, CalculatorProviderConfig, CalculatorState, CalculatorType, } from "@pie-players/pie-calculator";
@@ -2,13 +2,9 @@
2
2
  * Client-only tool system exports
3
3
  *
4
4
  * This entry point exports only browser-safe code for use in custom elements.
5
- * It excludes server-side services like accommodationResolver, variantResolver, etc.
6
5
  *
7
6
  * Tools should import from '@pie-players/pie-assessment-toolkit/tools/client' instead of
8
7
  * '@pie-players/pie-assessment-toolkit' to ensure
9
8
  * they don't accidentally pull in server-side dependencies.
10
9
  */
11
- // Response discovery (client-safe, browser-only)
12
- export { ResponseDiscoveryServiceImpl, responseDiscovery, } from "./response-discovery.js";
13
- // Core types (client-safe)
14
- export * from "./types.js";
10
+ export {};
@@ -4,8 +4,8 @@
4
4
  * What a package outside this one needs to write a `ToolRegistration`: the
5
5
  * registration types, the context predicates a tool answers `isVisibleInContext`
6
6
  * with, scoped-id and element-creation helpers, the toolbar button/overlay
7
- * helpers, and the provider descriptors for the two capabilities that ship a
8
- * provider.
7
+ * helpers, the provider contract a registration's descriptor creates, and the
8
+ * TTS provider.
9
9
  *
10
10
  * A separate entry point rather than additions to `.` for the same reason
11
11
  * `runtime/internal` and `policy/internal` exist: this is a surface for sibling
@@ -19,7 +19,7 @@
19
19
  * point — the mechanism is the same one our own registrations use.
20
20
  */
21
21
  export type { HostedToolContext, HostedToolSize, ResolvedToolContext, ToolActivation, ToolContentDependency, ToolContentDependencyContext, ToolModuleLoader, ToolProviderDescriptor, ToolRegistration, ToolRenderElement, ToolSingletonScope, ToolSurfaceRenderContext, ToolSurfaceRenderResult, ToolSurfaceServices, ToolToolbarButtonDefinition, ToolToolbarRenderResult, ToolWindowShellConfig, ToolbarContext, } from "../services/ToolRegistry.js";
22
- export { ToolRegistry } from "../services/ToolRegistry.js";
22
+ export { resolveToolProviderId, ToolRegistry, } from "../services/ToolRegistry.js";
23
23
  export { resolveToolRegistrationName } from "../services/ToolRegistry.js";
24
24
  export type { CatalogOwnerCard, CatalogOwnerSnapshot, } from "../services/AccessibilityCatalogResolver.js";
25
25
  export type { ToolSelectionAction, ToolSelectionContext, } from "../services/selection-action.js";
@@ -36,8 +36,9 @@ export type { ToolComponentFactory, ToolComponentFactoryMap, ToolComponentOverri
36
36
  export { createToolElement, resolveToolTag, toToolIdFromTag, } from "./tool-tag-map.js";
37
37
  export { applyOverlaySurface, createScopedVisibilityBinding, syncButtonAndOverlayVisibility, } from "./registrations/toolbar-registration-helpers.js";
38
38
  export type { ElementToolStateStoreApi, ToolCoordinatorApi, ToolkitCoordinatorApi, TtsServiceApi, } from "../services/interfaces.js";
39
- export type { ToolPlacementConfig, ToolProviderConfig, } from "../services/tools-config-normalizer.js";
39
+ export type { CalculatorToolProviderConfig, ToolPlacementConfig, ToolProviderConfig, } from "../services/tools-config-normalizer.js";
40
40
  export type { ToolConfigDiagnostic } from "../services/tool-config-validation.js";
41
- export { CortexToolProvider, DesmosToolProvider, GeoGebraToolProvider, TTSToolProvider, } from "../services/tool-providers/index.js";
41
+ export type { ToolProviderApi, ToolProviderCapabilities, } from "../services/tool-providers/ToolProviderApi.js";
42
+ export { TTSToolProvider } from "../services/tool-providers/index.js";
42
43
  export type { NormalizedTTSSpeedOption } from "../services/tts-runtime-config.js";
43
44
  export { buildRuntimeTTSConfig, normalizeTTSLayoutMode, normalizeTTSSpeedControlOptions, resolveRuntimeProvider, resolveTTSBackend, resolveTTSHostToolbarLayout, resolveTTSLayoutMode, resolveTTSRuntimeSettings, resolveTransportMode, } from "../services/tts-runtime-config.js";
@@ -4,8 +4,8 @@
4
4
  * What a package outside this one needs to write a `ToolRegistration`: the
5
5
  * registration types, the context predicates a tool answers `isVisibleInContext`
6
6
  * with, scoped-id and element-creation helpers, the toolbar button/overlay
7
- * helpers, and the provider descriptors for the two capabilities that ship a
8
- * provider.
7
+ * helpers, the provider contract a registration's descriptor creates, and the
8
+ * TTS provider.
9
9
  *
10
10
  * A separate entry point rather than additions to `.` for the same reason
11
11
  * `runtime/internal` and `policy/internal` exist: this is a surface for sibling
@@ -18,7 +18,7 @@
18
18
  * A host writing its own capability package imports from here too, which is the
19
19
  * point — the mechanism is the same one our own registrations use.
20
20
  */
21
- export { ToolRegistry } from "../services/ToolRegistry.js";
21
+ export { resolveToolProviderId, ToolRegistry, } from "../services/ToolRegistry.js";
22
22
  // A registration's own display name in the interface locale. A tool window's
23
23
  // title is the registration's name, so the shell needs the same `nameKey`-then-
24
24
  // `name` precedence the toolbar uses rather than the raw English field.
@@ -33,9 +33,7 @@ export { createScopedToolId } from "../services/tool-instance-id.js";
33
33
  export { createToolElement, resolveToolTag, toToolIdFromTag, } from "./tool-tag-map.js";
34
34
  // Toolbar button/overlay wiring shared by every toolbar-toggle registration.
35
35
  export { applyOverlaySurface, createScopedVisibilityBinding, syncButtonAndOverlayVisibility, } from "./registrations/toolbar-registration-helpers.js";
36
- // Provider descriptors for the two packaged capabilities that ship one. These
37
- // stay here rather than moving with the registrations because they are written
38
- // against the `pie-calculator` and `pie-tts` contract packages, which the
39
- // toolkit's own `TTSService` and provider registry also depend on.
40
- export { CortexToolProvider, DesmosToolProvider, GeoGebraToolProvider, TTSToolProvider, } from "../services/tool-providers/index.js";
36
+ // The TTS provider stays here because it is written against the `pie-tts`
37
+ // contract package, which the toolkit's own `TTSService` also depends on.
38
+ export { TTSToolProvider } from "../services/tool-providers/index.js";
41
39
  export { buildRuntimeTTSConfig, normalizeTTSLayoutMode, normalizeTTSSpeedControlOptions, resolveRuntimeProvider, resolveTTSBackend, resolveTTSHostToolbarLayout, resolveTTSLayoutMode, resolveTTSRuntimeSettings, resolveTransportMode, } from "../services/tts-runtime-config.js";