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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +451 -541
  2. package/custom-elements.json +1315 -0
  3. package/dist/attempt/TestSession.d.ts +0 -1
  4. package/dist/attempt/TestSession.js +10 -5
  5. package/dist/components/ItemScope.custom-element.js +1 -1
  6. package/dist/components/ItemToolBar.custom-element.js +1 -1
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +8 -8
  8. package/dist/components/SectionToolBar.custom-element.js +1 -1
  9. package/dist/components/chunks/{ItemToolBar-7tt7v2xp.js → ItemToolBar-3zrc4p3d.js} +4 -4
  10. package/dist/components/chunks/{ItemToolBar-jx38w99s.js → ItemToolBar-66hcwmfw.js} +2 -2
  11. package/dist/components/chunks/ItemToolBar-asenkvx6.js +40 -0
  12. package/dist/components/chunks/ItemToolBar-hzc5wyjp.js +2 -0
  13. package/dist/components/chunks/{ItemToolBar-97hx60z4.js → ItemToolBar-kkj69ptr.js} +1 -1
  14. package/dist/components/chunks/sre-engine-bj997w4b.js +1 -0
  15. package/dist/context/assessment-toolkit-context.d.ts +6 -0
  16. package/dist/index.d.ts +7 -7
  17. package/dist/index.js +4 -10
  18. package/dist/policy/core/PolicySource.d.ts +1 -1
  19. package/dist/policy/core/ToolPolicyEngine.d.ts +46 -7
  20. package/dist/policy/core/ToolPolicyEngine.js +73 -16
  21. package/dist/policy/core/compose-decision.d.ts +3 -3
  22. package/dist/policy/core/compose-decision.js +32 -30
  23. package/dist/policy/core/decision-types.d.ts +51 -34
  24. package/dist/policy/core/feature-decision.d.ts +17 -15
  25. package/dist/policy/core/feature-decision.js +11 -13
  26. package/dist/policy/core/pnp-policy-inputs.d.ts +7 -3
  27. package/dist/policy/core/pnp-policy-inputs.js +17 -33
  28. package/dist/policy/core/provenance.d.ts +2 -2
  29. package/dist/policy/core/tool-parameters.d.ts +12 -0
  30. package/dist/policy/core/tool-parameters.js +27 -0
  31. package/dist/policy/engine.d.ts +1 -1
  32. package/dist/policy/sources/PnpPolicySource.d.ts +18 -24
  33. package/dist/policy/sources/PnpPolicySource.js +57 -29
  34. package/dist/runtime/SectionControllerBinding.d.ts +6 -2
  35. package/dist/runtime/SectionControllerBinding.js +21 -15
  36. package/dist/runtime/adapter/subscriber-fanout.d.ts +0 -1
  37. package/dist/runtime/adapter/subscriber-fanout.js +1 -4
  38. package/dist/runtime/composition-emit-scheduler.d.ts +3 -3
  39. package/dist/runtime/composition-emit-scheduler.js +1 -1
  40. package/dist/runtime/content-region.d.ts +0 -4
  41. package/dist/runtime/content-region.js +2 -2
  42. package/dist/runtime/core/SectionEngineCore.d.ts +0 -2
  43. package/dist/runtime/core/SectionEngineCore.js +0 -4
  44. package/dist/runtime/core/cohort.d.ts +0 -5
  45. package/dist/runtime/core/cohort.js +0 -9
  46. package/dist/runtime/core/engine-output.d.ts +3 -2
  47. package/dist/runtime/core/engine-transition.js +3 -2
  48. package/dist/runtime/registration-events.d.ts +11 -14
  49. package/dist/runtime/runtime-id.d.ts +1 -1
  50. package/dist/runtime/runtime-id.js +1 -1
  51. package/dist/runtime/shell-scope.d.ts +4 -4
  52. package/dist/runtime/shell-scope.js +15 -9
  53. package/dist/runtime/tool-host-contract.d.ts +0 -4
  54. package/dist/runtime/tool-host-contract.js +1 -1
  55. package/dist/services/AccessibilityCatalogResolver.d.ts +21 -31
  56. package/dist/services/AccessibilityCatalogResolver.js +29 -42
  57. package/dist/services/ElementToolStateStore.d.ts +32 -61
  58. package/dist/services/ElementToolStateStore.js +37 -67
  59. package/dist/services/HighlightCoordinator.d.ts +9 -21
  60. package/dist/services/HighlightCoordinator.js +52 -80
  61. package/dist/services/RangeSerializer.d.ts +4 -35
  62. package/dist/services/RangeSerializer.js +4 -48
  63. package/dist/services/SSMLExtractor.d.ts +8 -31
  64. package/dist/services/SSMLExtractor.js +18 -40
  65. package/dist/services/TTSService.d.ts +51 -57
  66. package/dist/services/TTSService.js +323 -284
  67. package/dist/services/ToolCoordinator.d.ts +6 -49
  68. package/dist/services/ToolCoordinator.js +25 -61
  69. package/dist/services/ToolRegistry.d.ts +83 -80
  70. package/dist/services/ToolRegistry.js +91 -67
  71. package/dist/services/ToolkitCoordinator.d.ts +83 -81
  72. package/dist/services/ToolkitCoordinator.js +186 -174
  73. package/dist/services/framework-error.d.ts +30 -7
  74. package/dist/services/framework-error.js +10 -13
  75. package/dist/services/interfaces.d.ts +51 -71
  76. package/dist/services/section-controller-types.d.ts +7 -7
  77. package/dist/services/section-session-assignment.js +6 -8
  78. package/dist/services/spoken-audio-cards.d.ts +2 -4
  79. package/dist/services/spoken-audio-cards.js +2 -4
  80. package/dist/services/tool-config-validation.d.ts +5 -7
  81. package/dist/services/tool-config-validation.js +11 -26
  82. package/dist/services/tool-context.d.ts +39 -24
  83. package/dist/services/tool-context.js +158 -92
  84. package/dist/services/tool-failure.d.ts +30 -0
  85. package/dist/services/tool-failure.js +11 -0
  86. package/dist/services/tool-icons.d.ts +1 -2
  87. package/dist/services/tool-icons.js +2 -2
  88. package/dist/services/tool-instance-id.d.ts +8 -6
  89. package/dist/services/tool-instance-id.js +12 -18
  90. package/dist/services/tool-providers/TTSToolProvider.d.ts +6 -43
  91. package/dist/services/tool-providers/TTSToolProvider.js +7 -63
  92. package/dist/services/tool-providers/ToolProviderApi.d.ts +0 -7
  93. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +7 -42
  94. package/dist/services/tool-providers/ToolProviderRegistry.js +21 -63
  95. package/dist/services/tool-request.d.ts +4 -0
  96. package/dist/services/tool-request.js +7 -2
  97. package/dist/services/toolbar-decided-tools.d.ts +2 -0
  98. package/dist/services/toolbar-decided-tools.js +2 -0
  99. package/dist/services/toolbar-items.js +2 -5
  100. package/dist/services/tools-config-normalizer.d.ts +18 -9
  101. package/dist/services/tools-config-normalizer.js +25 -19
  102. package/dist/services/tts/browser-provider.d.ts +2 -5
  103. package/dist/services/tts/browser-provider.js +13 -22
  104. package/dist/services/tts/catalog-span-alignment.js +14 -7
  105. package/dist/services/tts/flat-tree.d.ts +0 -7
  106. package/dist/services/tts/flat-tree.js +2 -2
  107. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +2 -2
  108. package/dist/services/tts/generated-speech/index.d.ts +1 -1
  109. package/dist/services/tts/generated-speech/index.js +1 -1
  110. package/dist/services/tts/generated-speech/types.d.ts +1 -1
  111. package/dist/services/tts/generated-speech/types.js +1 -1
  112. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +10 -12
  113. package/dist/services/tts/highlight-target-resolver.d.ts +12 -0
  114. package/dist/services/tts/math-aware-text-processing.js +10 -29
  115. package/dist/services/tts/math-speech.d.ts +1 -1
  116. package/dist/services/tts/math-speech.js +3 -0
  117. package/dist/services/tts/mathml-sanitization.d.ts +7 -0
  118. package/dist/services/tts/mathml-sanitization.js +9 -1
  119. package/dist/services/tts/provider-options.d.ts +12 -1
  120. package/dist/services/tts/ssml/spoken-text.js +1 -1
  121. package/dist/services/tts/start-failure.d.ts +0 -7
  122. package/dist/services/tts/start-failure.js +1 -1
  123. package/dist/services/tts/text-segmentation.d.ts +5 -0
  124. package/dist/services/tts/text-segmentation.js +13 -10
  125. package/dist/services/tts-runtime-config.d.ts +32 -12
  126. package/dist/services/tts-runtime-config.js +14 -20
  127. package/dist/tools/content-capability-resolution.d.ts +2 -2
  128. package/dist/tools/registration.d.ts +7 -6
  129. package/dist/tools/registration.js +10 -9
  130. package/dist/tools/registrations/toolbar-registration-helpers.d.ts +6 -0
  131. package/dist/tools/registrations/toolbar-registration-helpers.js +6 -0
  132. package/dist/tools/tool-surface-host.js +1 -8
  133. package/package.json +8 -6
  134. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +0 -2
  135. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +0 -40
  136. package/dist/components/chunks/sre-engine-jd8cc08x.js +0 -1
  137. package/dist/services/ContextVariableStore.d.ts +0 -113
  138. package/dist/services/ContextVariableStore.js +0 -206
  139. package/dist/services/tts-control-events.d.ts +0 -13
  140. package/dist/services/tts-control-events.js +0 -7
  141. package/dist/utils/logger.d.ts +0 -6
  142. package/dist/utils/logger.js +0 -10
@@ -7,8 +7,6 @@
7
7
  * `polly` and `google` run on the `pie` transport; `custom` runs on the
8
8
  * `custom` transport to a host's own service, which takes `lang_id`,
9
9
  * `speedRate` and `cache`
10
- *
11
- * Part of PIE Assessment Toolkit.
12
10
  */
13
11
  import { createPieLogger, isTtsDebugEnabled, } from "@pie-players/pie-players-shared/pie";
14
12
  import { BrowserTTSProvider } from "../../services/tts/browser-provider.js";
@@ -23,8 +21,6 @@ const logger = createPieLogger("tts-tool-provider", isTtsDebugEnabled);
23
21
  function bindServerBackendConfig(provider, bound) {
24
22
  return {
25
23
  providerId: provider.providerId,
26
- providerName: provider.providerName,
27
- version: provider.version,
28
24
  initialize: (config) => {
29
25
  const runtime = config;
30
26
  const merged = {
@@ -84,24 +80,15 @@ export class TTSToolProvider {
84
80
  console.warn("[TTSToolProvider] telemetry callback failed:", error);
85
81
  }
86
82
  }
87
- /**
88
- * Create TTS tool provider
89
- *
90
- * @param backend TTS backend to use (default: 'browser')
91
- * @param options Loader for the server adapter, required by server backends
92
- */
83
+ /** A server backend needs `options.loadServerProvider`. */
93
84
  constructor(backend = "browser", options = {}) {
94
85
  this.backend = backend;
95
86
  this.requiresAuth = backend !== "browser";
96
87
  this.loadServerProvider = options.loadServerProvider;
97
88
  }
98
89
  /**
99
- * Initialize TTS provider
100
- *
101
- * Sets up the appropriate TTS backend.
102
- *
103
- * @param config Runtime configuration and credentials
104
- * @throws Error if initialization fails or required config missing
90
+ * Set up the backend's TTS provider. Throws when the backend is unavailable
91
+ * or its required config is missing. Once initialized, a call only warns.
105
92
  */
106
93
  async initialize(config) {
107
94
  if (this.ttsProvider) {
@@ -111,7 +98,7 @@ export class TTSToolProvider {
111
98
  this.config = config;
112
99
  switch (this.backend) {
113
100
  case "browser":
114
- await this._initializeBrowserTTS(config);
101
+ await this._initializeBrowserTTS();
115
102
  break;
116
103
  case "server":
117
104
  await this._initializeServerTTS(config);
@@ -121,20 +108,13 @@ export class TTSToolProvider {
121
108
  }
122
109
  logger.debug(`initialized (backend: ${this.backend})`);
123
110
  }
124
- /**
125
- * Initialize browser TTS (Web Speech API)
126
- */
127
- async _initializeBrowserTTS(config) {
128
- // Check if Web Speech API is available
111
+ async _initializeBrowserTTS() {
129
112
  if (typeof window === "undefined" || !("speechSynthesis" in window)) {
130
113
  throw new Error("[TTSToolProvider] Browser TTS not supported (Web Speech API not available)");
131
114
  }
132
115
  this.ttsProvider = new BrowserTTSProvider();
133
116
  logger.debug("browser TTS initialized (Web Speech API)");
134
117
  }
135
- /**
136
- * Initialize server-based TTS
137
- */
138
118
  async _initializeServerTTS(config) {
139
119
  if (!config.apiEndpoint) {
140
120
  throw new Error("[TTSToolProvider] apiEndpoint required for server-based TTS backends");
@@ -178,13 +158,8 @@ export class TTSToolProvider {
178
158
  logger.debug(`server TTS initialized (provider: ${serverProvider})`);
179
159
  }
180
160
  /**
181
- * Create a TTS provider instance
182
- *
183
- * Returns the initialized TTS provider.
184
- *
185
- * @param config Optional instance-specific configuration (currently unused)
186
- * @returns TTS provider
187
- * @throws Error if provider not initialized
161
+ * The initialized TTS provider; every call returns the same one and ignores
162
+ * `config`. Throws before `initialize`.
188
163
  */
189
164
  async createInstance(config) {
190
165
  if (!this.ttsProvider) {
@@ -192,40 +167,9 @@ export class TTSToolProvider {
192
167
  }
193
168
  return this.ttsProvider;
194
169
  }
195
- /**
196
- * The backend's capabilities. Its features are the speech provider's own,
197
- * and all false before {@link initialize} creates it.
198
- */
199
- getCapabilities() {
200
- const isBrowser = this.backend === "browser";
201
- const speech = this.ttsProvider?.getCapabilities();
202
- return {
203
- supportsOffline: isBrowser,
204
- requiresAuth: !isBrowser,
205
- maxInstances: 1, // Single TTS instance (playback is sequential)
206
- features: {
207
- wordBoundary: speech?.supportsWordBoundary ?? false,
208
- pause: speech?.supportsPause ?? false,
209
- resume: speech?.supportsResume ?? false,
210
- rateControl: speech?.supportsRateControl ?? false,
211
- pitchControl: speech?.supportsPitchControl ?? false,
212
- voiceSelection: speech?.supportsVoiceSelection ?? false,
213
- },
214
- };
215
- }
216
- /**
217
- * Check if provider is ready
218
- *
219
- * @returns true if provider is initialized
220
- */
221
170
  isReady() {
222
171
  return this.ttsProvider !== null;
223
172
  }
224
- /**
225
- * Clean up provider resources
226
- *
227
- * Destroys the TTS provider and releases resources.
228
- */
229
173
  destroy() {
230
174
  if (this.ttsProvider) {
231
175
  this.ttsProvider.destroy();
@@ -5,12 +5,6 @@
5
5
  * configuration, authentication, or external services.
6
6
  */
7
7
  export type ToolCategory = "calculator" | "tts" | "translation" | "annotation" | "accessibility" | "other";
8
- export interface ToolProviderCapabilities {
9
- supportsOffline: boolean;
10
- requiresAuth: boolean;
11
- maxInstances?: number | null;
12
- features: Record<string, boolean>;
13
- }
14
8
  /**
15
9
  * A tool's provider. It registers under its tool's id, which is how the toolkit
16
10
  * and the tool find it; it carries no id of its own.
@@ -25,7 +19,6 @@ export interface ToolProviderApi<TInstance = any> {
25
19
  readonly requiresAuth: boolean;
26
20
  initialize(config: unknown): Promise<void>;
27
21
  createInstance(config?: unknown): Promise<TInstance>;
28
- getCapabilities(): ToolProviderCapabilities;
29
22
  isReady(): boolean;
30
23
  destroy(): void;
31
24
  }
@@ -3,8 +3,6 @@
3
3
  *
4
4
  * Centralized registry for managing tool providers.
5
5
  * Handles initialization, authentication, and lazy loading.
6
- *
7
- * Part of PIE Assessment Toolkit.
8
6
  */
9
7
  import type { ToolProviderApi, ToolCategory } from "./ToolProviderApi.js";
10
8
  /**
@@ -36,7 +34,7 @@ export interface ToolProviderRegistration<TConfig = any> {
36
34
  * @example
37
35
  * ```typescript
38
36
  * authFetcher: async () => {
39
- * const response = await fetch('/api/desmos/token');
37
+ * const response = await fetch('/api/tools/desmos/auth');
40
38
  * return response.json(); // { apiKey: '...' }
41
39
  * }
42
40
  * ```
@@ -90,9 +88,6 @@ export declare class ToolProviderRegistry {
90
88
  * A registration under an id already registered replaces it: the replaced
91
89
  * provider is destroyed once a start in progress settles, and a caller
92
90
  * waiting on that start gets the replacement.
93
- *
94
- * @param toolId Id of the tool the provider serves
95
- * @param config Provider configuration
96
91
  */
97
92
  register(toolId: string, config: ToolProviderRegistration): void;
98
93
  /**
@@ -100,10 +95,7 @@ export declare class ToolProviderRegistry {
100
95
  *
101
96
  * Fetches auth if needed and initializes the provider.
102
97
  * Safe to call multiple times - subsequent calls wait for first initialization.
103
- *
104
- * @param toolId Tool whose provider to initialize
105
- * @returns Promise that resolves when initialization complete
106
- * @throws Error if provider not registered or initialization fails
98
+ * Throws when the provider is not registered or its initialization fails.
107
99
  */
108
100
  initialize(toolId: string): Promise<void>;
109
101
  /** Whether a registration replaced `provider` under `toolId`. */
@@ -117,51 +109,24 @@ export declare class ToolProviderRegistry {
117
109
  * Get a provider instance
118
110
  *
119
111
  * Retrieves a registered provider. If autoInitialize is true and provider
120
- * is not initialized, initializes it first.
121
- *
122
- * @param toolId Id of the tool the provider serves
123
- * @param autoInitialize Auto-initialize if not ready (default: true)
124
- * @returns Provider instance
125
- * @throws Error if provider not registered
112
+ * is not initialized, initializes it first. Throws when the provider is not
113
+ * registered.
126
114
  */
127
115
  getProvider<T extends ToolProviderApi = ToolProviderApi>(toolId: string, autoInitialize?: boolean): Promise<T>;
128
- /**
129
- * The ids of the tools whose provider is in `category`.
130
- *
131
- * @param category Tool category to filter by
132
- */
116
+ /** The ids of the tools whose provider is in `category`. */
133
117
  getProvidersByCategory(category: ToolCategory): string[];
134
118
  /**
135
119
  * The ids of the tools with a registered provider.
136
120
  */
137
121
  getProviderIds(): string[];
138
- /**
139
- * Check if provider is registered
140
- *
141
- * @param toolId Id of the tool the provider serves
142
- * @returns true if provider is registered
143
- */
144
122
  has(toolId: string): boolean;
145
- /**
146
- * Check if provider is initialized
147
- *
148
- * @param toolId Id of the tool the provider serves
149
- * @returns true if provider is initialized
150
- */
151
123
  isInitialized(toolId: string): boolean;
152
- /**
153
- * Check if provider is currently initializing
154
- *
155
- * @param toolId Id of the tool the provider serves
156
- * @returns true if initialization in progress
157
- */
158
124
  isInitializing(toolId: string): boolean;
159
125
  /**
160
126
  * Unregister and destroy a provider
161
127
  *
162
- * Removes the provider from the registry and calls its destroy method.
163
- *
164
- * @param toolId Tool whose provider to unregister
128
+ * Removes the provider from the registry and calls its destroy method,
129
+ * after any start in progress settles.
165
130
  */
166
131
  unregister(toolId: string): Promise<void>;
167
132
  /**
@@ -3,12 +3,17 @@
3
3
  *
4
4
  * Centralized registry for managing tool providers.
5
5
  * Handles initialization, authentication, and lazy loading.
6
- *
7
- * Part of PIE Assessment Toolkit.
8
6
  */
9
7
  import { createPieLogger, isGlobalDebugEnabled, } from "@pie-players/pie-players-shared/pie";
10
8
  // Lifecycle lines need `window.PIE_DEBUG = true`, read on each line.
11
9
  const logger = createPieLogger("ToolProviderRegistry", isGlobalDebugEnabled);
10
+ /** The `backend` a provider config names, for telemetry payloads. */
11
+ function telemetryBackend(value) {
12
+ const backend = value && typeof value === "object"
13
+ ? value.backend
14
+ : undefined;
15
+ return typeof backend === "string" && backend ? backend : "unknown";
16
+ }
12
17
  /**
13
18
  * Tool Provider Registry
14
19
  *
@@ -59,9 +64,6 @@ export class ToolProviderRegistry {
59
64
  * A registration under an id already registered replaces it: the replaced
60
65
  * provider is destroyed once a start in progress settles, and a caller
61
66
  * waiting on that start gets the replacement.
62
- *
63
- * @param toolId Id of the tool the provider serves
64
- * @param config Provider configuration
65
67
  */
66
68
  register(toolId, config) {
67
69
  const replaced = this.providers.get(toolId);
@@ -81,7 +83,6 @@ export class ToolProviderRegistry {
81
83
  }
82
84
  }
83
85
  logger.debug(`${replaced ? "Replaced" : "Registered"} provider "${toolId}" (${config.provider.providerName})`);
84
- // Initialize immediately if not lazy
85
86
  if (config.lazy === false) {
86
87
  this.initialize(toolId).catch((error) => {
87
88
  console.error(`[ToolProviderRegistry] Failed to initialize provider "${toolId}":`, error);
@@ -93,22 +94,16 @@ export class ToolProviderRegistry {
93
94
  *
94
95
  * Fetches auth if needed and initializes the provider.
95
96
  * Safe to call multiple times - subsequent calls wait for first initialization.
96
- *
97
- * @param toolId Tool whose provider to initialize
98
- * @returns Promise that resolves when initialization complete
99
- * @throws Error if provider not registered or initialization fails
97
+ * Throws when the provider is not registered or its initialization fails.
100
98
  */
101
99
  async initialize(toolId) {
102
- // Already initialized
103
100
  if (this.initialized.get(toolId)) {
104
101
  return;
105
102
  }
106
- // Initialization in progress - wait for it
107
103
  const existingPromise = this.initializationPromises.get(toolId);
108
104
  if (existingPromise) {
109
105
  return existingPromise;
110
106
  }
111
- // Start new initialization
112
107
  const provider = this.providers.get(toolId);
113
108
  const initPromise = this._doInitialize(toolId);
114
109
  this.initializationPromises.set(toolId, initPromise);
@@ -116,6 +111,8 @@ export class ToolProviderRegistry {
116
111
  await initPromise;
117
112
  }
118
113
  catch (error) {
114
+ // When a registration replaced the provider mid-start, this caller
115
+ // gets the replacement, so the old start's failure is dropped.
119
116
  if (!this.isReplaced(toolId, provider))
120
117
  throw error;
121
118
  }
@@ -150,26 +147,18 @@ export class ToolProviderRegistry {
150
147
  if (!provider || !config) {
151
148
  throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
152
149
  }
153
- // Start with base config
154
150
  let providerConfig = { ...config.config };
155
- const deriveBackend = (value) => {
156
- const backend = value && typeof value === "object"
157
- ? value.backend
158
- : undefined;
159
- return typeof backend === "string" && backend ? backend : "unknown";
160
- };
161
151
  const providerInitStartedAt = Date.now();
162
152
  await this.emitTelemetry(config, "pie-tool-init-start", {
163
153
  toolId,
164
- backend: deriveBackend(providerConfig),
154
+ backend: telemetryBackend(providerConfig),
165
155
  operation: "provider-initialize",
166
156
  });
167
- // Fetch auth if needed
168
157
  if (provider.requiresAuth && config.authFetcher) {
169
158
  const authFetchStartedAt = Date.now();
170
159
  await this.emitTelemetry(config, "pie-tool-backend-call-start", {
171
160
  toolId,
172
- backend: deriveBackend(providerConfig),
161
+ backend: telemetryBackend(providerConfig),
173
162
  operation: "auth-fetch",
174
163
  });
175
164
  logger.debug(`Fetching auth for "${toolId}"...`);
@@ -178,7 +167,7 @@ export class ToolProviderRegistry {
178
167
  providerConfig = { ...providerConfig, ...authData };
179
168
  await this.emitTelemetry(config, "pie-tool-backend-call-success", {
180
169
  toolId,
181
- backend: deriveBackend(providerConfig),
170
+ backend: telemetryBackend(providerConfig),
182
171
  operation: "auth-fetch",
183
172
  duration: Date.now() - authFetchStartedAt,
184
173
  });
@@ -186,7 +175,7 @@ export class ToolProviderRegistry {
186
175
  catch (error) {
187
176
  await this.emitTelemetry(config, "pie-tool-backend-call-error", {
188
177
  toolId,
189
- backend: deriveBackend(providerConfig),
178
+ backend: telemetryBackend(providerConfig),
190
179
  operation: "auth-fetch",
191
180
  duration: Date.now() - authFetchStartedAt,
192
181
  errorType: "ProviderAuthFetchError",
@@ -196,7 +185,6 @@ export class ToolProviderRegistry {
196
185
  throw new Error(`Failed to fetch auth credentials for provider "${toolId}"`);
197
186
  }
198
187
  }
199
- // Initialize provider
200
188
  try {
201
189
  await provider.initialize(providerConfig);
202
190
  if (this.providers.get(toolId) === provider) {
@@ -204,7 +192,7 @@ export class ToolProviderRegistry {
204
192
  }
205
193
  await this.emitTelemetry(config, "pie-tool-init-success", {
206
194
  toolId,
207
- backend: deriveBackend(providerConfig),
195
+ backend: telemetryBackend(providerConfig),
208
196
  operation: "provider-initialize",
209
197
  duration: Date.now() - providerInitStartedAt,
210
198
  });
@@ -213,7 +201,7 @@ export class ToolProviderRegistry {
213
201
  catch (error) {
214
202
  await this.emitTelemetry(config, "pie-tool-init-error", {
215
203
  toolId,
216
- backend: deriveBackend(providerConfig),
204
+ backend: telemetryBackend(providerConfig),
217
205
  operation: "provider-initialize",
218
206
  duration: Date.now() - providerInitStartedAt,
219
207
  errorType: "ProviderInitializationError",
@@ -227,12 +215,8 @@ export class ToolProviderRegistry {
227
215
  * Get a provider instance
228
216
  *
229
217
  * Retrieves a registered provider. If autoInitialize is true and provider
230
- * is not initialized, initializes it first.
231
- *
232
- * @param toolId Id of the tool the provider serves
233
- * @param autoInitialize Auto-initialize if not ready (default: true)
234
- * @returns Provider instance
235
- * @throws Error if provider not registered
218
+ * is not initialized, initializes it first. Throws when the provider is not
219
+ * registered.
236
220
  */
237
221
  async getProvider(toolId, autoInitialize = true) {
238
222
  const provider = this.providers.get(toolId);
@@ -243,18 +227,13 @@ export class ToolProviderRegistry {
243
227
  });
244
228
  throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
245
229
  }
246
- // Auto-initialize if needed
247
230
  if (autoInitialize && !this.initialized.get(toolId)) {
248
231
  logger.debug("Auto-initializing provider:", toolId);
249
232
  await this.initialize(toolId);
250
233
  }
251
234
  return provider;
252
235
  }
253
- /**
254
- * The ids of the tools whose provider is in `category`.
255
- *
256
- * @param category Tool category to filter by
257
- */
236
+ /** The ids of the tools whose provider is in `category`. */
258
237
  getProvidersByCategory(category) {
259
238
  return Array.from(this.providers.entries())
260
239
  .filter(([_, provider]) => provider.category === category)
@@ -266,44 +245,24 @@ export class ToolProviderRegistry {
266
245
  getProviderIds() {
267
246
  return Array.from(this.providers.keys());
268
247
  }
269
- /**
270
- * Check if provider is registered
271
- *
272
- * @param toolId Id of the tool the provider serves
273
- * @returns true if provider is registered
274
- */
275
248
  has(toolId) {
276
249
  return this.providers.has(toolId);
277
250
  }
278
- /**
279
- * Check if provider is initialized
280
- *
281
- * @param toolId Id of the tool the provider serves
282
- * @returns true if provider is initialized
283
- */
284
251
  isInitialized(toolId) {
285
252
  return this.initialized.get(toolId) === true;
286
253
  }
287
- /**
288
- * Check if provider is currently initializing
289
- *
290
- * @param toolId Id of the tool the provider serves
291
- * @returns true if initialization in progress
292
- */
293
254
  isInitializing(toolId) {
294
255
  return this.initializationPromises.has(toolId);
295
256
  }
296
257
  /**
297
258
  * Unregister and destroy a provider
298
259
  *
299
- * Removes the provider from the registry and calls its destroy method.
300
- *
301
- * @param toolId Tool whose provider to unregister
260
+ * Removes the provider from the registry and calls its destroy method,
261
+ * after any start in progress settles.
302
262
  */
303
263
  async unregister(toolId) {
304
264
  const provider = this.providers.get(toolId);
305
265
  if (provider) {
306
- // Wait for any pending initialization
307
266
  const initPromise = this.initializationPromises.get(toolId);
308
267
  if (initPromise) {
309
268
  try {
@@ -318,7 +277,6 @@ export class ToolProviderRegistry {
318
277
  return;
319
278
  }
320
279
  this.destroyProvider(toolId, provider);
321
- // Remove from registry
322
280
  this.providers.delete(toolId);
323
281
  this.configs.delete(toolId);
324
282
  this.initialized.delete(toolId);
@@ -19,6 +19,7 @@
19
19
  * Core names no capability. The requester supplies the tool id, which is why the
20
20
  * pairing of a selection action to a dictionary lives in the composition layer.
21
21
  */
22
+ import type { ToolFailurePhase } from "./tool-failure.js";
22
23
  import type { ToolPlacementLevel } from "./tools-config-normalizer.js";
23
24
  /** Level a request resolves against when the requester names none. */
24
25
  export declare const DEFAULT_TOOL_REQUEST_LEVEL: ToolPlacementLevel;
@@ -79,8 +80,11 @@ export interface ToolRequestTarget {
79
80
  * earlier one is still mounted does not displace it.
80
81
  */
81
82
  export declare class ToolRequestRegistry {
83
+ private readonly onTargetFailure;
82
84
  private readonly targets;
83
85
  private readonly changeListeners;
86
+ /** `onTargetFailure` receives a target's throw, which the registry recovers from. */
87
+ constructor(onTargetFailure?: (toolId: string, phase: ToolFailurePhase, error: unknown) => void);
84
88
  registerTarget(target: ToolRequestTarget): () => void;
85
89
  /**
86
90
  * Whether a request for this tool would reach a toolbar.
@@ -28,8 +28,13 @@ export const DEFAULT_TOOL_REQUEST_LEVEL = "section";
28
28
  * earlier one is still mounted does not displace it.
29
29
  */
30
30
  export class ToolRequestRegistry {
31
+ onTargetFailure;
31
32
  targets = new Set();
32
33
  changeListeners = new Set();
34
+ /** `onTargetFailure` receives a target's throw, which the registry recovers from. */
35
+ constructor(onTargetFailure = (toolId, phase, error) => console.error(`[ToolRequestRegistry] "${toolId}" ${phase} failed:`, error)) {
36
+ this.onTargetFailure = onTargetFailure;
37
+ }
33
38
  registerTarget(target) {
34
39
  this.targets.add(target);
35
40
  this.notifyTargetsChange();
@@ -58,7 +63,7 @@ export class ToolRequestRegistry {
58
63
  target.open(request.toolId, request.params);
59
64
  }
60
65
  catch (error) {
61
- console.error(`[ToolRequestRegistry] Target failed to open "${request.toolId}":`, error);
66
+ this.onTargetFailure(request.toolId, "tool-request-open", error);
62
67
  return false;
63
68
  }
64
69
  return true;
@@ -115,7 +120,7 @@ export class ToolRequestRegistry {
115
120
  return target.hostsTool(toolId) === true;
116
121
  }
117
122
  catch (error) {
118
- console.warn(`[ToolRequestRegistry] Target at level "${target.level}" failed the host check for "${toolId}":`, error);
123
+ this.onTargetFailure(toolId, "tool-request-host-check", error);
119
124
  return false;
120
125
  }
121
126
  }
@@ -4,6 +4,8 @@ export interface DecidedToolbarTool {
4
4
  toolId: string;
5
5
  required: boolean;
6
6
  alwaysAvailable: boolean;
7
+ activateAtInit: boolean;
8
+ parameters?: Record<string, unknown>;
7
9
  }
8
10
  /**
9
11
  * A toolbar's decided tools across re-decisions. The tracker returns its
@@ -17,6 +17,8 @@ export function createDecidedToolsTracker() {
17
17
  toolId: entry.toolId,
18
18
  required: entry.required,
19
19
  alwaysAvailable: entry.alwaysAvailable,
20
+ activateAtInit: entry.activateAtInit,
21
+ parameters: entry.parameters,
20
22
  }));
21
23
  if (last?.renderVersion === renderVersion &&
22
24
  structurallyEqual(last.tools, tools)) {
@@ -19,13 +19,10 @@ export function isValidToolbarItemShape(value) {
19
19
  return false;
20
20
  if (item.disabled !== undefined && typeof item.disabled !== "boolean")
21
21
  return false;
22
+ // A link or a button: exactly one of `href` and `onClick`.
22
23
  const hasHref = typeof item.href === "string";
23
24
  const hasOnClick = typeof item.onClick === "function";
24
- if (hasHref === hasOnClick)
25
- return false;
26
- if (hasHref)
27
- return true;
28
- return hasOnClick;
25
+ return hasHref !== hasOnClick;
29
26
  }
30
27
  export function isInlineSvgIcon(icon) {
31
28
  return typeof icon === "string" && icon.trimStart().startsWith("<svg");
@@ -76,16 +76,16 @@ export interface ToolProvidersConfig {
76
76
  }
77
77
  /**
78
78
  * PNP/profile enforcement mode, set as `tools.pnpEnforcement` (on a section
79
- * player, `runtime.tools.pnpEnforcement`).
79
+ * player, `runtime.tools.pnpEnforcement`) or through `setPnpEnforcement`.
80
80
  *
81
- * - `"on"` — force enforcement (engine applies PNP/profile gates).
82
- * - `"off"` — opt out (engine ignores PNP/profile inputs).
83
- * - omitted / `undefined` — auto-mode (the default). The toolkit
84
- * coordinator computes the effective mode from PNP/profile policy material on the
85
- * bound `AssessmentEntity` / `AssessmentItemRef`. See
81
+ * - `"on"` — toolbar decisions apply PNP/profile policy.
82
+ * - `"off"` — toolbar decisions skip it. Feature decisions read it either way.
83
+ * - omitted — auto-mode (the default): on when the bound assessment carries
84
+ * PNP/profile policy material, or, for a decision scoped to an item, when
85
+ * that item's settings require or restrict a tool. See
86
86
  * `resolveDefaultPnpEnforcement` in `policy/core/pnp-policy-inputs.ts`.
87
87
  */
88
- export type ToolsPnpEnforcement = "on" | "off";
88
+ export type PnpEnforcementMode = "on" | "off";
89
89
  export interface CanonicalToolsConfig {
90
90
  policy: ToolPolicyConfig;
91
91
  placement: Required<ToolPlacementConfig>;
@@ -93,9 +93,9 @@ export interface CanonicalToolsConfig {
93
93
  /**
94
94
  * Optional PNP/profile enforcement override, read by
95
95
  * `<pie-assessment-toolkit>` and `ToolkitCoordinator`. See
96
- * {@link ToolsPnpEnforcement} for semantics.
96
+ * {@link PnpEnforcementMode} for semantics.
97
97
  */
98
- pnpEnforcement?: ToolsPnpEnforcement;
98
+ pnpEnforcement?: PnpEnforcementMode;
99
99
  }
100
100
  /**
101
101
  * The tools config a host supplies. Every part is optional, placement levels
@@ -104,6 +104,15 @@ export interface CanonicalToolsConfig {
104
104
  export type ToolsConfigInput = Omit<Partial<CanonicalToolsConfig>, "placement"> & {
105
105
  placement?: ToolPlacementConfig;
106
106
  };
107
+ /**
108
+ * Tool ids trimmed, with empty ones dropped and duplicates removed in
109
+ * first-seen order. Throws on a non-string entry.
110
+ */
107
111
  export declare function normalizeToolList(toolIds: string[] | undefined | null): string[];
108
112
  export declare function parseToolList(input: string | undefined | null): string[];
113
+ /**
114
+ * Check a host's tools config shape, throwing at the first malformed field, and
115
+ * return it canonical: tool lists trimmed and deduplicated, and every placement
116
+ * level that names no tool filled with its default.
117
+ */
109
118
  export declare function normalizeToolsConfig(input?: ToolsConfigInput | null): CanonicalToolsConfig;