@pie-players/pie-assessment-toolkit 0.3.73 → 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.
- package/README.md +469 -318
- package/dist/attempt/AssessmentSession.d.ts +1 -5
- package/dist/attempt/AssessmentSession.js +0 -29
- package/dist/attempt/TestSession.d.ts +1 -20
- package/dist/attempt/TestSession.js +8 -99
- package/dist/components/ItemScope.custom-element.d.ts +1 -0
- package/dist/components/ItemScope.custom-element.js +1 -0
- package/dist/components/ItemToolBar.custom-element.d.ts +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
- package/dist/components/PieAssessmentToolkit.custom-element.js +13 -13
- package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
- package/dist/components/chunks/ItemToolBar-97hx60z4.js +2 -0
- package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
- package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
- package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
- package/dist/components/item-scope-element.d.ts +2 -0
- package/dist/components/item-scope-element.js +2 -0
- package/dist/context/assessment-toolkit-context.d.ts +32 -6
- package/dist/index.d.ts +23 -48
- package/dist/index.js +12 -37
- package/dist/policy/core/PolicySource.d.ts +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +85 -34
- package/dist/policy/core/ToolPolicyEngine.js +148 -46
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +75 -54
- package/dist/policy/core/decision-types.d.ts +65 -34
- package/dist/policy/core/feature-decision.d.ts +16 -9
- package/dist/policy/core/feature-decision.js +14 -8
- package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
- package/dist/policy/core/pnp-policy-inputs.js +28 -56
- package/dist/policy/core/provenance.d.ts +9 -4
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
- package/dist/policy/sources/PnpPolicySource.js +117 -118
- package/dist/runtime/SectionControllerBinding.d.ts +279 -0
- package/dist/runtime/SectionControllerBinding.js +450 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
- package/dist/runtime/SectionRuntimeEngine.js +30 -372
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
- package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
- package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
- package/dist/runtime/adapter/dom-event-bridge.js +17 -44
- package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
- package/dist/runtime/adapter/subscriber-fanout.js +1 -2
- package/dist/runtime/catalog-context.d.ts +19 -0
- package/dist/runtime/catalog-context.js +28 -0
- package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
- package/dist/runtime/composition-emit-scheduler.js +9 -15
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +17 -0
- package/dist/runtime/content-region.js +29 -0
- package/dist/runtime/content-registration.d.ts +13 -0
- package/dist/runtime/content-registration.js +25 -0
- package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
- package/dist/runtime/core/SectionEngineCore.js +2 -2
- package/dist/runtime/core/cohort.d.ts +1 -1
- package/dist/runtime/core/cohort.js +1 -1
- package/dist/runtime/core/engine-input.d.ts +18 -30
- package/dist/runtime/core/engine-input.js +11 -12
- package/dist/runtime/core/engine-output.d.ts +16 -40
- package/dist/runtime/core/engine-output.js +9 -21
- package/dist/runtime/core/engine-readiness.d.ts +5 -37
- package/dist/runtime/core/engine-readiness.js +8 -41
- package/dist/runtime/core/engine-resolver.d.ts +46 -74
- package/dist/runtime/core/engine-resolver.js +18 -43
- package/dist/runtime/core/engine-state.d.ts +28 -36
- package/dist/runtime/core/engine-state.js +18 -21
- package/dist/runtime/core/engine-transition.d.ts +14 -19
- package/dist/runtime/core/engine-transition.js +71 -51
- package/dist/runtime/engine.d.ts +16 -19
- package/dist/runtime/engine.js +12 -19
- package/dist/runtime/page-warnings.d.ts +13 -0
- package/dist/runtime/page-warnings.js +22 -0
- package/dist/runtime/policy-input-forwarding.d.ts +25 -0
- package/dist/runtime/policy-input-forwarding.js +25 -0
- package/dist/runtime/registration-events.d.ts +14 -0
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/shell-event-bridge.d.ts +45 -0
- package/dist/runtime/shell-event-bridge.js +127 -0
- package/dist/runtime/shell-registration.d.ts +73 -0
- package/dist/runtime/shell-registration.js +95 -0
- package/dist/runtime/shell-scope.d.ts +68 -0
- package/dist/runtime/shell-scope.js +188 -0
- package/dist/runtime/tool-host-contract.d.ts +28 -10
- package/dist/runtime/tool-host-contract.js +31 -10
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/ElementToolStateStore.d.ts +14 -10
- package/dist/services/ElementToolStateStore.js +37 -13
- package/dist/services/HighlightCoordinator.d.ts +22 -28
- package/dist/services/HighlightCoordinator.js +431 -327
- package/dist/services/RangeSerializer.d.ts +10 -1
- package/dist/services/RangeSerializer.js +87 -14
- package/dist/services/TTSService.d.ts +130 -69
- package/dist/services/TTSService.js +713 -570
- package/dist/services/ToolCoordinator.d.ts +43 -66
- package/dist/services/ToolCoordinator.js +164 -208
- package/dist/services/ToolRegistry.d.ts +78 -54
- package/dist/services/ToolRegistry.js +107 -105
- package/dist/services/ToolkitCoordinator.d.ts +261 -175
- package/dist/services/ToolkitCoordinator.js +667 -433
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +11 -1
- package/dist/services/catalog-owner.js +25 -0
- package/dist/services/create-tools-config.d.ts +2 -2
- package/dist/services/framework-error-bus.d.ts +3 -6
- package/dist/services/framework-error-bus.js +3 -6
- package/dist/services/framework-error.d.ts +30 -8
- package/dist/services/framework-error.js +9 -7
- package/dist/services/interfaces.d.ts +158 -129
- package/dist/services/section-controller-types.d.ts +15 -0
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +82 -0
- package/dist/services/spoken-audio-cards.d.ts +0 -2
- package/dist/services/spoken-audio-cards.js +1 -3
- package/dist/services/tool-config-defaults.d.ts +3 -3
- package/dist/services/tool-config-defaults.js +3 -3
- package/dist/services/tool-config-validation.d.ts +14 -4
- package/dist/services/tool-config-validation.js +63 -71
- package/dist/services/tool-context.d.ts +0 -8
- package/dist/services/tool-context.js +29 -25
- package/dist/services/tool-icons.d.ts +4 -3
- package/dist/services/tool-icons.js +4 -3
- package/dist/services/tool-instance-id.d.ts +0 -1
- package/dist/services/tool-instance-id.js +0 -6
- package/dist/services/tool-providers/TTSToolProvider.d.ts +41 -80
- package/dist/services/tool-providers/TTSToolProvider.js +81 -40
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +32 -28
- package/dist/services/tool-providers/ToolProviderRegistry.js +121 -107
- package/dist/services/tool-providers/index.d.ts +0 -9
- package/dist/services/tool-providers/index.js +2 -4
- package/dist/services/tool-request.d.ts +27 -12
- package/dist/services/tool-request.js +24 -14
- package/dist/services/toolbar-decided-tools.d.ts +15 -0
- package/dist/services/toolbar-decided-tools.js +28 -0
- package/dist/services/tools-config-normalizer.d.ts +37 -23
- package/dist/services/tools-config-normalizer.js +2 -10
- package/dist/services/tts/browser-provider.d.ts +38 -2
- package/dist/services/tts/browser-provider.js +276 -140
- package/dist/services/tts/catalog-span-alignment.d.ts +2 -17
- package/dist/services/tts/catalog-span-alignment.js +17 -142
- package/dist/services/tts/flat-tree.d.ts +71 -0
- package/dist/services/tts/flat-tree.js +269 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
- package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
- package/dist/services/tts/generated-speech/types.d.ts +0 -12
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +38 -5
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
- package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
- package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
- package/dist/services/tts/math-alignment/index.d.ts +0 -5
- package/dist/services/tts/math-alignment/index.js +5 -25
- package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
- package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
- package/dist/services/tts/math-alignment/types.d.ts +4 -2
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +74 -18
- package/dist/services/tts/math-control-names.d.ts +21 -0
- package/dist/services/tts/math-control-names.js +142 -0
- package/dist/services/tts/math-speech.d.ts +16 -1
- package/dist/services/tts/math-speech.js +55 -19
- package/dist/services/tts/provider-options.d.ts +41 -0
- package/dist/services/tts/provider-options.js +1 -0
- package/dist/services/tts/sre-engine.d.ts +7 -0
- package/dist/services/tts/sre-engine.js +11 -0
- package/dist/services/tts/sre-locales.d.ts +14 -0
- package/dist/services/tts/sre-locales.js +57 -0
- package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
- package/dist/services/tts/ssml/spoken-text.js +29 -0
- package/dist/services/tts/start-failure.d.ts +11 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -1
- package/dist/services/tts/text-processing.js +46 -34
- package/dist/services/tts/text-segmentation.d.ts +7 -0
- package/dist/services/tts/text-segmentation.js +47 -0
- package/dist/services/tts-runtime-config.d.ts +44 -18
- package/dist/services/tts-runtime-config.js +22 -93
- package/dist/tools/content-capability-resolution.d.ts +7 -9
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +58 -0
- package/dist/tools/registration.js +54 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +44 -42
- package/dist/tools/tool-tag-map.d.ts +5 -15
- package/dist/tools/tool-tag-map.js +6 -20
- package/dist/utils/structural-equality.d.ts +11 -0
- package/dist/utils/structural-equality.js +47 -0
- package/package.json +24 -59
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
- package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
- package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -51
- package/dist/item-loader.d.ts +0 -44
- package/dist/item-loader.js +0 -61
- package/dist/policy/core/engine-context.d.ts +0 -29
- package/dist/policy/core/engine-context.js +0 -27
- package/dist/policy/core/policy-source-tag.d.ts +0 -31
- package/dist/policy/core/policy-source-tag.js +0 -28
- package/dist/policy/internal.d.ts +0 -25
- package/dist/policy/internal.js +0 -25
- package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
- package/dist/runtime/adapter/coordinator-bridge.js +0 -126
- package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
- package/dist/runtime/adapter/framework-error-bridge.js +0 -46
- package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
- package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
- package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
- package/dist/runtime/core/engine-stage-derivation.js +0 -29
- package/dist/runtime/engine-context.d.ts +0 -39
- package/dist/runtime/engine-context.js +0 -31
- package/dist/runtime/internal.d.ts +0 -43
- package/dist/runtime/internal.js +0 -45
- package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -80
- package/dist/runtime/section-runtime-engine-host-context.js +0 -95
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/I18nService.d.ts +0 -47
- package/dist/services/I18nService.js +0 -84
- package/dist/services/ThemeProvider.d.ts +0 -95
- package/dist/services/ThemeProvider.js +0 -334
- package/dist/services/ToolConfigResolver.d.ts +0 -161
- package/dist/services/ToolConfigResolver.js +0 -229
- package/dist/services/catalog-media.d.ts +0 -56
- package/dist/services/catalog-media.js +0 -190
- package/dist/services/createDefaultToolRegistry.d.ts +0 -53
- package/dist/services/createDefaultToolRegistry.js +0 -45
- package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
- package/dist/services/defaultPersonalNeedsProfile.js +0 -26
- package/dist/services/pnp-standard-features.d.ts +0 -238
- package/dist/services/pnp-standard-features.js +0 -338
- package/dist/services/tool-providers/CortexToolProvider.d.ts +0 -13
- package/dist/services/tool-providers/CortexToolProvider.js +0 -32
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
- package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
- package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
- package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
- package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
- package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
- package/dist/services/tts/visual-line-ranges.d.ts +0 -18
- package/dist/services/tts/visual-line-ranges.js +0 -226
- package/dist/tools/client.d.ts +0 -12
- package/dist/tools/client.js +0 -14
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -10
- package/dist/tools/internal.d.ts +0 -43
- package/dist/tools/internal.js +0 -41
- package/dist/tools/response-discovery.d.ts +0 -61
- package/dist/tools/response-discovery.js +0 -113
- package/dist/tools/types.d.ts +0 -337
- package/dist/tools/types.js +0 -12
- package/dist/tools/variant-resolver.d.ts +0 -41
- package/dist/tools/variant-resolver.js +0 -207
|
@@ -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,25 +16,27 @@
|
|
|
13
16
|
* - Registration
|
|
14
17
|
* - Lazy initialization
|
|
15
18
|
* - Auth credential fetching
|
|
16
|
-
* - Provider lookup by
|
|
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('
|
|
24
|
-
* provider: new
|
|
28
|
+
* registry.register('calculator', {
|
|
29
|
+
* provider: new ExampleToolProvider(),
|
|
25
30
|
* config: {},
|
|
26
31
|
* lazy: true,
|
|
27
32
|
* authFetcher: async () => {
|
|
28
|
-
* const response = await fetch('/api/
|
|
33
|
+
* const response = await fetch('/api/example/token');
|
|
29
34
|
* return response.json();
|
|
30
35
|
* },
|
|
31
36
|
* });
|
|
32
37
|
*
|
|
33
38
|
* // Get provider (auto-initializes if lazy)
|
|
34
|
-
* const
|
|
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
|
|
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(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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(
|
|
71
|
-
console.error(`[ToolProviderRegistry] Failed to initialize provider "${
|
|
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
|
|
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(
|
|
101
|
+
async initialize(toolId) {
|
|
86
102
|
// Already initialized
|
|
87
|
-
if (this.initialized.get(
|
|
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(
|
|
107
|
+
const existingPromise = this.initializationPromises.get(toolId);
|
|
92
108
|
if (existingPromise) {
|
|
93
109
|
return existingPromise;
|
|
94
110
|
}
|
|
95
111
|
// Start new initialization
|
|
96
|
-
const
|
|
97
|
-
this.
|
|
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.
|
|
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(
|
|
109
|
-
const provider = this.providers.get(
|
|
110
|
-
const config = this.configs.get(
|
|
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 "${
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
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
|
|
143
|
-
providerId,
|
|
171
|
+
toolId,
|
|
144
172
|
backend: deriveBackend(providerConfig),
|
|
145
173
|
operation: "auth-fetch",
|
|
146
174
|
});
|
|
147
|
-
|
|
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
|
|
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
|
|
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 "${
|
|
170
|
-
throw new Error(`Failed to fetch auth credentials for provider "${
|
|
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.
|
|
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
|
|
179
|
-
providerId,
|
|
206
|
+
toolId,
|
|
180
207
|
backend: deriveBackend(providerConfig),
|
|
181
208
|
operation: "provider-initialize",
|
|
182
209
|
duration: Date.now() - providerInitStartedAt,
|
|
183
210
|
});
|
|
184
|
-
|
|
211
|
+
logger.debug(`Provider "${toolId}" initialized`);
|
|
185
212
|
}
|
|
186
213
|
catch (error) {
|
|
187
214
|
await this.emitTelemetry(config, "pie-tool-init-error", {
|
|
188
|
-
toolId
|
|
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 "${
|
|
222
|
+
console.error(`[ToolProviderRegistry] Initialization failed for "${toolId}":`, error);
|
|
197
223
|
throw error;
|
|
198
224
|
}
|
|
199
225
|
}
|
|
@@ -203,44 +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
|
|
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(
|
|
212
|
-
|
|
213
|
-
providerId,
|
|
214
|
-
autoInitialize,
|
|
215
|
-
isRegistered: this.providers.has(providerId),
|
|
216
|
-
isInitialized: this.initialized.get(providerId),
|
|
217
|
-
allProviderIds: Array.from(this.providers.keys()),
|
|
218
|
-
});
|
|
219
|
-
const provider = this.providers.get(providerId);
|
|
237
|
+
async getProvider(toolId, autoInitialize = true) {
|
|
238
|
+
const provider = this.providers.get(toolId);
|
|
220
239
|
if (!provider) {
|
|
221
240
|
console.error("[ToolProviderRegistry] Provider not found:", {
|
|
222
|
-
requestedId:
|
|
241
|
+
requestedId: toolId,
|
|
223
242
|
availableProviders: Array.from(this.providers.keys()),
|
|
224
243
|
});
|
|
225
|
-
throw new Error(`[ToolProviderRegistry] Provider "${
|
|
244
|
+
throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
|
|
226
245
|
}
|
|
227
246
|
// Auto-initialize if needed
|
|
228
|
-
if (autoInitialize && !this.initialized.get(
|
|
229
|
-
|
|
230
|
-
await this.initialize(
|
|
247
|
+
if (autoInitialize && !this.initialized.get(toolId)) {
|
|
248
|
+
logger.debug("Auto-initializing provider:", toolId);
|
|
249
|
+
await this.initialize(toolId);
|
|
231
250
|
}
|
|
232
|
-
console.log("[ToolProviderRegistry] Returning provider:", {
|
|
233
|
-
providerId,
|
|
234
|
-
providerName: provider.providerName,
|
|
235
|
-
isReady: provider.isReady(),
|
|
236
|
-
});
|
|
237
251
|
return provider;
|
|
238
252
|
}
|
|
239
253
|
/**
|
|
240
|
-
*
|
|
254
|
+
* The ids of the tools whose provider is in `category`.
|
|
241
255
|
*
|
|
242
256
|
* @param category Tool category to filter by
|
|
243
|
-
* @returns Array of provider IDs in the category
|
|
244
257
|
*/
|
|
245
258
|
getProvidersByCategory(category) {
|
|
246
259
|
return Array.from(this.providers.entries())
|
|
@@ -248,9 +261,7 @@ export class ToolProviderRegistry {
|
|
|
248
261
|
.map(([id]) => id);
|
|
249
262
|
}
|
|
250
263
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
* @returns Array of all provider IDs
|
|
264
|
+
* The ids of the tools with a registered provider.
|
|
254
265
|
*/
|
|
255
266
|
getProviderIds() {
|
|
256
267
|
return Array.from(this.providers.keys());
|
|
@@ -258,42 +269,42 @@ export class ToolProviderRegistry {
|
|
|
258
269
|
/**
|
|
259
270
|
* Check if provider is registered
|
|
260
271
|
*
|
|
261
|
-
* @param
|
|
272
|
+
* @param toolId Id of the tool the provider serves
|
|
262
273
|
* @returns true if provider is registered
|
|
263
274
|
*/
|
|
264
|
-
has(
|
|
265
|
-
return this.providers.has(
|
|
275
|
+
has(toolId) {
|
|
276
|
+
return this.providers.has(toolId);
|
|
266
277
|
}
|
|
267
278
|
/**
|
|
268
279
|
* Check if provider is initialized
|
|
269
280
|
*
|
|
270
|
-
* @param
|
|
281
|
+
* @param toolId Id of the tool the provider serves
|
|
271
282
|
* @returns true if provider is initialized
|
|
272
283
|
*/
|
|
273
|
-
isInitialized(
|
|
274
|
-
return this.initialized.get(
|
|
284
|
+
isInitialized(toolId) {
|
|
285
|
+
return this.initialized.get(toolId) === true;
|
|
275
286
|
}
|
|
276
287
|
/**
|
|
277
288
|
* Check if provider is currently initializing
|
|
278
289
|
*
|
|
279
|
-
* @param
|
|
290
|
+
* @param toolId Id of the tool the provider serves
|
|
280
291
|
* @returns true if initialization in progress
|
|
281
292
|
*/
|
|
282
|
-
isInitializing(
|
|
283
|
-
return this.initializationPromises.has(
|
|
293
|
+
isInitializing(toolId) {
|
|
294
|
+
return this.initializationPromises.has(toolId);
|
|
284
295
|
}
|
|
285
296
|
/**
|
|
286
297
|
* Unregister and destroy a provider
|
|
287
298
|
*
|
|
288
299
|
* Removes the provider from the registry and calls its destroy method.
|
|
289
300
|
*
|
|
290
|
-
* @param
|
|
301
|
+
* @param toolId Tool whose provider to unregister
|
|
291
302
|
*/
|
|
292
|
-
async unregister(
|
|
293
|
-
const provider = this.providers.get(
|
|
303
|
+
async unregister(toolId) {
|
|
304
|
+
const provider = this.providers.get(toolId);
|
|
294
305
|
if (provider) {
|
|
295
306
|
// Wait for any pending initialization
|
|
296
|
-
const initPromise = this.initializationPromises.get(
|
|
307
|
+
const initPromise = this.initializationPromises.get(toolId);
|
|
297
308
|
if (initPromise) {
|
|
298
309
|
try {
|
|
299
310
|
await initPromise;
|
|
@@ -301,15 +312,18 @@ export class ToolProviderRegistry {
|
|
|
301
312
|
catch {
|
|
302
313
|
// Ignore initialization errors during unregister
|
|
303
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;
|
|
304
319
|
}
|
|
305
|
-
|
|
306
|
-
provider.destroy();
|
|
320
|
+
this.destroyProvider(toolId, provider);
|
|
307
321
|
// Remove from registry
|
|
308
|
-
this.providers.delete(
|
|
309
|
-
this.configs.delete(
|
|
310
|
-
this.initialized.delete(
|
|
311
|
-
this.initializationPromises.delete(
|
|
312
|
-
|
|
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}"`);
|
|
313
327
|
}
|
|
314
328
|
}
|
|
315
329
|
/**
|
|
@@ -318,8 +332,8 @@ export class ToolProviderRegistry {
|
|
|
318
332
|
* Unregisters and destroys all providers in the registry.
|
|
319
333
|
*/
|
|
320
334
|
async destroy() {
|
|
321
|
-
const
|
|
322
|
-
await Promise.all(
|
|
323
|
-
|
|
335
|
+
const toolIds = Array.from(this.providers.keys());
|
|
336
|
+
await Promise.all(toolIds.map((id) => this.unregister(id)));
|
|
337
|
+
logger.debug("Registry destroyed");
|
|
324
338
|
}
|
|
325
339
|
}
|
|
@@ -6,14 +6,5 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Part of PIE Assessment Toolkit.
|
|
8
8
|
*/
|
|
9
|
-
export type { ToolProviderApi, ToolCategory, ToolProviderCapabilities, } from "./ToolProviderApi.js";
|
|
10
9
|
export { ToolProviderRegistry } from "./ToolProviderRegistry.js";
|
|
11
|
-
export type { ToolProviderConfig } from "./ToolProviderRegistry.js";
|
|
12
|
-
export { DesmosToolProvider } from "./DesmosToolProvider.js";
|
|
13
|
-
export type { DesmosToolProviderConfig } from "./DesmosToolProvider.js";
|
|
14
|
-
export { CortexToolProvider } from "./CortexToolProvider.js";
|
|
15
|
-
export type { CortexToolProviderConfig } from "./CortexToolProvider.js";
|
|
16
|
-
export { GeoGebraToolProvider } from "./GeoGebraToolProvider.js";
|
|
17
|
-
export type { GeoGebraToolProviderConfig } from "./GeoGebraToolProvider.js";
|
|
18
10
|
export { TTSToolProvider } from "./TTSToolProvider.js";
|
|
19
|
-
export type { TTSToolProviderConfig, TTSBackend, } from "./TTSToolProvider.js";
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
// Registry
|
|
10
10
|
export { ToolProviderRegistry } from "./ToolProviderRegistry.js";
|
|
11
|
-
// Concrete providers
|
|
12
|
-
|
|
13
|
-
export { CortexToolProvider } from "./CortexToolProvider.js";
|
|
14
|
-
export { GeoGebraToolProvider } from "./GeoGebraToolProvider.js";
|
|
11
|
+
// Concrete providers. The calculator adapters live in the composition layer,
|
|
12
|
+
// `@pie-players/pie-default-tool-loaders`, which owns the engine imports.
|
|
15
13
|
export { TTSToolProvider } from "./TTSToolProvider.js";
|
|
@@ -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
|
|
46
|
-
*
|
|
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
|
|
90
|
-
* live, so a caller re-asking `canRequest` gets the current answer. A
|
|
91
|
-
* that needs to notice a policy change should also follow the policy
|
|
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
|
-
|
|
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
|
}
|
|
@@ -32,11 +32,11 @@ export class ToolRequestRegistry {
|
|
|
32
32
|
changeListeners = new Set();
|
|
33
33
|
registerTarget(target) {
|
|
34
34
|
this.targets.add(target);
|
|
35
|
-
this.
|
|
35
|
+
this.notifyTargetsChange();
|
|
36
36
|
return () => {
|
|
37
37
|
if (!this.targets.delete(target))
|
|
38
38
|
return;
|
|
39
|
-
this.
|
|
39
|
+
this.notifyTargetsChange();
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
@@ -46,12 +46,12 @@ export class ToolRequestRegistry {
|
|
|
46
46
|
* nothing is worse than an absent one, and availability moves with policy —
|
|
47
47
|
* hence {@link onTargetsChange}.
|
|
48
48
|
*/
|
|
49
|
-
canRequest(toolId, level) {
|
|
50
|
-
return this.findTarget(toolId, level) !== null;
|
|
49
|
+
canRequest(toolId, level, scopeId) {
|
|
50
|
+
return this.findTarget(toolId, level, scopeId) !== null;
|
|
51
51
|
}
|
|
52
52
|
/** Returns whether a target claimed the request. */
|
|
53
53
|
request(request) {
|
|
54
|
-
const target = this.findTarget(request.toolId, request.level);
|
|
54
|
+
const target = this.findTarget(request.toolId, request.level, request.scopeId);
|
|
55
55
|
if (!target)
|
|
56
56
|
return false;
|
|
57
57
|
try {
|
|
@@ -64,12 +64,13 @@ export class ToolRequestRegistry {
|
|
|
64
64
|
return true;
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
* Fires when a toolbar registers or unregisters
|
|
67
|
+
* Fires when a toolbar registers or unregisters, and on
|
|
68
|
+
* {@link notifyTargetsChange}.
|
|
68
69
|
*
|
|
69
|
-
* Not when a registered toolbar's
|
|
70
|
-
* live, so a caller re-asking `canRequest` gets the current answer. A
|
|
71
|
-
* that needs to notice a policy change should also follow the policy
|
|
72
|
-
* already has.
|
|
70
|
+
* Not when a policy change moves a registered toolbar's visible set: `hostsTool`
|
|
71
|
+
* is read live, so a caller re-asking `canRequest` gets the current answer. A
|
|
72
|
+
* surface that needs to notice a policy change should also follow the policy
|
|
73
|
+
* signal it already has.
|
|
73
74
|
*/
|
|
74
75
|
onTargetsChange(listener) {
|
|
75
76
|
this.changeListeners.add(listener);
|
|
@@ -84,22 +85,26 @@ export class ToolRequestRegistry {
|
|
|
84
85
|
* and still have a section-scoped gateway reach it. Requesting a level explicitly
|
|
85
86
|
* does not fall back, because a requester that named one meant it.
|
|
86
87
|
*/
|
|
87
|
-
findTarget(toolId, level) {
|
|
88
|
-
const preferred = this.findTargetAtLevel(toolId, level ?? DEFAULT_TOOL_REQUEST_LEVEL);
|
|
88
|
+
findTarget(toolId, level, scopeId) {
|
|
89
|
+
const preferred = this.findTargetAtLevel(toolId, level ?? DEFAULT_TOOL_REQUEST_LEVEL, scopeId);
|
|
89
90
|
if (preferred || level !== undefined)
|
|
90
91
|
return preferred;
|
|
91
92
|
for (const target of this.targets) {
|
|
92
93
|
if (target.level === DEFAULT_TOOL_REQUEST_LEVEL)
|
|
93
94
|
continue;
|
|
95
|
+
if (scopeId !== undefined && target.scopeId !== scopeId)
|
|
96
|
+
continue;
|
|
94
97
|
if (this.hostsTool(target, toolId))
|
|
95
98
|
return target;
|
|
96
99
|
}
|
|
97
100
|
return null;
|
|
98
101
|
}
|
|
99
|
-
findTargetAtLevel(toolId, level) {
|
|
102
|
+
findTargetAtLevel(toolId, level, scopeId) {
|
|
100
103
|
for (const target of this.targets) {
|
|
101
104
|
if (target.level !== level)
|
|
102
105
|
continue;
|
|
106
|
+
if (scopeId !== undefined && target.scopeId !== scopeId)
|
|
107
|
+
continue;
|
|
103
108
|
if (this.hostsTool(target, toolId))
|
|
104
109
|
return target;
|
|
105
110
|
}
|
|
@@ -114,7 +119,12 @@ export class ToolRequestRegistry {
|
|
|
114
119
|
return false;
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Tell change listeners that what the targets can claim changed. Registration
|
|
124
|
+
* calls it, and so does the coordinator when a target stops hosting a tool
|
|
125
|
+
* outside a policy change, as when the tool's module fails to load.
|
|
126
|
+
*/
|
|
127
|
+
notifyTargetsChange() {
|
|
118
128
|
for (const listener of this.changeListeners) {
|
|
119
129
|
try {
|
|
120
130
|
listener();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ToolPolicyDecision } from "../policy/core/decision-types.js";
|
|
2
|
+
/** What a toolbar renders from in a policy decision, per visible tool. */
|
|
3
|
+
export interface DecidedToolbarTool {
|
|
4
|
+
toolId: string;
|
|
5
|
+
required: boolean;
|
|
6
|
+
alwaysAvailable: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A toolbar's decided tools across re-decisions. The tracker returns its
|
|
10
|
+
* previous list while `renderVersion` is unchanged and the decision shows the
|
|
11
|
+
* same tools, so a re-decision that changes nothing the toolbar renders from
|
|
12
|
+
* keeps the rendered tools: rendering swaps in fresh tool elements, whose state
|
|
13
|
+
* starts over. A new `renderVersion` always yields a new list.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createDecidedToolsTracker(): (decision: ToolPolicyDecision | null, renderVersion: number) => readonly DecidedToolbarTool[] | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { structurallyEqual } from "../utils/structural-equality.js";
|
|
2
|
+
/**
|
|
3
|
+
* A toolbar's decided tools across re-decisions. The tracker returns its
|
|
4
|
+
* previous list while `renderVersion` is unchanged and the decision shows the
|
|
5
|
+
* same tools, so a re-decision that changes nothing the toolbar renders from
|
|
6
|
+
* keeps the rendered tools: rendering swaps in fresh tool elements, whose state
|
|
7
|
+
* starts over. A new `renderVersion` always yields a new list.
|
|
8
|
+
*/
|
|
9
|
+
export function createDecidedToolsTracker() {
|
|
10
|
+
let last = null;
|
|
11
|
+
return (decision, renderVersion) => {
|
|
12
|
+
if (!decision) {
|
|
13
|
+
last = null;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const tools = decision.visibleTools.map((entry) => ({
|
|
17
|
+
toolId: entry.toolId,
|
|
18
|
+
required: entry.required,
|
|
19
|
+
alwaysAvailable: entry.alwaysAvailable,
|
|
20
|
+
}));
|
|
21
|
+
if (last?.renderVersion === renderVersion &&
|
|
22
|
+
structurallyEqual(last.tools, tools)) {
|
|
23
|
+
return last.tools;
|
|
24
|
+
}
|
|
25
|
+
last = { renderVersion, tools };
|
|
26
|
+
return tools;
|
|
27
|
+
};
|
|
28
|
+
}
|