@caupulican/pi-adaptative 0.80.40 → 0.80.44
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/CHANGELOG.md +25 -0
- package/dist/bundled-resources/prompts/extensionify.md +20 -0
- package/dist/bundled-resources/prompts/learn.md +27 -0
- package/dist/bundled-resources/prompts/skillify.md +21 -0
- package/dist/bundled-resources/skills/pi-harness-learning/SKILL.md +217 -0
- package/dist/bundled-resources/skills/skill-architect/SKILL.md +162 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +37 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +217 -11
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +4 -2
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +27 -5
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +10 -3
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +38 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +6 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +53 -6
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +16 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/profile-registry.d.ts +39 -0
- package/dist/core/profile-registry.d.ts.map +1 -0
- package/dist/core/profile-registry.js +230 -0
- package/dist/core/profile-registry.js.map +1 -0
- package/dist/core/profile-resource-selection.d.ts +19 -0
- package/dist/core/profile-resource-selection.d.ts.map +1 -0
- package/dist/core/profile-resource-selection.js +84 -0
- package/dist/core/profile-resource-selection.js.map +1 -0
- package/dist/core/resource-loader.d.ts +33 -3
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +68 -11
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +44 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +557 -27
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +5 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +2 -2
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -17
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/extensionify.d.ts +33 -0
- package/dist/core/tools/extensionify.d.ts.map +1 -0
- package/dist/core/tools/extensionify.js +146 -0
- package/dist/core/tools/extensionify.js.map +1 -0
- package/dist/core/tools/index.d.ts +10 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +36 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/skill-audit.d.ts +63 -0
- package/dist/core/tools/skill-audit.d.ts.map +1 -0
- package/dist/core/tools/skill-audit.js +175 -0
- package/dist/core/tools/skill-audit.js.map +1 -0
- package/dist/core/tools/skillify.d.ts +30 -0
- package/dist/core/tools/skillify.d.ts.map +1 -0
- package/dist/core/tools/skillify.js +91 -0
- package/dist/core/tools/skillify.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts +50 -0
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/profile-resource-editor.js +232 -0
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -0
- package/dist/modes/interactive/components/profile-selector.d.ts +8 -0
- package/dist/modes/interactive/components/profile-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/profile-selector.js +77 -0
- package/dist/modes/interactive/components/profile-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +62 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +12 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +362 -24
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/settings.md +20 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +4 -0
- package/npm-shrinkwrap.json +12 -12
- package/package.json +6 -6
|
@@ -29,6 +29,7 @@ import { exportSessionToHtml } from "./export-html/index.js";
|
|
|
29
29
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
30
30
|
import { createCoreDiagnosticsToolDefinitions } from "./extensions/builtin.js";
|
|
31
31
|
import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
32
|
+
import { disposeExtensionEventSubscriptions } from "./extensions/loader.js";
|
|
32
33
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
33
34
|
import { compactToolResultDetailsForRetention } from "./message-retention.js";
|
|
34
35
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
@@ -71,6 +72,7 @@ export class AgentSession {
|
|
|
71
72
|
// Event subscription state
|
|
72
73
|
_unsubscribeAgent;
|
|
73
74
|
_eventListeners = [];
|
|
75
|
+
_extensionsChangedListeners = [];
|
|
74
76
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
75
77
|
_steeringMessages = [];
|
|
76
78
|
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
@@ -205,7 +207,7 @@ export class AgentSession {
|
|
|
205
207
|
const contextWindow = this.model?.contextWindow ?? 0;
|
|
206
208
|
if (settings.enabled && contextWindow > 0 && !this.isCompacting) {
|
|
207
209
|
const contextTokens = this._estimateCurrentContextTokens(authoritativeMessages);
|
|
208
|
-
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
210
|
+
if (shouldCompact(contextTokens, contextWindow, settings, this.model?.autoCompactionTriggerTokens)) {
|
|
209
211
|
const latestBefore = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
|
|
210
212
|
await this._runAutoCompaction("threshold", false);
|
|
211
213
|
const latestAfter = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
|
|
@@ -579,6 +581,33 @@ export class AgentSession {
|
|
|
579
581
|
}
|
|
580
582
|
};
|
|
581
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Subscribe to extensions changed events (load/unload live).
|
|
586
|
+
* Returns unsubscribe function for this listener.
|
|
587
|
+
*/
|
|
588
|
+
onExtensionsChanged(cb) {
|
|
589
|
+
this._extensionsChangedListeners.push(cb);
|
|
590
|
+
return () => {
|
|
591
|
+
const index = this._extensionsChangedListeners.indexOf(cb);
|
|
592
|
+
if (index !== -1) {
|
|
593
|
+
this._extensionsChangedListeners.splice(index, 1);
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Notify all extensions-changed listeners.
|
|
599
|
+
* Called after successful load/unload operations.
|
|
600
|
+
*/
|
|
601
|
+
_notifyExtensionsChanged() {
|
|
602
|
+
for (const listener of this._extensionsChangedListeners) {
|
|
603
|
+
try {
|
|
604
|
+
listener();
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
// Suppress errors from listeners to avoid cascading failures
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
582
611
|
/**
|
|
583
612
|
* Temporarily disconnect from agent events.
|
|
584
613
|
* User listeners are preserved and will receive events again after resubscribe().
|
|
@@ -756,9 +785,7 @@ export class AgentSession {
|
|
|
756
785
|
_buildSelfModificationPrompt() {
|
|
757
786
|
const settings = this.settingsManager.getSelfModificationSettings();
|
|
758
787
|
if (!settings.enabled) {
|
|
759
|
-
return
|
|
760
|
-
- Do not modify Pi core, the installed Pi runtime, or pi-adaptative harness source for self-evolution.
|
|
761
|
-
- If self-modification is needed, ask the user to enable \`selfModification.enabled\` and set \`selfModification.sourcePaths\` (or legacy \`selfModification.sourcePath\`) to the pi-adaptative source checkout.`;
|
|
788
|
+
return undefined;
|
|
762
789
|
}
|
|
763
790
|
// Resolve from an ordered candidate list first (portable WSL/Termux switching
|
|
764
791
|
// from settings alone), then fall back to the legacy single sourcePath.
|
|
@@ -796,7 +823,7 @@ export class AgentSession {
|
|
|
796
823
|
_buildAutonomyPrompt() {
|
|
797
824
|
const autoLearn = this.settingsManager.getAutoLearnSettings();
|
|
798
825
|
const autonomy = this.settingsManager.getAutonomySettings();
|
|
799
|
-
if (!autoLearn.enabled && autonomy.mode
|
|
826
|
+
if (!autoLearn.enabled && autonomy.mode !== "full") {
|
|
800
827
|
return undefined;
|
|
801
828
|
}
|
|
802
829
|
const reflection = autoLearn.reflectionReview ?? autonomy.mode !== "off";
|
|
@@ -1339,17 +1366,20 @@ export class AgentSession {
|
|
|
1339
1366
|
* Validates that auth is configured, saves to session and settings.
|
|
1340
1367
|
* @throws Error if no auth is configured for the model
|
|
1341
1368
|
*/
|
|
1342
|
-
async setModel(model) {
|
|
1369
|
+
async setModel(model, options = {}) {
|
|
1343
1370
|
if (!this._modelRegistry.hasConfiguredAuth(model)) {
|
|
1344
1371
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
1345
1372
|
}
|
|
1373
|
+
const persistSettings = options.persistSettings ?? true;
|
|
1346
1374
|
const previousModel = this.model;
|
|
1347
1375
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1348
1376
|
this.agent.state.model = model;
|
|
1349
1377
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1350
|
-
|
|
1378
|
+
if (persistSettings) {
|
|
1379
|
+
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1380
|
+
}
|
|
1351
1381
|
// Re-clamp thinking level for new model's capabilities
|
|
1352
|
-
this.setThinkingLevel(thinkingLevel);
|
|
1382
|
+
this.setThinkingLevel(thinkingLevel, { persistSettings });
|
|
1353
1383
|
await this._emitModelSelect(model, previousModel, "set");
|
|
1354
1384
|
}
|
|
1355
1385
|
/**
|
|
@@ -1416,16 +1446,17 @@ export class AgentSession {
|
|
|
1416
1446
|
* Clamps to model capabilities based on available thinking levels.
|
|
1417
1447
|
* Saves to session and settings only if the level actually changes.
|
|
1418
1448
|
*/
|
|
1419
|
-
setThinkingLevel(level) {
|
|
1449
|
+
setThinkingLevel(level, options = {}) {
|
|
1420
1450
|
const availableLevels = this.getAvailableThinkingLevels();
|
|
1421
1451
|
const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
|
|
1422
1452
|
// Only persist if actually changing
|
|
1423
1453
|
const previousLevel = this.agent.state.thinkingLevel;
|
|
1424
1454
|
const isChanging = effectiveLevel !== previousLevel;
|
|
1455
|
+
const persistSettings = options.persistSettings ?? true;
|
|
1425
1456
|
this.agent.state.thinkingLevel = effectiveLevel;
|
|
1426
1457
|
if (isChanging) {
|
|
1427
1458
|
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
1428
|
-
if (this.supportsThinking() || effectiveLevel !== "off") {
|
|
1459
|
+
if (persistSettings && (this.supportsThinking() || effectiveLevel !== "off")) {
|
|
1429
1460
|
this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
|
|
1430
1461
|
}
|
|
1431
1462
|
this._emit({ type: "thinking_level_changed", level: effectiveLevel });
|
|
@@ -1711,7 +1742,7 @@ export class AgentSession {
|
|
|
1711
1742
|
}
|
|
1712
1743
|
}
|
|
1713
1744
|
}
|
|
1714
|
-
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
1745
|
+
if (shouldCompact(contextTokens, contextWindow, settings, this.model?.autoCompactionTriggerTokens)) {
|
|
1715
1746
|
return await this._runAutoCompaction("threshold", false);
|
|
1716
1747
|
}
|
|
1717
1748
|
return false;
|
|
@@ -2319,6 +2350,181 @@ export class AgentSession {
|
|
|
2319
2350
|
throw error;
|
|
2320
2351
|
}
|
|
2321
2352
|
}
|
|
2353
|
+
/**
|
|
2354
|
+
* Unload a single extension without full reload.
|
|
2355
|
+
* Runs the extension's session_shutdown lifecycle, unregisters its providers,
|
|
2356
|
+
* disposes its event subscriptions, and rebuilds the runtime.
|
|
2357
|
+
* Falls back to full reload on error.
|
|
2358
|
+
*/
|
|
2359
|
+
async unloadExtensionLive(extensionPath) {
|
|
2360
|
+
if (this.isStreaming) {
|
|
2361
|
+
throw new Error("Cannot unload extension while the agent is streaming or a tool call is active");
|
|
2362
|
+
}
|
|
2363
|
+
if (this.isCompacting) {
|
|
2364
|
+
throw new Error("Cannot unload extension while context compaction or branch summarization is active");
|
|
2365
|
+
}
|
|
2366
|
+
const ext = this._resourceLoader.getLoadedExtension(extensionPath);
|
|
2367
|
+
if (!ext) {
|
|
2368
|
+
return; // Nothing to unload
|
|
2369
|
+
}
|
|
2370
|
+
const previousRunner = this._extensionRunner;
|
|
2371
|
+
try {
|
|
2372
|
+
// Run session_shutdown lifecycle for this extension only
|
|
2373
|
+
await this._extensionRunner.emitToExtension(ext, { type: "session_shutdown", reason: "unload" });
|
|
2374
|
+
// Unregister its providers (keyed by the extension's own path, as registered)
|
|
2375
|
+
const runtime = this._resourceLoader.getExtensions().runtime;
|
|
2376
|
+
for (const name of runtime.getProvidersForExtension(ext.path)) {
|
|
2377
|
+
runtime.unregisterProvider(name, ext.path);
|
|
2378
|
+
}
|
|
2379
|
+
// Dispose its event subscriptions and run disposers
|
|
2380
|
+
await disposeExtensionEventSubscriptions([ext]);
|
|
2381
|
+
// Remove from loaded extensions
|
|
2382
|
+
this._resourceLoader.removeLoadedExtension(extensionPath);
|
|
2383
|
+
// Rebuild runtime with new extension set
|
|
2384
|
+
const activeToolNames = this.getActiveToolNames();
|
|
2385
|
+
const previousFlagValues = previousRunner.getFlagValues();
|
|
2386
|
+
this._buildRuntime({
|
|
2387
|
+
activeToolNames,
|
|
2388
|
+
flagValues: previousFlagValues,
|
|
2389
|
+
includeAllExtensionTools: true,
|
|
2390
|
+
});
|
|
2391
|
+
previousRunner.invalidate();
|
|
2392
|
+
// Notify extensions-changed listeners
|
|
2393
|
+
this._notifyExtensionsChanged();
|
|
2394
|
+
}
|
|
2395
|
+
catch (error) {
|
|
2396
|
+
// Fall back to full reload on error
|
|
2397
|
+
try {
|
|
2398
|
+
await this.reload();
|
|
2399
|
+
}
|
|
2400
|
+
catch {
|
|
2401
|
+
// Suppress nested error; original error will be thrown below
|
|
2402
|
+
}
|
|
2403
|
+
throw error;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Load a single extension without full reload.
|
|
2408
|
+
* Loads the extension with fresh import, rebuilds the runtime,
|
|
2409
|
+
* and runs the extension's session_start lifecycle.
|
|
2410
|
+
* Falls back to full reload on error.
|
|
2411
|
+
*/
|
|
2412
|
+
async loadExtensionLive(extensionPath) {
|
|
2413
|
+
if (this.isStreaming) {
|
|
2414
|
+
throw new Error("Cannot load extension while the agent is streaming or a tool call is active");
|
|
2415
|
+
}
|
|
2416
|
+
if (this.isCompacting) {
|
|
2417
|
+
throw new Error("Cannot load extension while context compaction or branch summarization is active");
|
|
2418
|
+
}
|
|
2419
|
+
const previousRunner = this._extensionRunner;
|
|
2420
|
+
try {
|
|
2421
|
+
// Load the extension with fresh import
|
|
2422
|
+
const { extension, error } = await this._resourceLoader.loadSingleExtension(extensionPath);
|
|
2423
|
+
if (error || !extension) {
|
|
2424
|
+
throw new Error(error || `Failed to load extension: ${extensionPath}`);
|
|
2425
|
+
}
|
|
2426
|
+
// Rebuild runtime to aggregate tools/commands/handlers/providers
|
|
2427
|
+
const activeToolNames = this.getActiveToolNames();
|
|
2428
|
+
const previousFlagValues = previousRunner.getFlagValues();
|
|
2429
|
+
this._buildRuntime({
|
|
2430
|
+
activeToolNames,
|
|
2431
|
+
flagValues: previousFlagValues,
|
|
2432
|
+
includeAllExtensionTools: true,
|
|
2433
|
+
});
|
|
2434
|
+
// Run session_start lifecycle for the new extension only
|
|
2435
|
+
await this._extensionRunner.emitToExtension(extension, { type: "session_start", reason: "load" });
|
|
2436
|
+
// Notify extensions-changed listeners
|
|
2437
|
+
this._notifyExtensionsChanged();
|
|
2438
|
+
}
|
|
2439
|
+
catch (error) {
|
|
2440
|
+
// Fall back to full reload on error
|
|
2441
|
+
try {
|
|
2442
|
+
await this.reload();
|
|
2443
|
+
}
|
|
2444
|
+
catch {
|
|
2445
|
+
// Suppress nested error; original error will be thrown below
|
|
2446
|
+
}
|
|
2447
|
+
throw error;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Reconcile loaded extensions with the active profile.
|
|
2452
|
+
* Loads extensions that should be enabled but aren't, and unloads extensions that shouldn't be.
|
|
2453
|
+
* Falls back to full reload if any individual load/unload fails.
|
|
2454
|
+
*/
|
|
2455
|
+
async reconcileLoadedExtensions() {
|
|
2456
|
+
if (this.isStreaming) {
|
|
2457
|
+
throw new Error("Cannot reconcile extensions while the agent is streaming or a tool call is active");
|
|
2458
|
+
}
|
|
2459
|
+
if (this.isCompacting) {
|
|
2460
|
+
throw new Error("Cannot reconcile extensions while context compaction or branch summarization is active");
|
|
2461
|
+
}
|
|
2462
|
+
try {
|
|
2463
|
+
// Get all discoverable extension paths
|
|
2464
|
+
const allDiscoverablePaths = await this._resourceLoader.getDiscoverableExtensionPaths();
|
|
2465
|
+
// Get the target enabled set based on profile filters
|
|
2466
|
+
const targetEnabledSet = new Set();
|
|
2467
|
+
for (const path of allDiscoverablePaths) {
|
|
2468
|
+
if (this.settingsManager.isResourceAllowedByProfile("extensions", path)) {
|
|
2469
|
+
targetEnabledSet.add(path);
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
// Get currently loaded set
|
|
2473
|
+
const loadedExtensions = this._resourceLoader.getExtensions().extensions;
|
|
2474
|
+
const loadedSet = new Set();
|
|
2475
|
+
for (const ext of loadedExtensions) {
|
|
2476
|
+
loadedSet.add(ext.path);
|
|
2477
|
+
}
|
|
2478
|
+
// Collect unloads and loads
|
|
2479
|
+
const toUnload = [];
|
|
2480
|
+
const toLoad = [];
|
|
2481
|
+
for (const path of loadedSet) {
|
|
2482
|
+
if (!targetEnabledSet.has(path)) {
|
|
2483
|
+
toUnload.push(path);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
for (const path of targetEnabledSet) {
|
|
2487
|
+
if (!loadedSet.has(path)) {
|
|
2488
|
+
toLoad.push(path);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
// Apply unloads first, then loads, to minimize churn
|
|
2492
|
+
// Collect errors but continue through all operations
|
|
2493
|
+
const errors = [];
|
|
2494
|
+
for (const path of toUnload) {
|
|
2495
|
+
try {
|
|
2496
|
+
await this.unloadExtensionLive(path);
|
|
2497
|
+
}
|
|
2498
|
+
catch (error) {
|
|
2499
|
+
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
for (const path of toLoad) {
|
|
2503
|
+
try {
|
|
2504
|
+
await this.loadExtensionLive(path);
|
|
2505
|
+
}
|
|
2506
|
+
catch (error) {
|
|
2507
|
+
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
// If any errors occurred, throw the first one (already fell back to full reload in load/unload)
|
|
2511
|
+
if (errors.length > 0) {
|
|
2512
|
+
throw errors[0];
|
|
2513
|
+
}
|
|
2514
|
+
// Single notification at the end
|
|
2515
|
+
this._notifyExtensionsChanged();
|
|
2516
|
+
}
|
|
2517
|
+
catch (error) {
|
|
2518
|
+
// Fall back to full reload on error
|
|
2519
|
+
try {
|
|
2520
|
+
await this.reload();
|
|
2521
|
+
}
|
|
2522
|
+
catch {
|
|
2523
|
+
// Suppress nested error; original error will be thrown below
|
|
2524
|
+
}
|
|
2525
|
+
throw error;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2322
2528
|
// =========================================================================
|
|
2323
2529
|
// Auto-Retry
|
|
2324
2530
|
// =========================================================================
|