@pie-players/pie-assessment-toolkit 0.3.74 → 1.0.0-beta.0
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 +658 -714
- package/custom-elements.json +1315 -0
- package/dist/attempt/TestSession.d.ts +1 -21
- package/dist/attempt/TestSession.js +16 -102
- 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.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-3zrc4p3d.js +4 -0
- package/dist/components/chunks/ItemToolBar-66hcwmfw.js +2 -0
- package/dist/components/chunks/ItemToolBar-9gqhfxtn.js +40 -0
- package/dist/components/chunks/ItemToolBar-hzc5wyjp.js +2 -0
- package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-kkj69ptr.js} +1 -1
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-bj997w4b.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 +33 -6
- package/dist/index.d.ts +25 -50
- package/dist/index.js +15 -45
- package/dist/policy/core/PolicySource.d.ts +2 -2
- package/dist/policy/core/ToolPolicyEngine.d.ts +118 -34
- package/dist/policy/core/ToolPolicyEngine.js +203 -56
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +95 -74
- package/dist/policy/core/decision-types.d.ts +98 -55
- package/dist/policy/core/feature-decision.d.ts +28 -19
- package/dist/policy/core/feature-decision.js +15 -11
- package/dist/policy/core/pnp-policy-inputs.d.ts +29 -35
- package/dist/policy/core/pnp-policy-inputs.js +40 -88
- package/dist/policy/core/provenance.d.ts +10 -5
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/core/tool-parameters.d.ts +11 -0
- package/dist/policy/core/tool-parameters.js +15 -0
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +43 -68
- package/dist/policy/sources/PnpPolicySource.js +133 -132
- package/dist/runtime/SectionControllerBinding.d.ts +283 -0
- package/dist/runtime/SectionControllerBinding.js +456 -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 -3
- package/dist/runtime/adapter/subscriber-fanout.js +2 -6
- 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 +12 -18
- package/dist/runtime/composition-emit-scheduler.js +10 -16
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +13 -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 -4
- package/dist/runtime/core/SectionEngineCore.js +2 -6
- package/dist/runtime/core/cohort.d.ts +1 -6
- package/dist/runtime/core/cohort.js +1 -10
- 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 +17 -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 +74 -53
- 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 +22 -14
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/runtime-id.d.ts +1 -1
- package/dist/runtime/runtime-id.js +1 -1
- 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 +194 -0
- package/dist/runtime/tool-host-contract.d.ts +8 -14
- package/dist/runtime/tool-host-contract.js +12 -12
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts +21 -31
- package/dist/services/AccessibilityCatalogResolver.js +29 -42
- package/dist/services/ElementToolStateStore.d.ts +42 -67
- package/dist/services/ElementToolStateStore.js +65 -71
- package/dist/services/HighlightCoordinator.d.ts +28 -40
- package/dist/services/HighlightCoordinator.js +459 -381
- package/dist/services/RangeSerializer.d.ts +11 -33
- package/dist/services/RangeSerializer.js +88 -59
- package/dist/services/SSMLExtractor.d.ts +8 -31
- package/dist/services/SSMLExtractor.js +18 -40
- package/dist/services/TTSService.d.ts +159 -119
- package/dist/services/TTSService.js +937 -822
- package/dist/services/ToolCoordinator.d.ts +42 -102
- package/dist/services/ToolCoordinator.js +178 -256
- package/dist/services/ToolRegistry.d.ts +152 -133
- package/dist/services/ToolRegistry.js +192 -178
- package/dist/services/ToolkitCoordinator.d.ts +233 -237
- package/dist/services/ToolkitCoordinator.js +615 -557
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +0 -1
- 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 +51 -13
- package/dist/services/framework-error.js +19 -20
- package/dist/services/interfaces.d.ts +189 -184
- package/dist/services/section-controller-types.d.ts +7 -7
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +80 -0
- package/dist/services/spoken-audio-cards.d.ts +2 -6
- package/dist/services/spoken-audio-cards.js +2 -6
- 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 +9 -12
- package/dist/services/tool-config-validation.js +47 -87
- package/dist/services/tool-context.d.ts +7 -31
- package/dist/services/tool-context.js +99 -65
- package/dist/services/tool-failure.d.ts +30 -0
- package/dist/services/tool-failure.js +11 -0
- package/dist/services/tool-icons.d.ts +5 -5
- package/dist/services/tool-icons.js +6 -5
- package/dist/services/tool-instance-id.d.ts +8 -7
- package/dist/services/tool-instance-id.js +12 -24
- package/dist/services/tool-providers/TTSToolProvider.d.ts +20 -58
- package/dist/services/tool-providers/TTSToolProvider.js +32 -85
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -11
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +29 -60
- package/dist/services/tool-providers/ToolProviderRegistry.js +128 -144
- package/dist/services/tool-request.d.ts +31 -12
- package/dist/services/tool-request.js +31 -16
- package/dist/services/toolbar-decided-tools.d.ts +16 -0
- package/dist/services/toolbar-decided-tools.js +29 -0
- package/dist/services/toolbar-items.js +2 -5
- package/dist/services/tools-config-normalizer.d.ts +33 -29
- package/dist/services/tools-config-normalizer.js +26 -28
- package/dist/services/tts/browser-provider.d.ts +39 -6
- package/dist/services/tts/browser-provider.js +280 -160
- package/dist/services/tts/catalog-span-alignment.js +14 -7
- package/dist/services/tts/flat-tree.d.ts +64 -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/dom/to-chunk-inputs.d.ts +2 -2
- package/dist/services/tts/generated-speech/index.d.ts +1 -1
- package/dist/services/tts/generated-speech/index.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 +1 -1
- package/dist/services/tts/generated-speech/types.js +1 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +22 -16
- 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/highlight-target-resolver.d.ts +12 -0
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +42 -47
- 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 +12 -1
- package/dist/services/tts/math-speech.js +54 -18
- package/dist/services/tts/mathml-sanitization.d.ts +7 -0
- package/dist/services/tts/mathml-sanitization.js +9 -1
- package/dist/services/tts/provider-options.d.ts +52 -0
- package/dist/services/tts/provider-options.js +1 -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 +4 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -0
- package/dist/services/tts/text-processing.js +46 -33
- package/dist/services/tts/text-segmentation.d.ts +12 -0
- package/dist/services/tts/text-segmentation.js +60 -10
- package/dist/services/tts-runtime-config.d.ts +54 -26
- package/dist/services/tts-runtime-config.js +27 -112
- package/dist/tools/content-capability-resolution.d.ts +9 -11
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +59 -0
- package/dist/tools/registration.js +55 -0
- package/dist/tools/registrations/toolbar-registration-helpers.d.ts +6 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +8 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +45 -50
- 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 +15 -29
- 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-b9dz5jvd.js +0 -40
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
- package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -18
- 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 -74
- package/dist/runtime/section-runtime-engine-host-context.js +0 -57
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/ContextVariableStore.d.ts +0 -113
- package/dist/services/ContextVariableStore.js +0 -206
- 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/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/tts-control-events.d.ts +0 -13
- package/dist/services/tts-control-events.js +0 -7
- package/dist/tools/client.d.ts +0 -10
- package/dist/tools/client.js +0 -10
- package/dist/tools/internal.d.ts +0 -44
- package/dist/tools/internal.js +0 -39
- package/dist/utils/logger.d.ts +0 -6
- package/dist/utils/logger.js +0 -10
|
@@ -3,9 +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
|
*/
|
|
7
|
+
import { createPieLogger, isGlobalDebugEnabled, } from "@pie-players/pie-players-shared/pie";
|
|
8
|
+
// Lifecycle lines need `window.PIE_DEBUG = true`, read on each line.
|
|
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
|
+
}
|
|
9
17
|
/**
|
|
10
18
|
* Tool Provider Registry
|
|
11
19
|
*
|
|
@@ -13,14 +21,16 @@
|
|
|
13
21
|
* - Registration
|
|
14
22
|
* - Lazy initialization
|
|
15
23
|
* - Auth credential fetching
|
|
16
|
-
* - Provider lookup by
|
|
24
|
+
* - Provider lookup by tool id or category
|
|
25
|
+
*
|
|
26
|
+
* The toolkit coordinator registers each provider under its tool's id.
|
|
17
27
|
*
|
|
18
28
|
* @example
|
|
19
29
|
* ```typescript
|
|
20
30
|
* const registry = new ToolProviderRegistry();
|
|
21
31
|
*
|
|
22
32
|
* // Register a provider with auth fetcher
|
|
23
|
-
* registry.register('
|
|
33
|
+
* registry.register('calculator', {
|
|
24
34
|
* provider: new ExampleToolProvider(),
|
|
25
35
|
* config: {},
|
|
26
36
|
* lazy: true,
|
|
@@ -31,7 +41,7 @@
|
|
|
31
41
|
* });
|
|
32
42
|
*
|
|
33
43
|
* // Get provider (auto-initializes if lazy)
|
|
34
|
-
* const provider = await registry.getProvider('
|
|
44
|
+
* const provider = await registry.getProvider('calculator');
|
|
35
45
|
* ```
|
|
36
46
|
*/
|
|
37
47
|
export class ToolProviderRegistry {
|
|
@@ -51,24 +61,31 @@ export class ToolProviderRegistry {
|
|
|
51
61
|
* Register a tool provider
|
|
52
62
|
*
|
|
53
63
|
* Adds a provider to the registry. If lazy is false, initializes immediately.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* @throws Error if provider with same ID already registered
|
|
64
|
+
* A registration under an id already registered replaces it: the replaced
|
|
65
|
+
* provider is destroyed once a start in progress settles, and a caller
|
|
66
|
+
* waiting on that start gets the replacement.
|
|
58
67
|
*/
|
|
59
|
-
register(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
register(toolId, config) {
|
|
69
|
+
const replaced = this.providers.get(toolId);
|
|
70
|
+
const replacedStart = this.initializationPromises.get(toolId);
|
|
71
|
+
this.providers.set(toolId, config.provider);
|
|
72
|
+
this.configs.set(toolId, config);
|
|
73
|
+
this.initialized.set(toolId, false);
|
|
74
|
+
this.initializationPromises.delete(toolId);
|
|
75
|
+
if (replaced) {
|
|
76
|
+
if (replacedStart) {
|
|
77
|
+
void replacedStart
|
|
78
|
+
.catch(() => { })
|
|
79
|
+
.then(() => this.destroyProvider(toolId, replaced));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.destroyProvider(toolId, replaced);
|
|
83
|
+
}
|
|
63
84
|
}
|
|
64
|
-
|
|
65
|
-
this.configs.set(providerId, config);
|
|
66
|
-
this.initialized.set(providerId, false);
|
|
67
|
-
console.log(`[ToolProviderRegistry] Registered provider "${providerId}" (${config.provider.providerName})`);
|
|
68
|
-
// Initialize immediately if not lazy
|
|
85
|
+
logger.debug(`${replaced ? "Replaced" : "Registered"} provider "${toolId}" (${config.provider.providerName})`);
|
|
69
86
|
if (config.lazy === false) {
|
|
70
|
-
this.initialize(
|
|
71
|
-
console.error(`[ToolProviderRegistry] Failed to initialize provider "${
|
|
87
|
+
this.initialize(toolId).catch((error) => {
|
|
88
|
+
console.error(`[ToolProviderRegistry] Failed to initialize provider "${toolId}":`, error);
|
|
72
89
|
});
|
|
73
90
|
}
|
|
74
91
|
}
|
|
@@ -77,123 +94,120 @@ export class ToolProviderRegistry {
|
|
|
77
94
|
*
|
|
78
95
|
* Fetches auth if needed and initializes the provider.
|
|
79
96
|
* Safe to call multiple times - subsequent calls wait for first initialization.
|
|
80
|
-
*
|
|
81
|
-
* @param providerId Provider to initialize
|
|
82
|
-
* @returns Promise that resolves when initialization complete
|
|
83
|
-
* @throws Error if provider not registered or initialization fails
|
|
97
|
+
* Throws when the provider is not registered or its initialization fails.
|
|
84
98
|
*/
|
|
85
|
-
async initialize(
|
|
86
|
-
|
|
87
|
-
if (this.initialized.get(providerId)) {
|
|
99
|
+
async initialize(toolId) {
|
|
100
|
+
if (this.initialized.get(toolId)) {
|
|
88
101
|
return;
|
|
89
102
|
}
|
|
90
|
-
|
|
91
|
-
const existingPromise = this.initializationPromises.get(providerId);
|
|
103
|
+
const existingPromise = this.initializationPromises.get(toolId);
|
|
92
104
|
if (existingPromise) {
|
|
93
105
|
return existingPromise;
|
|
94
106
|
}
|
|
95
|
-
|
|
96
|
-
const initPromise = this._doInitialize(
|
|
97
|
-
this.initializationPromises.set(
|
|
107
|
+
const provider = this.providers.get(toolId);
|
|
108
|
+
const initPromise = this._doInitialize(toolId);
|
|
109
|
+
this.initializationPromises.set(toolId, initPromise);
|
|
98
110
|
try {
|
|
99
111
|
await initPromise;
|
|
100
112
|
}
|
|
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.
|
|
116
|
+
if (!this.isReplaced(toolId, provider))
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
101
119
|
finally {
|
|
102
|
-
this.initializationPromises.
|
|
120
|
+
if (this.initializationPromises.get(toolId) === initPromise) {
|
|
121
|
+
this.initializationPromises.delete(toolId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (this.isReplaced(toolId, provider)) {
|
|
125
|
+
return this.initialize(toolId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Whether a registration replaced `provider` under `toolId`. */
|
|
129
|
+
isReplaced(toolId, provider) {
|
|
130
|
+
const current = this.providers.get(toolId);
|
|
131
|
+
return current !== undefined && current !== provider;
|
|
132
|
+
}
|
|
133
|
+
destroyProvider(toolId, provider) {
|
|
134
|
+
try {
|
|
135
|
+
provider.destroy();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.warn(`[ToolProviderRegistry] Failed to destroy provider "${toolId}":`, error);
|
|
103
139
|
}
|
|
104
140
|
}
|
|
105
141
|
/**
|
|
106
142
|
* Internal initialization logic
|
|
107
143
|
*/
|
|
108
|
-
async _doInitialize(
|
|
109
|
-
const provider = this.providers.get(
|
|
110
|
-
const config = this.configs.get(
|
|
144
|
+
async _doInitialize(toolId) {
|
|
145
|
+
const provider = this.providers.get(toolId);
|
|
146
|
+
const config = this.configs.get(toolId);
|
|
111
147
|
if (!provider || !config) {
|
|
112
|
-
throw new Error(`[ToolProviderRegistry] Provider "${
|
|
148
|
+
throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
|
|
113
149
|
}
|
|
114
|
-
// Start with base config
|
|
115
150
|
let providerConfig = { ...config.config };
|
|
116
|
-
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";
|
|
130
|
-
};
|
|
131
151
|
const providerInitStartedAt = Date.now();
|
|
132
152
|
await this.emitTelemetry(config, "pie-tool-init-start", {
|
|
133
|
-
toolId
|
|
134
|
-
|
|
135
|
-
backend: deriveBackend(providerConfig),
|
|
153
|
+
toolId,
|
|
154
|
+
backend: telemetryBackend(providerConfig),
|
|
136
155
|
operation: "provider-initialize",
|
|
137
156
|
});
|
|
138
|
-
// Fetch auth if needed
|
|
139
157
|
if (provider.requiresAuth && config.authFetcher) {
|
|
140
158
|
const authFetchStartedAt = Date.now();
|
|
141
159
|
await this.emitTelemetry(config, "pie-tool-backend-call-start", {
|
|
142
|
-
toolId
|
|
143
|
-
|
|
144
|
-
backend: deriveBackend(providerConfig),
|
|
160
|
+
toolId,
|
|
161
|
+
backend: telemetryBackend(providerConfig),
|
|
145
162
|
operation: "auth-fetch",
|
|
146
163
|
});
|
|
147
|
-
|
|
164
|
+
logger.debug(`Fetching auth for "${toolId}"...`);
|
|
148
165
|
try {
|
|
149
166
|
const authData = await config.authFetcher();
|
|
150
167
|
providerConfig = { ...providerConfig, ...authData };
|
|
151
168
|
await this.emitTelemetry(config, "pie-tool-backend-call-success", {
|
|
152
|
-
toolId
|
|
153
|
-
|
|
154
|
-
backend: deriveBackend(providerConfig),
|
|
169
|
+
toolId,
|
|
170
|
+
backend: telemetryBackend(providerConfig),
|
|
155
171
|
operation: "auth-fetch",
|
|
156
172
|
duration: Date.now() - authFetchStartedAt,
|
|
157
173
|
});
|
|
158
174
|
}
|
|
159
175
|
catch (error) {
|
|
160
176
|
await this.emitTelemetry(config, "pie-tool-backend-call-error", {
|
|
161
|
-
toolId
|
|
162
|
-
|
|
163
|
-
backend: deriveBackend(providerConfig),
|
|
177
|
+
toolId,
|
|
178
|
+
backend: telemetryBackend(providerConfig),
|
|
164
179
|
operation: "auth-fetch",
|
|
165
180
|
duration: Date.now() - authFetchStartedAt,
|
|
166
181
|
errorType: "ProviderAuthFetchError",
|
|
167
182
|
message: error instanceof Error ? error.message : String(error),
|
|
168
183
|
});
|
|
169
|
-
console.error(`[ToolProviderRegistry] Auth fetch failed for "${
|
|
170
|
-
throw new Error(`Failed to fetch auth credentials for provider "${
|
|
184
|
+
console.error(`[ToolProviderRegistry] Auth fetch failed for "${toolId}":`, error);
|
|
185
|
+
throw new Error(`Failed to fetch auth credentials for provider "${toolId}"`);
|
|
171
186
|
}
|
|
172
187
|
}
|
|
173
|
-
// Initialize provider
|
|
174
188
|
try {
|
|
175
189
|
await provider.initialize(providerConfig);
|
|
176
|
-
this.
|
|
190
|
+
if (this.providers.get(toolId) === provider) {
|
|
191
|
+
this.initialized.set(toolId, true);
|
|
192
|
+
}
|
|
177
193
|
await this.emitTelemetry(config, "pie-tool-init-success", {
|
|
178
|
-
toolId
|
|
179
|
-
|
|
180
|
-
backend: deriveBackend(providerConfig),
|
|
194
|
+
toolId,
|
|
195
|
+
backend: telemetryBackend(providerConfig),
|
|
181
196
|
operation: "provider-initialize",
|
|
182
197
|
duration: Date.now() - providerInitStartedAt,
|
|
183
198
|
});
|
|
184
|
-
|
|
199
|
+
logger.debug(`Provider "${toolId}" initialized`);
|
|
185
200
|
}
|
|
186
201
|
catch (error) {
|
|
187
202
|
await this.emitTelemetry(config, "pie-tool-init-error", {
|
|
188
|
-
toolId
|
|
189
|
-
|
|
190
|
-
backend: deriveBackend(providerConfig),
|
|
203
|
+
toolId,
|
|
204
|
+
backend: telemetryBackend(providerConfig),
|
|
191
205
|
operation: "provider-initialize",
|
|
192
206
|
duration: Date.now() - providerInitStartedAt,
|
|
193
207
|
errorType: "ProviderInitializationError",
|
|
194
208
|
message: error instanceof Error ? error.message : String(error),
|
|
195
209
|
});
|
|
196
|
-
console.error(`[ToolProviderRegistry] Initialization failed for "${
|
|
210
|
+
console.error(`[ToolProviderRegistry] Initialization failed for "${toolId}":`, error);
|
|
197
211
|
throw error;
|
|
198
212
|
}
|
|
199
213
|
}
|
|
@@ -201,87 +215,55 @@ export class ToolProviderRegistry {
|
|
|
201
215
|
* Get a provider instance
|
|
202
216
|
*
|
|
203
217
|
* Retrieves a registered provider. If autoInitialize is true and provider
|
|
204
|
-
* is not initialized, initializes it first.
|
|
205
|
-
*
|
|
206
|
-
* @param providerId Provider identifier
|
|
207
|
-
* @param autoInitialize Auto-initialize if not ready (default: true)
|
|
208
|
-
* @returns Provider instance
|
|
209
|
-
* @throws Error if provider not registered
|
|
218
|
+
* is not initialized, initializes it first. Throws when the provider is not
|
|
219
|
+
* registered.
|
|
210
220
|
*/
|
|
211
|
-
async getProvider(
|
|
212
|
-
const provider = this.providers.get(
|
|
221
|
+
async getProvider(toolId, autoInitialize = true) {
|
|
222
|
+
const provider = this.providers.get(toolId);
|
|
213
223
|
if (!provider) {
|
|
214
224
|
console.error("[ToolProviderRegistry] Provider not found:", {
|
|
215
|
-
requestedId:
|
|
225
|
+
requestedId: toolId,
|
|
216
226
|
availableProviders: Array.from(this.providers.keys()),
|
|
217
227
|
});
|
|
218
|
-
throw new Error(`[ToolProviderRegistry] Provider "${
|
|
228
|
+
throw new Error(`[ToolProviderRegistry] Provider "${toolId}" not registered`);
|
|
219
229
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
await this.initialize(providerId);
|
|
230
|
+
if (autoInitialize && !this.initialized.get(toolId)) {
|
|
231
|
+
logger.debug("Auto-initializing provider:", toolId);
|
|
232
|
+
await this.initialize(toolId);
|
|
224
233
|
}
|
|
225
234
|
return provider;
|
|
226
235
|
}
|
|
227
|
-
/**
|
|
228
|
-
* Get all providers in a category
|
|
229
|
-
*
|
|
230
|
-
* @param category Tool category to filter by
|
|
231
|
-
* @returns Array of provider IDs in the category
|
|
232
|
-
*/
|
|
236
|
+
/** The ids of the tools whose provider is in `category`. */
|
|
233
237
|
getProvidersByCategory(category) {
|
|
234
238
|
return Array.from(this.providers.entries())
|
|
235
239
|
.filter(([_, provider]) => provider.category === category)
|
|
236
240
|
.map(([id]) => id);
|
|
237
241
|
}
|
|
238
242
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* @returns Array of all provider IDs
|
|
243
|
+
* The ids of the tools with a registered provider.
|
|
242
244
|
*/
|
|
243
245
|
getProviderIds() {
|
|
244
246
|
return Array.from(this.providers.keys());
|
|
245
247
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
*
|
|
249
|
-
* @param providerId Provider identifier
|
|
250
|
-
* @returns true if provider is registered
|
|
251
|
-
*/
|
|
252
|
-
has(providerId) {
|
|
253
|
-
return this.providers.has(providerId);
|
|
248
|
+
has(toolId) {
|
|
249
|
+
return this.providers.has(toolId);
|
|
254
250
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
*
|
|
258
|
-
* @param providerId Provider identifier
|
|
259
|
-
* @returns true if provider is initialized
|
|
260
|
-
*/
|
|
261
|
-
isInitialized(providerId) {
|
|
262
|
-
return this.initialized.get(providerId) === true;
|
|
251
|
+
isInitialized(toolId) {
|
|
252
|
+
return this.initialized.get(toolId) === true;
|
|
263
253
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
*
|
|
267
|
-
* @param providerId Provider identifier
|
|
268
|
-
* @returns true if initialization in progress
|
|
269
|
-
*/
|
|
270
|
-
isInitializing(providerId) {
|
|
271
|
-
return this.initializationPromises.has(providerId);
|
|
254
|
+
isInitializing(toolId) {
|
|
255
|
+
return this.initializationPromises.has(toolId);
|
|
272
256
|
}
|
|
273
257
|
/**
|
|
274
258
|
* Unregister and destroy a provider
|
|
275
259
|
*
|
|
276
|
-
* Removes the provider from the registry and calls its destroy method
|
|
277
|
-
*
|
|
278
|
-
* @param providerId Provider to unregister
|
|
260
|
+
* Removes the provider from the registry and calls its destroy method,
|
|
261
|
+
* after any start in progress settles.
|
|
279
262
|
*/
|
|
280
|
-
async unregister(
|
|
281
|
-
const provider = this.providers.get(
|
|
263
|
+
async unregister(toolId) {
|
|
264
|
+
const provider = this.providers.get(toolId);
|
|
282
265
|
if (provider) {
|
|
283
|
-
|
|
284
|
-
const initPromise = this.initializationPromises.get(providerId);
|
|
266
|
+
const initPromise = this.initializationPromises.get(toolId);
|
|
285
267
|
if (initPromise) {
|
|
286
268
|
try {
|
|
287
269
|
await initPromise;
|
|
@@ -289,15 +271,17 @@ export class ToolProviderRegistry {
|
|
|
289
271
|
catch {
|
|
290
272
|
// Ignore initialization errors during unregister
|
|
291
273
|
}
|
|
274
|
+
// A provider registered meanwhile owns the slot, and register()
|
|
275
|
+
// already destroys this one once its start settles.
|
|
276
|
+
if (this.providers.get(toolId) !== provider)
|
|
277
|
+
return;
|
|
292
278
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
this.
|
|
297
|
-
this.
|
|
298
|
-
|
|
299
|
-
this.initializationPromises.delete(providerId);
|
|
300
|
-
console.log(`[ToolProviderRegistry] Unregistered provider "${providerId}"`);
|
|
279
|
+
this.destroyProvider(toolId, provider);
|
|
280
|
+
this.providers.delete(toolId);
|
|
281
|
+
this.configs.delete(toolId);
|
|
282
|
+
this.initialized.delete(toolId);
|
|
283
|
+
this.initializationPromises.delete(toolId);
|
|
284
|
+
logger.debug(`Unregistered provider "${toolId}"`);
|
|
301
285
|
}
|
|
302
286
|
}
|
|
303
287
|
/**
|
|
@@ -306,8 +290,8 @@ export class ToolProviderRegistry {
|
|
|
306
290
|
* Unregisters and destroys all providers in the registry.
|
|
307
291
|
*/
|
|
308
292
|
async destroy() {
|
|
309
|
-
const
|
|
310
|
-
await Promise.all(
|
|
311
|
-
|
|
293
|
+
const toolIds = Array.from(this.providers.keys());
|
|
294
|
+
await Promise.all(toolIds.map((id) => this.unregister(id)));
|
|
295
|
+
logger.debug("Registry destroyed");
|
|
312
296
|
}
|
|
313
297
|
}
|
|
@@ -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;
|
|
@@ -42,17 +43,26 @@ export interface ToolOpenRequest {
|
|
|
42
43
|
* to match a placement made elsewhere is a step it has no reason to expect.
|
|
43
44
|
*
|
|
44
45
|
* 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.
|
|
46
|
+
* registered one that hosts the tool claims the request unless `scopeId` names
|
|
47
|
+
* the card.
|
|
50
48
|
*/
|
|
51
49
|
level?: ToolPlacementLevel;
|
|
50
|
+
/**
|
|
51
|
+
* Scope of the toolbar that should open the tool: the item's canonical id for
|
|
52
|
+
* an item toolbar, the section id for a section toolbar. A constraint like
|
|
53
|
+
* `level`, for a requester that belongs to one card, as an inline control in an
|
|
54
|
+
* item does. Only a target registered with the same `scopeId` claims it.
|
|
55
|
+
*/
|
|
56
|
+
scopeId?: string;
|
|
52
57
|
}
|
|
53
58
|
export interface ToolRequestTarget {
|
|
54
59
|
/** The placement level this toolbar renders. */
|
|
55
60
|
level: ToolPlacementLevel;
|
|
61
|
+
/**
|
|
62
|
+
* The scope this toolbar renders for, matched against a request's `scopeId`.
|
|
63
|
+
* A target without one claims only requests that name no scope.
|
|
64
|
+
*/
|
|
65
|
+
readonly scopeId?: string;
|
|
56
66
|
/** Whether this toolbar currently renders the tool, per its own policy pass. */
|
|
57
67
|
hostsTool: (toolId: string) => boolean;
|
|
58
68
|
/**
|
|
@@ -70,8 +80,11 @@ export interface ToolRequestTarget {
|
|
|
70
80
|
* earlier one is still mounted does not displace it.
|
|
71
81
|
*/
|
|
72
82
|
export declare class ToolRequestRegistry {
|
|
83
|
+
private readonly onTargetFailure;
|
|
73
84
|
private readonly targets;
|
|
74
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);
|
|
75
88
|
registerTarget(target: ToolRequestTarget): () => void;
|
|
76
89
|
/**
|
|
77
90
|
* Whether a request for this tool would reach a toolbar.
|
|
@@ -80,16 +93,17 @@ export declare class ToolRequestRegistry {
|
|
|
80
93
|
* nothing is worse than an absent one, and availability moves with policy —
|
|
81
94
|
* hence {@link onTargetsChange}.
|
|
82
95
|
*/
|
|
83
|
-
canRequest(toolId: string, level?: ToolPlacementLevel): boolean;
|
|
96
|
+
canRequest(toolId: string, level?: ToolPlacementLevel, scopeId?: string): boolean;
|
|
84
97
|
/** Returns whether a target claimed the request. */
|
|
85
98
|
request(request: ToolOpenRequest): boolean;
|
|
86
99
|
/**
|
|
87
|
-
* Fires when a toolbar registers or unregisters
|
|
100
|
+
* Fires when a toolbar registers or unregisters, and on
|
|
101
|
+
* {@link notifyTargetsChange}.
|
|
88
102
|
*
|
|
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.
|
|
103
|
+
* Not when a policy change moves a registered toolbar's visible set: `hostsTool`
|
|
104
|
+
* is read live, so a caller re-asking `canRequest` gets the current answer. A
|
|
105
|
+
* surface that needs to notice a policy change should also follow the policy
|
|
106
|
+
* signal it already has.
|
|
93
107
|
*/
|
|
94
108
|
onTargetsChange(listener: () => void): () => void;
|
|
95
109
|
/**
|
|
@@ -102,6 +116,11 @@ export declare class ToolRequestRegistry {
|
|
|
102
116
|
private findTarget;
|
|
103
117
|
private findTargetAtLevel;
|
|
104
118
|
private hostsTool;
|
|
105
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Tell change listeners that what the targets can claim changed. Registration
|
|
121
|
+
* calls it, and so does the coordinator when a target stops hosting a tool
|
|
122
|
+
* outside a policy change, as when the tool's module fails to load.
|
|
123
|
+
*/
|
|
124
|
+
notifyTargetsChange(): void;
|
|
106
125
|
dispose(): void;
|
|
107
126
|
}
|
|
@@ -28,15 +28,20 @@ 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
|
-
this.
|
|
40
|
+
this.notifyTargetsChange();
|
|
36
41
|
return () => {
|
|
37
42
|
if (!this.targets.delete(target))
|
|
38
43
|
return;
|
|
39
|
-
this.
|
|
44
|
+
this.notifyTargetsChange();
|
|
40
45
|
};
|
|
41
46
|
}
|
|
42
47
|
/**
|
|
@@ -46,30 +51,31 @@ export class ToolRequestRegistry {
|
|
|
46
51
|
* nothing is worse than an absent one, and availability moves with policy —
|
|
47
52
|
* hence {@link onTargetsChange}.
|
|
48
53
|
*/
|
|
49
|
-
canRequest(toolId, level) {
|
|
50
|
-
return this.findTarget(toolId, level) !== null;
|
|
54
|
+
canRequest(toolId, level, scopeId) {
|
|
55
|
+
return this.findTarget(toolId, level, scopeId) !== null;
|
|
51
56
|
}
|
|
52
57
|
/** Returns whether a target claimed the request. */
|
|
53
58
|
request(request) {
|
|
54
|
-
const target = this.findTarget(request.toolId, request.level);
|
|
59
|
+
const target = this.findTarget(request.toolId, request.level, request.scopeId);
|
|
55
60
|
if (!target)
|
|
56
61
|
return false;
|
|
57
62
|
try {
|
|
58
63
|
target.open(request.toolId, request.params);
|
|
59
64
|
}
|
|
60
65
|
catch (error) {
|
|
61
|
-
|
|
66
|
+
this.onTargetFailure(request.toolId, "tool-request-open", error);
|
|
62
67
|
return false;
|
|
63
68
|
}
|
|
64
69
|
return true;
|
|
65
70
|
}
|
|
66
71
|
/**
|
|
67
|
-
* Fires when a toolbar registers or unregisters
|
|
72
|
+
* Fires when a toolbar registers or unregisters, and on
|
|
73
|
+
* {@link notifyTargetsChange}.
|
|
68
74
|
*
|
|
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.
|
|
75
|
+
* Not when a policy change moves a registered toolbar's visible set: `hostsTool`
|
|
76
|
+
* is read live, so a caller re-asking `canRequest` gets the current answer. A
|
|
77
|
+
* surface that needs to notice a policy change should also follow the policy
|
|
78
|
+
* signal it already has.
|
|
73
79
|
*/
|
|
74
80
|
onTargetsChange(listener) {
|
|
75
81
|
this.changeListeners.add(listener);
|
|
@@ -84,22 +90,26 @@ export class ToolRequestRegistry {
|
|
|
84
90
|
* and still have a section-scoped gateway reach it. Requesting a level explicitly
|
|
85
91
|
* does not fall back, because a requester that named one meant it.
|
|
86
92
|
*/
|
|
87
|
-
findTarget(toolId, level) {
|
|
88
|
-
const preferred = this.findTargetAtLevel(toolId, level ?? DEFAULT_TOOL_REQUEST_LEVEL);
|
|
93
|
+
findTarget(toolId, level, scopeId) {
|
|
94
|
+
const preferred = this.findTargetAtLevel(toolId, level ?? DEFAULT_TOOL_REQUEST_LEVEL, scopeId);
|
|
89
95
|
if (preferred || level !== undefined)
|
|
90
96
|
return preferred;
|
|
91
97
|
for (const target of this.targets) {
|
|
92
98
|
if (target.level === DEFAULT_TOOL_REQUEST_LEVEL)
|
|
93
99
|
continue;
|
|
100
|
+
if (scopeId !== undefined && target.scopeId !== scopeId)
|
|
101
|
+
continue;
|
|
94
102
|
if (this.hostsTool(target, toolId))
|
|
95
103
|
return target;
|
|
96
104
|
}
|
|
97
105
|
return null;
|
|
98
106
|
}
|
|
99
|
-
findTargetAtLevel(toolId, level) {
|
|
107
|
+
findTargetAtLevel(toolId, level, scopeId) {
|
|
100
108
|
for (const target of this.targets) {
|
|
101
109
|
if (target.level !== level)
|
|
102
110
|
continue;
|
|
111
|
+
if (scopeId !== undefined && target.scopeId !== scopeId)
|
|
112
|
+
continue;
|
|
103
113
|
if (this.hostsTool(target, toolId))
|
|
104
114
|
return target;
|
|
105
115
|
}
|
|
@@ -110,11 +120,16 @@ export class ToolRequestRegistry {
|
|
|
110
120
|
return target.hostsTool(toolId) === true;
|
|
111
121
|
}
|
|
112
122
|
catch (error) {
|
|
113
|
-
|
|
123
|
+
this.onTargetFailure(toolId, "tool-request-host-check", error);
|
|
114
124
|
return false;
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Tell change listeners that what the targets can claim changed. Registration
|
|
129
|
+
* calls it, and so does the coordinator when a target stops hosting a tool
|
|
130
|
+
* outside a policy change, as when the tool's module fails to load.
|
|
131
|
+
*/
|
|
132
|
+
notifyTargetsChange() {
|
|
118
133
|
for (const listener of this.changeListeners) {
|
|
119
134
|
try {
|
|
120
135
|
listener();
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
parameters?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A toolbar's decided tools across re-decisions. The tracker returns its
|
|
11
|
+
* previous list while `renderVersion` is unchanged and the decision shows the
|
|
12
|
+
* same tools, so a re-decision that changes nothing the toolbar renders from
|
|
13
|
+
* keeps the rendered tools: rendering swaps in fresh tool elements, whose state
|
|
14
|
+
* starts over. A new `renderVersion` always yields a new list.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createDecidedToolsTracker(): (decision: ToolPolicyDecision | null, renderVersion: number) => readonly DecidedToolbarTool[] | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
parameters: entry.parameters,
|
|
21
|
+
}));
|
|
22
|
+
if (last?.renderVersion === renderVersion &&
|
|
23
|
+
structurallyEqual(last.tools, tools)) {
|
|
24
|
+
return last.tools;
|
|
25
|
+
}
|
|
26
|
+
last = { renderVersion, tools };
|
|
27
|
+
return tools;
|
|
28
|
+
};
|
|
29
|
+
}
|