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

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 (228) hide show
  1. package/README.md +349 -313
  2. package/dist/attempt/TestSession.d.ts +1 -20
  3. package/dist/attempt/TestSession.js +8 -99
  4. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  5. package/dist/components/ItemScope.custom-element.js +1 -0
  6. package/dist/components/ItemToolBar.custom-element.js +1 -1
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
  8. package/dist/components/SectionToolBar.custom-element.js +1 -1
  9. package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
  10. package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-97hx60z4.js} +1 -1
  11. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
  12. package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
  13. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
  14. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  15. package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
  16. package/dist/components/item-scope-element.d.ts +2 -0
  17. package/dist/components/item-scope-element.js +2 -0
  18. package/dist/context/assessment-toolkit-context.d.ts +27 -6
  19. package/dist/index.d.ts +22 -47
  20. package/dist/index.js +12 -36
  21. package/dist/policy/core/PolicySource.d.ts +1 -1
  22. package/dist/policy/core/ToolPolicyEngine.d.ts +77 -32
  23. package/dist/policy/core/ToolPolicyEngine.js +136 -46
  24. package/dist/policy/core/compose-decision.d.ts +28 -7
  25. package/dist/policy/core/compose-decision.js +75 -54
  26. package/dist/policy/core/decision-types.d.ts +65 -34
  27. package/dist/policy/core/feature-decision.d.ts +16 -9
  28. package/dist/policy/core/feature-decision.js +14 -8
  29. package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
  30. package/dist/policy/core/pnp-policy-inputs.js +28 -56
  31. package/dist/policy/core/provenance.d.ts +9 -4
  32. package/dist/policy/core/provenance.js +1 -1
  33. package/dist/policy/engine.d.ts +7 -23
  34. package/dist/policy/engine.js +5 -20
  35. package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
  36. package/dist/policy/sources/PnpPolicySource.js +117 -118
  37. package/dist/runtime/SectionControllerBinding.d.ts +279 -0
  38. package/dist/runtime/SectionControllerBinding.js +450 -0
  39. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  40. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  41. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  42. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  43. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  44. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  45. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
  46. package/dist/runtime/adapter/subscriber-fanout.js +1 -2
  47. package/dist/runtime/catalog-context.d.ts +19 -0
  48. package/dist/runtime/catalog-context.js +28 -0
  49. package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
  50. package/dist/runtime/composition-emit-scheduler.js +9 -15
  51. package/dist/runtime/content-language.d.ts +32 -0
  52. package/dist/runtime/content-language.js +46 -0
  53. package/dist/runtime/content-region.d.ts +17 -0
  54. package/dist/runtime/content-region.js +29 -0
  55. package/dist/runtime/content-registration.d.ts +13 -0
  56. package/dist/runtime/content-registration.js +25 -0
  57. package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
  58. package/dist/runtime/core/SectionEngineCore.js +2 -2
  59. package/dist/runtime/core/cohort.d.ts +1 -1
  60. package/dist/runtime/core/cohort.js +1 -1
  61. package/dist/runtime/core/engine-input.d.ts +18 -30
  62. package/dist/runtime/core/engine-input.js +11 -12
  63. package/dist/runtime/core/engine-output.d.ts +16 -40
  64. package/dist/runtime/core/engine-output.js +9 -21
  65. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  66. package/dist/runtime/core/engine-readiness.js +8 -41
  67. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  68. package/dist/runtime/core/engine-resolver.js +18 -43
  69. package/dist/runtime/core/engine-state.d.ts +28 -36
  70. package/dist/runtime/core/engine-state.js +18 -21
  71. package/dist/runtime/core/engine-transition.d.ts +14 -19
  72. package/dist/runtime/core/engine-transition.js +71 -51
  73. package/dist/runtime/engine.d.ts +16 -19
  74. package/dist/runtime/engine.js +12 -19
  75. package/dist/runtime/page-warnings.d.ts +13 -0
  76. package/dist/runtime/page-warnings.js +22 -0
  77. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  78. package/dist/runtime/policy-input-forwarding.js +25 -0
  79. package/dist/runtime/registration-events.d.ts +11 -0
  80. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  81. package/dist/runtime/runtime-event-claim.js +15 -0
  82. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  83. package/dist/runtime/shell-event-bridge.js +127 -0
  84. package/dist/runtime/shell-registration.d.ts +73 -0
  85. package/dist/runtime/shell-registration.js +95 -0
  86. package/dist/runtime/shell-scope.d.ts +68 -0
  87. package/dist/runtime/shell-scope.js +188 -0
  88. package/dist/runtime/tool-host-contract.d.ts +8 -10
  89. package/dist/runtime/tool-host-contract.js +11 -11
  90. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  91. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  92. package/dist/services/ElementToolStateStore.d.ts +14 -10
  93. package/dist/services/ElementToolStateStore.js +37 -13
  94. package/dist/services/HighlightCoordinator.d.ts +21 -21
  95. package/dist/services/HighlightCoordinator.js +431 -325
  96. package/dist/services/RangeSerializer.d.ts +10 -1
  97. package/dist/services/RangeSerializer.js +87 -14
  98. package/dist/services/TTSService.d.ts +117 -70
  99. package/dist/services/TTSService.js +668 -578
  100. package/dist/services/ToolCoordinator.d.ts +43 -60
  101. package/dist/services/ToolCoordinator.js +164 -206
  102. package/dist/services/ToolRegistry.d.ts +74 -57
  103. package/dist/services/ToolRegistry.js +104 -114
  104. package/dist/services/ToolkitCoordinator.d.ts +183 -189
  105. package/dist/services/ToolkitCoordinator.js +477 -431
  106. package/dist/services/audio-handoff.d.ts +6 -7
  107. package/dist/services/audio-handoff.js +20 -14
  108. package/dist/services/catalog-owner.d.ts +0 -1
  109. package/dist/services/framework-error-bus.d.ts +3 -6
  110. package/dist/services/framework-error-bus.js +3 -6
  111. package/dist/services/framework-error.d.ts +22 -7
  112. package/dist/services/framework-error.js +9 -7
  113. package/dist/services/interfaces.d.ts +158 -129
  114. package/dist/services/section-session-assignment.d.ts +21 -0
  115. package/dist/services/section-session-assignment.js +82 -0
  116. package/dist/services/spoken-audio-cards.d.ts +0 -2
  117. package/dist/services/spoken-audio-cards.js +0 -2
  118. package/dist/services/tool-config-defaults.d.ts +3 -3
  119. package/dist/services/tool-config-defaults.js +3 -3
  120. package/dist/services/tool-config-validation.d.ts +4 -5
  121. package/dist/services/tool-config-validation.js +36 -61
  122. package/dist/services/tool-context.d.ts +0 -8
  123. package/dist/services/tool-context.js +0 -12
  124. package/dist/services/tool-icons.d.ts +4 -3
  125. package/dist/services/tool-icons.js +4 -3
  126. package/dist/services/tool-instance-id.d.ts +0 -1
  127. package/dist/services/tool-instance-id.js +0 -6
  128. package/dist/services/tool-providers/TTSToolProvider.d.ts +18 -19
  129. package/dist/services/tool-providers/TTSToolProvider.js +37 -34
  130. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
  131. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +30 -26
  132. package/dist/services/tool-providers/ToolProviderRegistry.js +119 -93
  133. package/dist/services/tool-request.d.ts +27 -12
  134. package/dist/services/tool-request.js +24 -14
  135. package/dist/services/toolbar-decided-tools.d.ts +15 -0
  136. package/dist/services/toolbar-decided-tools.js +28 -0
  137. package/dist/services/tools-config-normalizer.d.ts +17 -22
  138. package/dist/services/tools-config-normalizer.js +2 -10
  139. package/dist/services/tts/browser-provider.d.ts +38 -2
  140. package/dist/services/tts/browser-provider.js +276 -140
  141. package/dist/services/tts/flat-tree.d.ts +71 -0
  142. package/dist/services/tts/flat-tree.js +269 -0
  143. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  144. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  145. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  146. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  147. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  148. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  149. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +12 -4
  150. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  151. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  152. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  153. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  154. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  155. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  156. package/dist/services/tts/math-aware-text-processing.js +32 -18
  157. package/dist/services/tts/math-control-names.d.ts +21 -0
  158. package/dist/services/tts/math-control-names.js +142 -0
  159. package/dist/services/tts/math-speech.d.ts +11 -0
  160. package/dist/services/tts/math-speech.js +51 -18
  161. package/dist/services/tts/provider-options.d.ts +41 -0
  162. package/dist/services/tts/provider-options.js +1 -0
  163. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  164. package/dist/services/tts/ssml/spoken-text.js +29 -0
  165. package/dist/services/tts/start-failure.d.ts +11 -0
  166. package/dist/services/tts/start-failure.js +19 -0
  167. package/dist/services/tts/text-processing.d.ts +9 -0
  168. package/dist/services/tts/text-processing.js +46 -33
  169. package/dist/services/tts/text-segmentation.d.ts +7 -0
  170. package/dist/services/tts/text-segmentation.js +47 -0
  171. package/dist/services/tts-runtime-config.d.ts +25 -17
  172. package/dist/services/tts-runtime-config.js +16 -95
  173. package/dist/tools/content-capability-resolution.d.ts +7 -9
  174. package/dist/tools/content-capability-resolution.js +8 -45
  175. package/dist/tools/registration.d.ts +58 -0
  176. package/dist/tools/registration.js +54 -0
  177. package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
  178. package/dist/tools/tool-surface-host.d.ts +6 -10
  179. package/dist/tools/tool-surface-host.js +44 -42
  180. package/dist/tools/tool-tag-map.d.ts +5 -15
  181. package/dist/tools/tool-tag-map.js +6 -20
  182. package/dist/utils/structural-equality.d.ts +11 -0
  183. package/dist/utils/structural-equality.js +47 -0
  184. package/package.json +13 -29
  185. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  186. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  187. package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
  188. package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
  189. package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
  190. package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
  191. package/dist/context/runtime-context-consumer.d.ts +0 -13
  192. package/dist/context/runtime-context-consumer.js +0 -18
  193. package/dist/item-loader.d.ts +0 -44
  194. package/dist/item-loader.js +0 -61
  195. package/dist/policy/core/engine-context.d.ts +0 -29
  196. package/dist/policy/core/engine-context.js +0 -27
  197. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  198. package/dist/policy/core/policy-source-tag.js +0 -28
  199. package/dist/policy/internal.d.ts +0 -25
  200. package/dist/policy/internal.js +0 -25
  201. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  202. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  203. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  204. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  205. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  206. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  207. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  208. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  209. package/dist/runtime/engine-context.d.ts +0 -39
  210. package/dist/runtime/engine-context.js +0 -31
  211. package/dist/runtime/internal.d.ts +0 -43
  212. package/dist/runtime/internal.js +0 -45
  213. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
  214. package/dist/runtime/section-runtime-engine-host-context.js +0 -57
  215. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  216. package/dist/runtime/stage-emit-gate.js +0 -82
  217. package/dist/services/I18nService.d.ts +0 -47
  218. package/dist/services/I18nService.js +0 -84
  219. package/dist/services/ThemeProvider.d.ts +0 -95
  220. package/dist/services/ThemeProvider.js +0 -334
  221. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  222. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  223. package/dist/services/pnp-standard-features.d.ts +0 -238
  224. package/dist/services/pnp-standard-features.js +0 -338
  225. package/dist/tools/client.d.ts +0 -10
  226. package/dist/tools/client.js +0 -10
  227. package/dist/tools/internal.d.ts +0 -44
  228. package/dist/tools/internal.js +0 -39
@@ -8,13 +8,13 @@
8
8
  */
9
9
  import type { ToolProviderApi, ToolCategory } from "./ToolProviderApi.js";
10
10
  /**
11
- * Configuration for registering a tool provider
11
+ * What `register` takes: the provider, its config, and how it starts.
12
12
  */
13
- export interface ToolProviderConfig<TConfig = any> {
13
+ export interface ToolProviderRegistration<TConfig = any> {
14
14
  /**
15
15
  * Provider instance to register
16
16
  */
17
- provider: ToolProviderApi<TConfig>;
17
+ provider: ToolProviderApi;
18
18
  /**
19
19
  * Configuration for this provider
20
20
  * (may include auth credentials, endpoints, etc.)
@@ -54,14 +54,16 @@ export interface ToolProviderConfig<TConfig = any> {
54
54
  * - Registration
55
55
  * - Lazy initialization
56
56
  * - Auth credential fetching
57
- * - Provider lookup by ID or category
57
+ * - Provider lookup by tool id or category
58
+ *
59
+ * The toolkit coordinator registers each provider under its tool's id.
58
60
  *
59
61
  * @example
60
62
  * ```typescript
61
63
  * const registry = new ToolProviderRegistry();
62
64
  *
63
65
  * // Register a provider with auth fetcher
64
- * registry.register('example-provider', {
66
+ * registry.register('calculator', {
65
67
  * provider: new ExampleToolProvider(),
66
68
  * config: {},
67
69
  * lazy: true,
@@ -72,7 +74,7 @@ export interface ToolProviderConfig<TConfig = any> {
72
74
  * });
73
75
  *
74
76
  * // Get provider (auto-initializes if lazy)
75
- * const provider = await registry.getProvider('example-provider');
77
+ * const provider = await registry.getProvider('calculator');
76
78
  * ```
77
79
  */
78
80
  export declare class ToolProviderRegistry {
@@ -85,23 +87,28 @@ export declare class ToolProviderRegistry {
85
87
  * Register a tool provider
86
88
  *
87
89
  * Adds a provider to the registry. If lazy is false, initializes immediately.
90
+ * A registration under an id already registered replaces it: the replaced
91
+ * provider is destroyed once a start in progress settles, and a caller
92
+ * waiting on that start gets the replacement.
88
93
  *
89
- * @param providerId Unique provider identifier
94
+ * @param toolId Id of the tool the provider serves
90
95
  * @param config Provider configuration
91
- * @throws Error if provider with same ID already registered
92
96
  */
93
- register(providerId: string, config: ToolProviderConfig): void;
97
+ register(toolId: string, config: ToolProviderRegistration): void;
94
98
  /**
95
99
  * Initialize a provider
96
100
  *
97
101
  * Fetches auth if needed and initializes the provider.
98
102
  * Safe to call multiple times - subsequent calls wait for first initialization.
99
103
  *
100
- * @param providerId Provider to initialize
104
+ * @param toolId Tool whose provider to initialize
101
105
  * @returns Promise that resolves when initialization complete
102
106
  * @throws Error if provider not registered or initialization fails
103
107
  */
104
- initialize(providerId: string): Promise<void>;
108
+ initialize(toolId: string): Promise<void>;
109
+ /** Whether a registration replaced `provider` under `toolId`. */
110
+ private isReplaced;
111
+ private destroyProvider;
105
112
  /**
106
113
  * Internal initialization logic
107
114
  */
@@ -112,54 +119,51 @@ export declare class ToolProviderRegistry {
112
119
  * Retrieves a registered provider. If autoInitialize is true and provider
113
120
  * is not initialized, initializes it first.
114
121
  *
115
- * @param providerId Provider identifier
122
+ * @param toolId Id of the tool the provider serves
116
123
  * @param autoInitialize Auto-initialize if not ready (default: true)
117
124
  * @returns Provider instance
118
125
  * @throws Error if provider not registered
119
126
  */
120
- getProvider<T extends ToolProviderApi = ToolProviderApi>(providerId: string, autoInitialize?: boolean): Promise<T>;
127
+ getProvider<T extends ToolProviderApi = ToolProviderApi>(toolId: string, autoInitialize?: boolean): Promise<T>;
121
128
  /**
122
- * Get all providers in a category
129
+ * The ids of the tools whose provider is in `category`.
123
130
  *
124
131
  * @param category Tool category to filter by
125
- * @returns Array of provider IDs in the category
126
132
  */
127
133
  getProvidersByCategory(category: ToolCategory): string[];
128
134
  /**
129
- * Get all registered provider IDs
130
- *
131
- * @returns Array of all provider IDs
135
+ * The ids of the tools with a registered provider.
132
136
  */
133
137
  getProviderIds(): string[];
134
138
  /**
135
139
  * Check if provider is registered
136
140
  *
137
- * @param providerId Provider identifier
141
+ * @param toolId Id of the tool the provider serves
138
142
  * @returns true if provider is registered
139
143
  */
140
- has(providerId: string): boolean;
144
+ has(toolId: string): boolean;
141
145
  /**
142
146
  * Check if provider is initialized
143
147
  *
144
- * @param providerId Provider identifier
148
+ * @param toolId Id of the tool the provider serves
145
149
  * @returns true if provider is initialized
146
150
  */
147
- isInitialized(providerId: string): boolean;
151
+ isInitialized(toolId: string): boolean;
148
152
  /**
149
153
  * Check if provider is currently initializing
150
154
  *
151
- * @param providerId Provider identifier
155
+ * @param toolId Id of the tool the provider serves
152
156
  * @returns true if initialization in progress
153
157
  */
154
- isInitializing(providerId: string): boolean;
158
+ isInitializing(toolId: string): boolean;
155
159
  /**
156
160
  * Unregister and destroy a provider
157
161
  *
158
162
  * Removes the provider from the registry and calls its destroy method.
159
163
  *
160
- * @param providerId Provider to unregister
164
+ * @param toolId Tool whose provider to unregister
161
165
  */
162
- unregister(providerId: string): Promise<void>;
166
+ unregister(toolId: string): Promise<void>;
163
167
  /**
164
168
  * Clean up all providers
165
169
  *
@@ -6,6 +6,9 @@
6
6
  *
7
7
  * Part of PIE Assessment Toolkit.
8
8
  */
9
+ import { createPieLogger, isGlobalDebugEnabled, } from "@pie-players/pie-players-shared/pie";
10
+ // Lifecycle lines need `window.PIE_DEBUG = true`, read on each line.
11
+ const logger = createPieLogger("ToolProviderRegistry", isGlobalDebugEnabled);
9
12
  /**
10
13
  * Tool Provider Registry
11
14
  *
@@ -13,14 +16,16 @@
13
16
  * - Registration
14
17
  * - Lazy initialization
15
18
  * - Auth credential fetching
16
- * - Provider lookup by ID or category
19
+ * - Provider lookup by tool id or category
20
+ *
21
+ * The toolkit coordinator registers each provider under its tool's id.
17
22
  *
18
23
  * @example
19
24
  * ```typescript
20
25
  * const registry = new ToolProviderRegistry();
21
26
  *
22
27
  * // Register a provider with auth fetcher
23
- * registry.register('example-provider', {
28
+ * registry.register('calculator', {
24
29
  * provider: new ExampleToolProvider(),
25
30
  * config: {},
26
31
  * lazy: true,
@@ -31,7 +36,7 @@
31
36
  * });
32
37
  *
33
38
  * // Get provider (auto-initializes if lazy)
34
- * const provider = await registry.getProvider('example-provider');
39
+ * const provider = await registry.getProvider('calculator');
35
40
  * ```
36
41
  */
37
42
  export class ToolProviderRegistry {
@@ -51,24 +56,35 @@ export class ToolProviderRegistry {
51
56
  * Register a tool provider
52
57
  *
53
58
  * Adds a provider to the registry. If lazy is false, initializes immediately.
59
+ * A registration under an id already registered replaces it: the replaced
60
+ * provider is destroyed once a start in progress settles, and a caller
61
+ * waiting on that start gets the replacement.
54
62
  *
55
- * @param providerId Unique provider identifier
63
+ * @param toolId Id of the tool the provider serves
56
64
  * @param config Provider configuration
57
- * @throws Error if provider with same ID already registered
58
65
  */
59
- register(providerId, config) {
60
- if (this.providers.has(providerId)) {
61
- console.warn(`[ToolProviderRegistry] Provider "${providerId}" already registered, skipping`);
62
- return;
66
+ register(toolId, config) {
67
+ const replaced = this.providers.get(toolId);
68
+ const replacedStart = this.initializationPromises.get(toolId);
69
+ this.providers.set(toolId, config.provider);
70
+ this.configs.set(toolId, config);
71
+ this.initialized.set(toolId, false);
72
+ this.initializationPromises.delete(toolId);
73
+ if (replaced) {
74
+ if (replacedStart) {
75
+ void replacedStart
76
+ .catch(() => { })
77
+ .then(() => this.destroyProvider(toolId, replaced));
78
+ }
79
+ else {
80
+ this.destroyProvider(toolId, replaced);
81
+ }
63
82
  }
64
- this.providers.set(providerId, config.provider);
65
- this.configs.set(providerId, config);
66
- this.initialized.set(providerId, false);
67
- console.log(`[ToolProviderRegistry] Registered provider "${providerId}" (${config.provider.providerName})`);
83
+ logger.debug(`${replaced ? "Replaced" : "Registered"} provider "${toolId}" (${config.provider.providerName})`);
68
84
  // Initialize immediately if not lazy
69
85
  if (config.lazy === false) {
70
- this.initialize(providerId).catch((error) => {
71
- console.error(`[ToolProviderRegistry] Failed to initialize provider "${providerId}":`, error);
86
+ this.initialize(toolId).catch((error) => {
87
+ console.error(`[ToolProviderRegistry] Failed to initialize provider "${toolId}":`, error);
72
88
  });
73
89
  }
74
90
  }
@@ -78,60 +94,73 @@ export class ToolProviderRegistry {
78
94
  * Fetches auth if needed and initializes the provider.
79
95
  * Safe to call multiple times - subsequent calls wait for first initialization.
80
96
  *
81
- * @param providerId Provider to initialize
97
+ * @param toolId Tool whose provider to initialize
82
98
  * @returns Promise that resolves when initialization complete
83
99
  * @throws Error if provider not registered or initialization fails
84
100
  */
85
- async initialize(providerId) {
101
+ async initialize(toolId) {
86
102
  // Already initialized
87
- if (this.initialized.get(providerId)) {
103
+ if (this.initialized.get(toolId)) {
88
104
  return;
89
105
  }
90
106
  // Initialization in progress - wait for it
91
- const existingPromise = this.initializationPromises.get(providerId);
107
+ const existingPromise = this.initializationPromises.get(toolId);
92
108
  if (existingPromise) {
93
109
  return existingPromise;
94
110
  }
95
111
  // Start new initialization
96
- const initPromise = this._doInitialize(providerId);
97
- this.initializationPromises.set(providerId, initPromise);
112
+ const provider = this.providers.get(toolId);
113
+ const initPromise = this._doInitialize(toolId);
114
+ this.initializationPromises.set(toolId, initPromise);
98
115
  try {
99
116
  await initPromise;
100
117
  }
118
+ catch (error) {
119
+ if (!this.isReplaced(toolId, provider))
120
+ throw error;
121
+ }
101
122
  finally {
102
- this.initializationPromises.delete(providerId);
123
+ if (this.initializationPromises.get(toolId) === initPromise) {
124
+ this.initializationPromises.delete(toolId);
125
+ }
126
+ }
127
+ if (this.isReplaced(toolId, provider)) {
128
+ return this.initialize(toolId);
129
+ }
130
+ }
131
+ /** Whether a registration replaced `provider` under `toolId`. */
132
+ isReplaced(toolId, provider) {
133
+ const current = this.providers.get(toolId);
134
+ return current !== undefined && current !== provider;
135
+ }
136
+ destroyProvider(toolId, provider) {
137
+ try {
138
+ provider.destroy();
139
+ }
140
+ catch (error) {
141
+ console.warn(`[ToolProviderRegistry] Failed to destroy provider "${toolId}":`, error);
103
142
  }
104
143
  }
105
144
  /**
106
145
  * Internal initialization logic
107
146
  */
108
- async _doInitialize(providerId) {
109
- const provider = this.providers.get(providerId);
110
- const config = this.configs.get(providerId);
147
+ async _doInitialize(toolId) {
148
+ const provider = this.providers.get(toolId);
149
+ const config = this.configs.get(toolId);
111
150
  if (!provider || !config) {
112
- throw new Error(`[ToolProviderRegistry] Provider "${providerId}" not registered`);
151
+ throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
113
152
  }
114
153
  // Start with base config
115
154
  let providerConfig = { ...config.config };
116
155
  const deriveBackend = (value) => {
117
- if (!value || typeof value !== "object")
118
- return "unknown";
119
- const typed = value;
120
- const direct = typeof typed.backend === "string"
121
- ? typed.backend
122
- : typeof typed.provider === "string"
123
- ? typed.provider
124
- : typeof typed.serverProvider === "string"
125
- ? typed.serverProvider
126
- : typeof typed.providerId === "string"
127
- ? typed.providerId
128
- : "";
129
- return direct || "unknown";
156
+ const backend = value && typeof value === "object"
157
+ ? value.backend
158
+ : undefined;
159
+ return typeof backend === "string" && backend ? backend : "unknown";
130
160
  };
131
161
  const providerInitStartedAt = Date.now();
132
162
  await this.emitTelemetry(config, "pie-tool-init-start", {
133
- toolId: providerId,
134
- providerId,
163
+ toolId,
135
164
  backend: deriveBackend(providerConfig),
136
165
  operation: "provider-initialize",
137
166
  });
@@ -139,18 +168,16 @@ export class ToolProviderRegistry {
139
168
  if (provider.requiresAuth && config.authFetcher) {
140
169
  const authFetchStartedAt = Date.now();
141
170
  await this.emitTelemetry(config, "pie-tool-backend-call-start", {
142
- toolId: providerId,
143
- providerId,
171
+ toolId,
144
172
  backend: deriveBackend(providerConfig),
145
173
  operation: "auth-fetch",
146
174
  });
147
- console.log(`[ToolProviderRegistry] Fetching auth for "${providerId}"...`);
175
+ logger.debug(`Fetching auth for "${toolId}"...`);
148
176
  try {
149
177
  const authData = await config.authFetcher();
150
178
  providerConfig = { ...providerConfig, ...authData };
151
179
  await this.emitTelemetry(config, "pie-tool-backend-call-success", {
152
- toolId: providerId,
153
- providerId,
180
+ toolId,
154
181
  backend: deriveBackend(providerConfig),
155
182
  operation: "auth-fetch",
156
183
  duration: Date.now() - authFetchStartedAt,
@@ -158,42 +185,41 @@ export class ToolProviderRegistry {
158
185
  }
159
186
  catch (error) {
160
187
  await this.emitTelemetry(config, "pie-tool-backend-call-error", {
161
- toolId: providerId,
162
- providerId,
188
+ toolId,
163
189
  backend: deriveBackend(providerConfig),
164
190
  operation: "auth-fetch",
165
191
  duration: Date.now() - authFetchStartedAt,
166
192
  errorType: "ProviderAuthFetchError",
167
193
  message: error instanceof Error ? error.message : String(error),
168
194
  });
169
- console.error(`[ToolProviderRegistry] Auth fetch failed for "${providerId}":`, error);
170
- throw new Error(`Failed to fetch auth credentials for provider "${providerId}"`);
195
+ console.error(`[ToolProviderRegistry] Auth fetch failed for "${toolId}":`, error);
196
+ throw new Error(`Failed to fetch auth credentials for provider "${toolId}"`);
171
197
  }
172
198
  }
173
199
  // Initialize provider
174
200
  try {
175
201
  await provider.initialize(providerConfig);
176
- this.initialized.set(providerId, true);
202
+ if (this.providers.get(toolId) === provider) {
203
+ this.initialized.set(toolId, true);
204
+ }
177
205
  await this.emitTelemetry(config, "pie-tool-init-success", {
178
- toolId: providerId,
179
- providerId,
206
+ toolId,
180
207
  backend: deriveBackend(providerConfig),
181
208
  operation: "provider-initialize",
182
209
  duration: Date.now() - providerInitStartedAt,
183
210
  });
184
- console.log(`[ToolProviderRegistry] Provider "${providerId}" initialized`);
211
+ logger.debug(`Provider "${toolId}" initialized`);
185
212
  }
186
213
  catch (error) {
187
214
  await this.emitTelemetry(config, "pie-tool-init-error", {
188
- toolId: providerId,
189
- providerId,
215
+ toolId,
190
216
  backend: deriveBackend(providerConfig),
191
217
  operation: "provider-initialize",
192
218
  duration: Date.now() - providerInitStartedAt,
193
219
  errorType: "ProviderInitializationError",
194
220
  message: error instanceof Error ? error.message : String(error),
195
221
  });
196
- console.error(`[ToolProviderRegistry] Initialization failed for "${providerId}":`, error);
222
+ console.error(`[ToolProviderRegistry] Initialization failed for "${toolId}":`, error);
197
223
  throw error;
198
224
  }
199
225
  }
@@ -203,32 +229,31 @@ export class ToolProviderRegistry {
203
229
  * Retrieves a registered provider. If autoInitialize is true and provider
204
230
  * is not initialized, initializes it first.
205
231
  *
206
- * @param providerId Provider identifier
232
+ * @param toolId Id of the tool the provider serves
207
233
  * @param autoInitialize Auto-initialize if not ready (default: true)
208
234
  * @returns Provider instance
209
235
  * @throws Error if provider not registered
210
236
  */
211
- async getProvider(providerId, autoInitialize = true) {
212
- const provider = this.providers.get(providerId);
237
+ async getProvider(toolId, autoInitialize = true) {
238
+ const provider = this.providers.get(toolId);
213
239
  if (!provider) {
214
240
  console.error("[ToolProviderRegistry] Provider not found:", {
215
- requestedId: providerId,
241
+ requestedId: toolId,
216
242
  availableProviders: Array.from(this.providers.keys()),
217
243
  });
218
- throw new Error(`[ToolProviderRegistry] Provider "${providerId}" not registered`);
244
+ throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
219
245
  }
220
246
  // Auto-initialize if needed
221
- if (autoInitialize && !this.initialized.get(providerId)) {
222
- console.log("[ToolProviderRegistry] Auto-initializing provider:", providerId);
223
- await this.initialize(providerId);
247
+ if (autoInitialize && !this.initialized.get(toolId)) {
248
+ logger.debug("Auto-initializing provider:", toolId);
249
+ await this.initialize(toolId);
224
250
  }
225
251
  return provider;
226
252
  }
227
253
  /**
228
- * Get all providers in a category
254
+ * The ids of the tools whose provider is in `category`.
229
255
  *
230
256
  * @param category Tool category to filter by
231
- * @returns Array of provider IDs in the category
232
257
  */
233
258
  getProvidersByCategory(category) {
234
259
  return Array.from(this.providers.entries())
@@ -236,9 +261,7 @@ export class ToolProviderRegistry {
236
261
  .map(([id]) => id);
237
262
  }
238
263
  /**
239
- * Get all registered provider IDs
240
- *
241
- * @returns Array of all provider IDs
264
+ * The ids of the tools with a registered provider.
242
265
  */
243
266
  getProviderIds() {
244
267
  return Array.from(this.providers.keys());
@@ -246,42 +269,42 @@ export class ToolProviderRegistry {
246
269
  /**
247
270
  * Check if provider is registered
248
271
  *
249
- * @param providerId Provider identifier
272
+ * @param toolId Id of the tool the provider serves
250
273
  * @returns true if provider is registered
251
274
  */
252
- has(providerId) {
253
- return this.providers.has(providerId);
275
+ has(toolId) {
276
+ return this.providers.has(toolId);
254
277
  }
255
278
  /**
256
279
  * Check if provider is initialized
257
280
  *
258
- * @param providerId Provider identifier
281
+ * @param toolId Id of the tool the provider serves
259
282
  * @returns true if provider is initialized
260
283
  */
261
- isInitialized(providerId) {
262
- return this.initialized.get(providerId) === true;
284
+ isInitialized(toolId) {
285
+ return this.initialized.get(toolId) === true;
263
286
  }
264
287
  /**
265
288
  * Check if provider is currently initializing
266
289
  *
267
- * @param providerId Provider identifier
290
+ * @param toolId Id of the tool the provider serves
268
291
  * @returns true if initialization in progress
269
292
  */
270
- isInitializing(providerId) {
271
- return this.initializationPromises.has(providerId);
293
+ isInitializing(toolId) {
294
+ return this.initializationPromises.has(toolId);
272
295
  }
273
296
  /**
274
297
  * Unregister and destroy a provider
275
298
  *
276
299
  * Removes the provider from the registry and calls its destroy method.
277
300
  *
278
- * @param providerId Provider to unregister
301
+ * @param toolId Tool whose provider to unregister
279
302
  */
280
- async unregister(providerId) {
281
- const provider = this.providers.get(providerId);
303
+ async unregister(toolId) {
304
+ const provider = this.providers.get(toolId);
282
305
  if (provider) {
283
306
  // Wait for any pending initialization
284
- const initPromise = this.initializationPromises.get(providerId);
307
+ const initPromise = this.initializationPromises.get(toolId);
285
308
  if (initPromise) {
286
309
  try {
287
310
  await initPromise;
@@ -289,15 +312,18 @@ export class ToolProviderRegistry {
289
312
  catch {
290
313
  // Ignore initialization errors during unregister
291
314
  }
315
+ // A provider registered meanwhile owns the slot, and register()
316
+ // already destroys this one once its start settles.
317
+ if (this.providers.get(toolId) !== provider)
318
+ return;
292
319
  }
293
- // Destroy provider
294
- provider.destroy();
320
+ this.destroyProvider(toolId, provider);
295
321
  // Remove from registry
296
- this.providers.delete(providerId);
297
- this.configs.delete(providerId);
298
- this.initialized.delete(providerId);
299
- this.initializationPromises.delete(providerId);
300
- console.log(`[ToolProviderRegistry] Unregistered provider "${providerId}"`);
322
+ this.providers.delete(toolId);
323
+ this.configs.delete(toolId);
324
+ this.initialized.delete(toolId);
325
+ this.initializationPromises.delete(toolId);
326
+ logger.debug(`Unregistered provider "${toolId}"`);
301
327
  }
302
328
  }
303
329
  /**
@@ -306,8 +332,8 @@ export class ToolProviderRegistry {
306
332
  * Unregisters and destroys all providers in the registry.
307
333
  */
308
334
  async destroy() {
309
- const providerIds = Array.from(this.providers.keys());
310
- await Promise.all(providerIds.map((id) => this.unregister(id)));
311
- console.log("[ToolProviderRegistry] Registry destroyed");
335
+ const toolIds = Array.from(this.providers.keys());
336
+ await Promise.all(toolIds.map((id) => this.unregister(id)));
337
+ logger.debug("Registry destroyed");
312
338
  }
313
339
  }
@@ -42,17 +42,26 @@ export interface ToolOpenRequest {
42
42
  * to match a placement made elsewhere is a step it has no reason to expect.
43
43
  *
44
44
  * At `"item"` and `"passage"` a section holds one target per card, and the first
45
- * registered one that hosts the tool claims the request. A requester that needs a
46
- * particular card's instance cannot express that here, and the gateway PIE ships
47
- * does not need to: the strip is a section-scoped singleton acting on passage
48
- * selections, so the selection belongs to no card, and what opens is a floating
49
- * shell rather than anything rendered inside one.
45
+ * registered one that hosts the tool claims the request unless `scopeId` names
46
+ * the card.
50
47
  */
51
48
  level?: ToolPlacementLevel;
49
+ /**
50
+ * Scope of the toolbar that should open the tool: the item's canonical id for
51
+ * an item toolbar, the section id for a section toolbar. A constraint like
52
+ * `level`, for a requester that belongs to one card, as an inline control in an
53
+ * item does. Only a target registered with the same `scopeId` claims it.
54
+ */
55
+ scopeId?: string;
52
56
  }
53
57
  export interface ToolRequestTarget {
54
58
  /** The placement level this toolbar renders. */
55
59
  level: ToolPlacementLevel;
60
+ /**
61
+ * The scope this toolbar renders for, matched against a request's `scopeId`.
62
+ * A target without one claims only requests that name no scope.
63
+ */
64
+ readonly scopeId?: string;
56
65
  /** Whether this toolbar currently renders the tool, per its own policy pass. */
57
66
  hostsTool: (toolId: string) => boolean;
58
67
  /**
@@ -80,16 +89,17 @@ export declare class ToolRequestRegistry {
80
89
  * nothing is worse than an absent one, and availability moves with policy —
81
90
  * hence {@link onTargetsChange}.
82
91
  */
83
- canRequest(toolId: string, level?: ToolPlacementLevel): boolean;
92
+ canRequest(toolId: string, level?: ToolPlacementLevel, scopeId?: string): boolean;
84
93
  /** Returns whether a target claimed the request. */
85
94
  request(request: ToolOpenRequest): boolean;
86
95
  /**
87
- * Fires when a toolbar registers or unregisters.
96
+ * Fires when a toolbar registers or unregisters, and on
97
+ * {@link notifyTargetsChange}.
88
98
  *
89
- * Not when a registered toolbar's own visible set changes: `hostsTool` is read
90
- * live, so a caller re-asking `canRequest` gets the current answer. A surface
91
- * that needs to notice a policy change should also follow the policy signal it
92
- * already has.
99
+ * Not when a policy change moves a registered toolbar's visible set: `hostsTool`
100
+ * is read live, so a caller re-asking `canRequest` gets the current answer. A
101
+ * surface that needs to notice a policy change should also follow the policy
102
+ * signal it already has.
93
103
  */
94
104
  onTargetsChange(listener: () => void): () => void;
95
105
  /**
@@ -102,6 +112,11 @@ export declare class ToolRequestRegistry {
102
112
  private findTarget;
103
113
  private findTargetAtLevel;
104
114
  private hostsTool;
105
- private notifyChange;
115
+ /**
116
+ * Tell change listeners that what the targets can claim changed. Registration
117
+ * calls it, and so does the coordinator when a target stops hosting a tool
118
+ * outside a policy change, as when the tool's module fails to load.
119
+ */
120
+ notifyTargetsChange(): void;
106
121
  dispose(): void;
107
122
  }