@yeaft/webchat-agent 1.0.409 → 1.0.410
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/connection/message-router.js +24 -2
- package/llm-config-cli.js +36 -3
- package/local-runtime/server/handlers/agent-output.js +14 -0
- package/local-runtime/server/handlers/agent-sync.js +6 -1
- package/local-runtime/server/handlers/client-conversation.js +4 -0
- package/local-runtime/server/handlers/client-misc.js +27 -0
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +166 -64
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/config-api.js +109 -48
- package/yeaft/config.js +56 -9
- package/yeaft/engine.js +98 -43
- package/yeaft/plugins.js +170 -0
- package/yeaft/session.js +7 -4
- package/yeaft/sessions/feature-flag.js +42 -9
- package/yeaft/tools/mcp-tools.js +1 -0
- package/yeaft/tools/registry.js +51 -7
- package/yeaft/tools/types.js +6 -0
- package/yeaft/web-bridge.js +317 -89
- package/yeaft/work-center/runner.js +5 -1
package/yeaft/web-bridge.js
CHANGED
|
@@ -23,8 +23,9 @@ import { DEFAULT_YEAFT_DIR } from './init.js';
|
|
|
23
23
|
import { buildDreamOutputSnapshot } from './dream/output-snapshot.js';
|
|
24
24
|
import { Engine } from './engine.js';
|
|
25
25
|
import { loadSession } from './session.js';
|
|
26
|
-
import { loadConfig, loadMCPConfig } from './config.js';
|
|
26
|
+
import { loadAgentMCPConfig, loadConfig, loadMCPConfig } from './config.js';
|
|
27
27
|
import { createSkillManager } from './skills.js';
|
|
28
|
+
import { buildPluginCatalog, createPluginSkillManager, resolveMcpPluginPolicy } from './plugins.js';
|
|
28
29
|
import { MCPManager } from './mcp.js';
|
|
29
30
|
import { sendToServer } from '../connection/buffer.js';
|
|
30
31
|
import ctx from '../context.js';
|
|
@@ -132,6 +133,19 @@ const pendingUserPrompts = new Map();
|
|
|
132
133
|
/** @type {import('./session.js').Session | null} */
|
|
133
134
|
let session = null;
|
|
134
135
|
|
|
136
|
+
// Agent-local MCP config and live runtime managers are mutable. WebSocket
|
|
137
|
+
// dispatch deliberately allows unrelated frames to overlap, so serialize MCP
|
|
138
|
+
// CRUD and runtime bootstrap from config read through connection, activation,
|
|
139
|
+
// tool hot-swap, and MCP publication. Keep the tail fulfilled after a failure:
|
|
140
|
+
// the caller still receives its error, but a later transition is not blocked.
|
|
141
|
+
let mcpTransitionTail = Promise.resolve();
|
|
142
|
+
|
|
143
|
+
function enqueueMcpTransition(operation) {
|
|
144
|
+
const queued = mcpTransitionTail.then(operation);
|
|
145
|
+
mcpTransitionTail = queued.catch(() => undefined);
|
|
146
|
+
return queued;
|
|
147
|
+
}
|
|
148
|
+
|
|
135
149
|
/**
|
|
136
150
|
* Single-flight runtime boot. History replay must not wait for this promise on
|
|
137
151
|
* cold load; message send still awaits it through ensureSessionLoaded().
|
|
@@ -224,6 +238,8 @@ export async function refreshLiveSessionConfig(options = {}) {
|
|
|
224
238
|
|
|
225
239
|
const liveSession = session;
|
|
226
240
|
const currentConfig = liveSession.config || {};
|
|
241
|
+
const pluginsChanged = JSON.stringify(currentConfig.plugins || {})
|
|
242
|
+
!== JSON.stringify(freshConfig.plugins || {});
|
|
227
243
|
const runtimeOnly = {};
|
|
228
244
|
for (const key of ['serverMode', '_readOnly', 'modelEffort']) {
|
|
229
245
|
if (Object.prototype.hasOwnProperty.call(currentConfig, key)) runtimeOnly[key] = currentConfig[key];
|
|
@@ -249,10 +265,19 @@ export async function refreshLiveSessionConfig(options = {}) {
|
|
|
249
265
|
if (typeof liveSession.adapter?.refreshProviders === 'function') {
|
|
250
266
|
liveSession.adapter.refreshProviders(freshConfig.providers || []);
|
|
251
267
|
}
|
|
268
|
+
// Plugin selections affect connected MCP processes as well as model-visible
|
|
269
|
+
// schemas. Retire cached project runtimes before applying the new selection;
|
|
270
|
+
// the base runtime is rebuilt below so the default Session cannot retain old
|
|
271
|
+
// MCP tools or a stale manager after a save.
|
|
272
|
+
if (pluginsChanged) await shutdownProjectRuntimes();
|
|
252
273
|
for (const key of Object.keys(currentConfig)) delete currentConfig[key];
|
|
253
274
|
Object.assign(currentConfig, nextConfig);
|
|
254
275
|
liveSession.engine?.refreshConfig?.(currentConfig);
|
|
255
276
|
liveSession.trace?.refreshConfig?.(currentConfig.telemetry || {});
|
|
277
|
+
if (pluginsChanged && session === liveSession) {
|
|
278
|
+
claimRuntimeOwnership(liveSession);
|
|
279
|
+
await scheduleBaseRuntimeLoad();
|
|
280
|
+
}
|
|
256
281
|
for (const { key, engine, config } of vpConfigSnapshots) {
|
|
257
282
|
engine.refreshConfig?.(config);
|
|
258
283
|
vpEngineConfigKeys.set(key, engineConfigKey(config));
|
|
@@ -689,10 +714,20 @@ function engineConfigKey(config) {
|
|
|
689
714
|
fastModel: config?.fastModel || '',
|
|
690
715
|
fastModelId: config?.fastModelId || '',
|
|
691
716
|
fallbackModel: config?.fallbackModel || '',
|
|
717
|
+
plugins: config?.plugins || {},
|
|
692
718
|
providers: Array.isArray(config?.providers) ? config.providers : [],
|
|
693
719
|
});
|
|
694
720
|
}
|
|
695
721
|
|
|
722
|
+
function effectiveRuntimeManagers(skillManager, mcpManager, _config = session?.config) {
|
|
723
|
+
// Engine owns the final skill-policy wrapper so refreshConfig() can safely
|
|
724
|
+
// reapply a changed Agent plugin selection without mutating shared managers.
|
|
725
|
+
return {
|
|
726
|
+
skillManager: skillManager || null,
|
|
727
|
+
mcpManager: mcpManager || null,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
696
731
|
function normalizeSessionWorkDir(workDir) {
|
|
697
732
|
return typeof workDir === 'string' && workDir.trim() ? workDir.trim() : '';
|
|
698
733
|
}
|
|
@@ -710,7 +745,7 @@ const RUNNING_THREAD_STATES = new Set(['queued', 'typing', 'thinking', 'retrying
|
|
|
710
745
|
const vpThreads = new Map();
|
|
711
746
|
/** @type {Map<string, Set<Promise<string|null>>>} */
|
|
712
747
|
const routePromisesByMsgId = new Map();
|
|
713
|
-
/** @type {Map<string, { workDir: string, skillManager: import('./skills.js').SkillManager, mcpManager: import('./mcp.js').MCPManager, mcpStatus: object,
|
|
748
|
+
/** @type {Map<string, { workDir: string, skillManager: import('./skills.js').SkillManager, mcpManager: import('./mcp.js').MCPManager, mcpStatus: object, configuredMcpConfig: object, effectiveMcpConfig: object, status: { skills: number, mcpServers: string[], mcpFailed: object[], mcpSkipped: object[], tools: number } }>} */
|
|
714
749
|
const projectRuntimes = new Map();
|
|
715
750
|
/** @type {Map<string, Promise<any>>} */
|
|
716
751
|
const baseRuntimeLoadPromises = new Map();
|
|
@@ -832,7 +867,7 @@ function activateBaseRuntime(owner = captureRuntimeOwner(), { reloadSkills = tru
|
|
|
832
867
|
if (!isCurrentRuntimeOwner(owner)) return { removed: 0, added: 0, skipped: true };
|
|
833
868
|
activeRuntimeKey = BASE_RUNTIME_KEY;
|
|
834
869
|
const swap = replaceSessionMcpTools(owner, mcpManager);
|
|
835
|
-
retargetVpEngines(owner,
|
|
870
|
+
retargetVpEngines(owner, effectiveRuntimeManagers(skillManager, mcpManager, ownerSession.config));
|
|
836
871
|
if (status) {
|
|
837
872
|
status.skills = skillManager?.size || 0;
|
|
838
873
|
status.mcpServers = Array.isArray(status.mcpServers) ? status.mcpServers : [];
|
|
@@ -859,10 +894,11 @@ function activateProjectRuntime(runtime, owner = captureRuntimeOwner(), { reload
|
|
|
859
894
|
if (!runtimeBelongsToOwner(runtime, owner)) return { removed: 0, added: 0, skipped: true };
|
|
860
895
|
activeRuntimeKey = runtimeKey;
|
|
861
896
|
const swap = replaceSessionMcpTools(owner, runtime.mcpManager);
|
|
862
|
-
retargetVpEngines(owner,
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
897
|
+
retargetVpEngines(owner, effectiveRuntimeManagers(
|
|
898
|
+
runtime.skillManager,
|
|
899
|
+
runtime.mcpManager,
|
|
900
|
+
owner.ownerSession.config,
|
|
901
|
+
));
|
|
866
902
|
runtime.status = {
|
|
867
903
|
...runtime.status,
|
|
868
904
|
skills: runtime.skillManager?.size || 0,
|
|
@@ -1798,6 +1834,9 @@ function getOrCreateVpEngine(sessionId, vpId, threadId = 'main') {
|
|
|
1798
1834
|
// Per-session config overlay (v1: model only). Falls back to the
|
|
1799
1835
|
// session's user-level config when no override is set. Session config is
|
|
1800
1836
|
// always resolved from the agent-local root; project `.yeaft` is ignored.
|
|
1837
|
+
// Agent-level plugin config is already part of session.config; Session
|
|
1838
|
+
// config still only overlays model/effort. Keep the runtime policy Agent
|
|
1839
|
+
// scoped even though each VP Engine has its own effective config snapshot.
|
|
1801
1840
|
const effectiveConfig = resolveLiveSessionConfig(session.config, sessionId);
|
|
1802
1841
|
const configKey = engineConfigKey(effectiveConfig);
|
|
1803
1842
|
let eng = vpEngines.get(key);
|
|
@@ -2353,6 +2392,8 @@ export async function __testDrainVpDrivers() {
|
|
|
2353
2392
|
* a half-aborted controller writing to a now-cleared map.
|
|
2354
2393
|
*/
|
|
2355
2394
|
export async function __testResetVpState() {
|
|
2395
|
+
await mcpTransitionTail.catch(() => undefined);
|
|
2396
|
+
mcpTransitionTail = Promise.resolve();
|
|
2356
2397
|
await shutdownProjectRuntimes();
|
|
2357
2398
|
for (const ctrl of vpAborts.values()) {
|
|
2358
2399
|
try { if (!ctrl.signal.aborted) ctrl.abort(); } catch { /* */ }
|
|
@@ -2486,9 +2527,11 @@ function loadAndBroadcastYeaftSkillSlashCommands() {
|
|
|
2486
2527
|
const configuredWorkDir = typeof ctx.CONFIG?.workDir === 'string' ? ctx.CONFIG.workDir.trim() : '';
|
|
2487
2528
|
if (configuredWorkDir && configuredWorkDir !== process.cwd()) roots.push(configuredWorkDir);
|
|
2488
2529
|
const skillManager = createSkillManager(yeaftDir, roots.join(delimiter));
|
|
2489
|
-
|
|
2530
|
+
const config = loadConfig({ dir: yeaftDir });
|
|
2531
|
+
const visibleSkillManager = createPluginSkillManager(skillManager, config.plugins);
|
|
2532
|
+
broadcastSkillSlashCommands({ skillManager: visibleSkillManager });
|
|
2490
2533
|
return {
|
|
2491
|
-
skills:
|
|
2534
|
+
skills: visibleSkillManager?.size || 0,
|
|
2492
2535
|
slashCommands: ctx.slashCommands,
|
|
2493
2536
|
slashCommandDescriptions: ctx.slashCommandDescriptions,
|
|
2494
2537
|
};
|
|
@@ -2503,7 +2546,10 @@ export function preloadYeaftSkillSlashCommands() {
|
|
|
2503
2546
|
}
|
|
2504
2547
|
|
|
2505
2548
|
function broadcastSkillSlashCommands(sessionLike, extraSkillManagers = []) {
|
|
2506
|
-
const
|
|
2549
|
+
const plugins = sessionLike?.config?.plugins || session?.config?.plugins || {};
|
|
2550
|
+
const managers = [sessionLike?.skillManager, ...extraSkillManagers]
|
|
2551
|
+
.filter(Boolean)
|
|
2552
|
+
.map(manager => createPluginSkillManager(manager, plugins));
|
|
2507
2553
|
const { commands: slashCommands, descriptions: slashCommandDescriptions } = buildMergedSkillSlashCommands(managers);
|
|
2508
2554
|
// Yeaft owns an isolated command catalogue. Reusing ctx's Claude Chat
|
|
2509
2555
|
// commands made unsupported entries such as /compact and /mcp appear in a
|
|
@@ -2606,14 +2652,18 @@ function stopSkillHotReload(owner = null) {
|
|
|
2606
2652
|
return true;
|
|
2607
2653
|
}
|
|
2608
2654
|
|
|
2609
|
-
async function
|
|
2655
|
+
async function runBaseRuntimeTransition(owner = captureRuntimeOwner()) {
|
|
2610
2656
|
if (!isCurrentRuntimeOwner(owner)) return null;
|
|
2611
2657
|
const ownerSession = owner.ownerSession;
|
|
2612
2658
|
const yeaftDir = ctx.CONFIG?.yeaftDir || ownerSession.yeaftDir || DEFAULT_YEAFT_DIR;
|
|
2613
2659
|
const previousSkillManager = ownerSession.skillManager;
|
|
2614
2660
|
const previousMcpManager = ownerSession.mcpManager;
|
|
2615
2661
|
const skillManager = createRuntimeSkillManager(yeaftDir, process.cwd());
|
|
2616
|
-
const
|
|
2662
|
+
const rawMcpConfig = loadRuntimeMcpConfig(yeaftDir, undefined, process.cwd());
|
|
2663
|
+
const { configured: configuredMcpConfig, effective: effectiveMcpConfig } = resolveMcpPluginPolicy(
|
|
2664
|
+
rawMcpConfig,
|
|
2665
|
+
ownerSession.config?.plugins,
|
|
2666
|
+
);
|
|
2617
2667
|
const mcpManager = createRuntimeMcpManager();
|
|
2618
2668
|
let mcpStatus = { connected: [], failed: [] };
|
|
2619
2669
|
const runtime = {
|
|
@@ -2625,13 +2675,14 @@ async function loadBaseRuntime(owner = captureRuntimeOwner()) {
|
|
|
2625
2675
|
skillManager,
|
|
2626
2676
|
mcpManager,
|
|
2627
2677
|
mcpStatus,
|
|
2628
|
-
|
|
2629
|
-
|
|
2678
|
+
configuredMcpConfig,
|
|
2679
|
+
effectiveMcpConfig,
|
|
2680
|
+
loading: effectiveMcpConfig.servers.length > 0,
|
|
2630
2681
|
status: {
|
|
2631
2682
|
skills: skillManager.size,
|
|
2632
2683
|
mcpServers: [],
|
|
2633
2684
|
mcpFailed: [],
|
|
2634
|
-
mcpSkipped:
|
|
2685
|
+
mcpSkipped: effectiveMcpConfig.skipped || [],
|
|
2635
2686
|
tools: ownerSession.toolRegistry?.size || 0,
|
|
2636
2687
|
},
|
|
2637
2688
|
};
|
|
@@ -2649,9 +2700,9 @@ async function loadBaseRuntime(owner = captureRuntimeOwner()) {
|
|
|
2649
2700
|
hydrateYeaftStatusFromSession(ownerSession, { reason: 'base_runtime_skills', emitEvent: true });
|
|
2650
2701
|
}
|
|
2651
2702
|
|
|
2652
|
-
if (
|
|
2703
|
+
if (effectiveMcpConfig.servers.length > 0) {
|
|
2653
2704
|
try {
|
|
2654
|
-
mcpStatus = await mcpManager.connectAll(
|
|
2705
|
+
mcpStatus = await mcpManager.connectAll(effectiveMcpConfig.servers);
|
|
2655
2706
|
} catch (err) {
|
|
2656
2707
|
runtime.loading = false;
|
|
2657
2708
|
if (ownerSession.skillManager === skillManager) ownerSession.skillManager = previousSkillManager;
|
|
@@ -2672,7 +2723,7 @@ async function loadBaseRuntime(owner = captureRuntimeOwner()) {
|
|
|
2672
2723
|
...runtime.status,
|
|
2673
2724
|
mcpServers: mcpStatus.connected,
|
|
2674
2725
|
mcpFailed: mcpStatus.failed,
|
|
2675
|
-
mcpSkipped:
|
|
2726
|
+
mcpSkipped: effectiveMcpConfig.skipped || [],
|
|
2676
2727
|
tools: ownerSession.toolRegistry?.size || 0,
|
|
2677
2728
|
};
|
|
2678
2729
|
ownerSession.mcpManager = mcpManager;
|
|
@@ -2681,7 +2732,7 @@ async function loadBaseRuntime(owner = captureRuntimeOwner()) {
|
|
|
2681
2732
|
activateBaseRuntime(owner, { reloadSkills: false });
|
|
2682
2733
|
hydrateYeaftStatusFromSession(ownerSession, { reason: 'base_runtime_mcp', emitEvent: true });
|
|
2683
2734
|
if (isCurrentRuntimeOwner(owner)) {
|
|
2684
|
-
try { broadcastMcpUpdated({ reason: 'base-runtime-load' }); } catch { /* best-effort */ }
|
|
2735
|
+
try { await broadcastMcpUpdated({ reason: 'base-runtime-load' }); } catch { /* best-effort */ }
|
|
2685
2736
|
}
|
|
2686
2737
|
}
|
|
2687
2738
|
}
|
|
@@ -2689,6 +2740,10 @@ async function loadBaseRuntime(owner = captureRuntimeOwner()) {
|
|
|
2689
2740
|
return isCurrentRuntimeOwner(owner) && baseRuntime === runtime ? runtime : null;
|
|
2690
2741
|
}
|
|
2691
2742
|
|
|
2743
|
+
function loadBaseRuntime(owner = captureRuntimeOwner()) {
|
|
2744
|
+
return enqueueMcpTransition(() => runBaseRuntimeTransition(owner));
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2692
2747
|
function scheduleBaseRuntimeLoad() {
|
|
2693
2748
|
const owner = captureRuntimeOwner();
|
|
2694
2749
|
if (!owner) return null;
|
|
@@ -2712,7 +2767,7 @@ function scheduleBaseRuntimeLoad() {
|
|
|
2712
2767
|
return promise;
|
|
2713
2768
|
}
|
|
2714
2769
|
|
|
2715
|
-
async function
|
|
2770
|
+
async function runProjectRuntimeTransition(workDir, owner = captureRuntimeOwner()) {
|
|
2716
2771
|
if (!isCurrentRuntimeOwner(owner)) return null;
|
|
2717
2772
|
const ownerSession = owner.ownerSession;
|
|
2718
2773
|
const normalizedWorkDir = normalizeSessionWorkDir(workDir);
|
|
@@ -2732,7 +2787,11 @@ async function loadProjectRuntime(workDir, owner = captureRuntimeOwner()) {
|
|
|
2732
2787
|
? `${process.cwd()}${delimiter}${normalizedWorkDir}`
|
|
2733
2788
|
: normalizedWorkDir;
|
|
2734
2789
|
const skillManager = createRuntimeSkillManager(yeaftDir, skillRoots);
|
|
2735
|
-
const
|
|
2790
|
+
const rawMcpConfig = loadRuntimeMcpConfig(yeaftDir, undefined, normalizedWorkDir);
|
|
2791
|
+
const { configured: configuredMcpConfig, effective: effectiveMcpConfig } = resolveMcpPluginPolicy(
|
|
2792
|
+
rawMcpConfig,
|
|
2793
|
+
ownerSession.config?.plugins,
|
|
2794
|
+
);
|
|
2736
2795
|
const mcpManager = createRuntimeMcpManager();
|
|
2737
2796
|
let mcpStatus = { connected: [], failed: [] };
|
|
2738
2797
|
const runtime = {
|
|
@@ -2742,13 +2801,14 @@ async function loadProjectRuntime(workDir, owner = captureRuntimeOwner()) {
|
|
|
2742
2801
|
skillManager,
|
|
2743
2802
|
mcpManager,
|
|
2744
2803
|
mcpStatus,
|
|
2745
|
-
|
|
2746
|
-
|
|
2804
|
+
configuredMcpConfig,
|
|
2805
|
+
effectiveMcpConfig,
|
|
2806
|
+
loading: effectiveMcpConfig.servers.length > 0,
|
|
2747
2807
|
status: {
|
|
2748
2808
|
skills: skillManager.size,
|
|
2749
2809
|
mcpServers: [],
|
|
2750
2810
|
mcpFailed: [],
|
|
2751
|
-
mcpSkipped:
|
|
2811
|
+
mcpSkipped: effectiveMcpConfig.skipped || [],
|
|
2752
2812
|
tools: ownerSession.toolRegistry?.size || 0,
|
|
2753
2813
|
},
|
|
2754
2814
|
};
|
|
@@ -2760,9 +2820,9 @@ async function loadProjectRuntime(workDir, owner = captureRuntimeOwner()) {
|
|
|
2760
2820
|
// Skill metadata is ready before external MCP startup. Activation is still
|
|
2761
2821
|
// owner-gated so reset cannot publish this runtime into a replacement session.
|
|
2762
2822
|
activateProjectRuntime(runtime, owner, { reloadSkills: false });
|
|
2763
|
-
if (
|
|
2823
|
+
if (effectiveMcpConfig.servers.length > 0) {
|
|
2764
2824
|
try {
|
|
2765
|
-
mcpStatus = await mcpManager.connectAll(
|
|
2825
|
+
mcpStatus = await mcpManager.connectAll(effectiveMcpConfig.servers);
|
|
2766
2826
|
} catch (err) {
|
|
2767
2827
|
runtime.loading = false;
|
|
2768
2828
|
await disconnectRuntimeMcpManager(mcpManager);
|
|
@@ -2779,7 +2839,7 @@ async function loadProjectRuntime(workDir, owner = captureRuntimeOwner()) {
|
|
|
2779
2839
|
...runtime.status,
|
|
2780
2840
|
mcpServers: mcpStatus.connected,
|
|
2781
2841
|
mcpFailed: mcpStatus.failed,
|
|
2782
|
-
mcpSkipped:
|
|
2842
|
+
mcpSkipped: effectiveMcpConfig.skipped || [],
|
|
2783
2843
|
tools: ownerSession.toolRegistry?.size || 0,
|
|
2784
2844
|
};
|
|
2785
2845
|
if (activeRuntimeKey === key) activateProjectRuntime(runtime, owner, { reloadSkills: false });
|
|
@@ -2787,6 +2847,10 @@ async function loadProjectRuntime(workDir, owner = captureRuntimeOwner()) {
|
|
|
2787
2847
|
return runtimeBelongsToOwner(runtime, owner) && projectRuntimes.get(key) === runtime ? runtime : null;
|
|
2788
2848
|
}
|
|
2789
2849
|
|
|
2850
|
+
function loadProjectRuntime(workDir, owner = captureRuntimeOwner()) {
|
|
2851
|
+
return enqueueMcpTransition(() => runProjectRuntimeTransition(workDir, owner));
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2790
2854
|
function scheduleProjectRuntimeLoad(workDir) {
|
|
2791
2855
|
const owner = captureRuntimeOwner();
|
|
2792
2856
|
const normalizedWorkDir = normalizeSessionWorkDir(workDir);
|
|
@@ -2818,6 +2882,7 @@ function getProjectRuntimeForTurn(sessionMeta) {
|
|
|
2818
2882
|
if (!owner) return null;
|
|
2819
2883
|
const workDir = normalizeSessionWorkDir(sessionMeta?.workDir);
|
|
2820
2884
|
if (!workDir) {
|
|
2885
|
+
if (!runtimeBelongsToOwner(baseRuntime, owner)) scheduleBaseRuntimeLoad();
|
|
2821
2886
|
activateBaseRuntime(owner);
|
|
2822
2887
|
return null;
|
|
2823
2888
|
}
|
|
@@ -3169,6 +3234,64 @@ function sendSessionCrudResult(payload) {
|
|
|
3169
3234
|
sendSessionEvent({ type: 'session_crud_result', ...next });
|
|
3170
3235
|
}
|
|
3171
3236
|
|
|
3237
|
+
function isMcpServerEnabled(name, plugins = session?.config?.plugins) {
|
|
3238
|
+
return !Array.isArray(plugins?.mcpServers) || plugins.mcpServers.includes(name);
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
function resolvePluginCatalogRuntime(workDir = '') {
|
|
3242
|
+
const owner = captureRuntimeOwner();
|
|
3243
|
+
const normalizedWorkDir = normalizeSessionWorkDir(workDir);
|
|
3244
|
+
const runtime = normalizedWorkDir
|
|
3245
|
+
? projectRuntimes.get(projectRuntimeKey(normalizedWorkDir))
|
|
3246
|
+
: null;
|
|
3247
|
+
const active = runtimeBelongsToOwner(runtime, owner)
|
|
3248
|
+
? runtime
|
|
3249
|
+
: (baseRuntime && runtimeBelongsToOwner(baseRuntime, owner) ? baseRuntime : null);
|
|
3250
|
+
return {
|
|
3251
|
+
toolRegistry: session?.toolRegistry || null,
|
|
3252
|
+
skillManager: active?.skillManager || session?.skillManager || null,
|
|
3253
|
+
mcpManager: active?.mcpManager || session?.mcpManager || null,
|
|
3254
|
+
};
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
function loadPluginCatalogMcpConfig(yeaftDir) {
|
|
3258
|
+
// The Plugins catalog is Agent-owned. Re-read its raw configuration on every
|
|
3259
|
+
// request so MCP CRUD is visible immediately, while keeping the
|
|
3260
|
+
// config.json -> mcp.json compatibility fallback.
|
|
3261
|
+
return loadAgentMCPConfig(yeaftDir);
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
/** Test-only: read the MCP catalog source without sending a bridge response. */
|
|
3265
|
+
export function __testLoadPluginCatalogMcpConfig(yeaftDir) {
|
|
3266
|
+
return loadPluginCatalogMcpConfig(yeaftDir);
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
export function handleYeaftPluginCatalog(msg = {}) {
|
|
3270
|
+
const requestId = msg.requestId || null;
|
|
3271
|
+
const requestedWorkDir = normalizeSessionWorkDir(msg.workDir);
|
|
3272
|
+
try {
|
|
3273
|
+
const runtime = resolvePluginCatalogRuntime(requestedWorkDir);
|
|
3274
|
+
const yeaftDir = ctx.CONFIG?.yeaftDir || session?.yeaftDir || DEFAULT_YEAFT_DIR;
|
|
3275
|
+
runtime.mcpConfig = loadPluginCatalogMcpConfig(yeaftDir);
|
|
3276
|
+
const catalog = buildPluginCatalog(runtime);
|
|
3277
|
+
sendToServer({
|
|
3278
|
+
type: 'yeaft_plugin_catalog_result',
|
|
3279
|
+
requestId,
|
|
3280
|
+
...(msg._requestClientId ? { _requestClientId: msg._requestClientId } : {}),
|
|
3281
|
+
catalog,
|
|
3282
|
+
error: null,
|
|
3283
|
+
});
|
|
3284
|
+
} catch (err) {
|
|
3285
|
+
sendToServer({
|
|
3286
|
+
type: 'yeaft_plugin_catalog_result',
|
|
3287
|
+
requestId,
|
|
3288
|
+
...(msg._requestClientId ? { _requestClientId: msg._requestClientId } : {}),
|
|
3289
|
+
catalog: { tools: [], skills: [], mcpServers: [] },
|
|
3290
|
+
error: err?.message || String(err),
|
|
3291
|
+
});
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3172
3295
|
function sendSessionSnapshotBroadcast() {
|
|
3173
3296
|
try {
|
|
3174
3297
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
@@ -5343,15 +5466,17 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
|
|
|
5343
5466
|
escalationState.engineKey = threadKey(sessionId, vpId, threadId);
|
|
5344
5467
|
}
|
|
5345
5468
|
if (projectRuntime) {
|
|
5346
|
-
vpEngine.setRuntimeManagers?.(
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5469
|
+
vpEngine.setRuntimeManagers?.(effectiveRuntimeManagers(
|
|
5470
|
+
projectRuntime.skillManager,
|
|
5471
|
+
projectRuntime.mcpManager,
|
|
5472
|
+
session?.config?.plugins,
|
|
5473
|
+
));
|
|
5350
5474
|
} else {
|
|
5351
|
-
vpEngine.setRuntimeManagers?.(
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5475
|
+
vpEngine.setRuntimeManagers?.(effectiveRuntimeManagers(
|
|
5476
|
+
session?.skillManager || null,
|
|
5477
|
+
session?.mcpManager || null,
|
|
5478
|
+
session?.config?.plugins,
|
|
5479
|
+
));
|
|
5355
5480
|
}
|
|
5356
5481
|
if (thread) thread.engine = vpEngine;
|
|
5357
5482
|
|
|
@@ -7528,16 +7653,54 @@ export async function resetYeaftSession() {
|
|
|
7528
7653
|
* connection state next to the configured servers. Safe to call when
|
|
7529
7654
|
* the session hasn't been initialised yet — returns an empty runtime.
|
|
7530
7655
|
*/
|
|
7656
|
+
function activeMcpManager(owner = captureRuntimeOwner()) {
|
|
7657
|
+
if (!isCurrentRuntimeOwner(owner)) return session?.mcpManager || null;
|
|
7658
|
+
if (activeRuntimeKey !== BASE_RUNTIME_KEY) {
|
|
7659
|
+
const runtime = projectRuntimes.get(activeRuntimeKey) || null;
|
|
7660
|
+
if (runtimeBelongsToOwner(runtime, owner)) return runtime.mcpManager || null;
|
|
7661
|
+
}
|
|
7662
|
+
return owner.ownerSession.mcpManager || null;
|
|
7663
|
+
}
|
|
7664
|
+
|
|
7665
|
+
async function retireInactiveMcpRuntimes(owner, activeManager) {
|
|
7666
|
+
if (!isCurrentRuntimeOwner(owner)) return;
|
|
7667
|
+
const retire = [];
|
|
7668
|
+
|
|
7669
|
+
// The base manager stays cached while a project runtime is active. It was
|
|
7670
|
+
// built from the old Agent config, so remove it rather than allowing a later
|
|
7671
|
+
// base activation to revive a deleted MCP. The next base turn schedules a
|
|
7672
|
+
// fresh loader; until then there is deliberately no base MCP fallback.
|
|
7673
|
+
if (runtimeBelongsToOwner(baseRuntime, owner) && baseRuntime.mcpManager !== activeManager) {
|
|
7674
|
+
const staleBase = baseRuntime;
|
|
7675
|
+
baseRuntime = null;
|
|
7676
|
+
baseRuntimeLoadPromises.delete(BASE_RUNTIME_KEY);
|
|
7677
|
+
if (owner.ownerSession.mcpManager === staleBase.mcpManager) owner.ownerSession.mcpManager = null;
|
|
7678
|
+
// A loading manager may acquire its connection after this point. Its
|
|
7679
|
+
// post-connect ownership check performs the reliable cleanup in that case.
|
|
7680
|
+
if (!staleBase.loading) retire.push(disconnectRuntimeMcpManager(staleBase.mcpManager));
|
|
7681
|
+
}
|
|
7682
|
+
|
|
7683
|
+
for (const [key, runtime] of projectRuntimes) {
|
|
7684
|
+
if (!runtimeBelongsToOwner(runtime, owner) || runtime.mcpManager === activeManager) continue;
|
|
7685
|
+
projectRuntimes.delete(key);
|
|
7686
|
+
projectRuntimeLoadPromises.delete(key);
|
|
7687
|
+
if (!runtime.loading) retire.push(disconnectRuntimeMcpManager(runtime.mcpManager));
|
|
7688
|
+
}
|
|
7689
|
+
|
|
7690
|
+
await Promise.allSettled(retire);
|
|
7691
|
+
}
|
|
7692
|
+
|
|
7531
7693
|
function mcpRuntimeSnapshot() {
|
|
7532
|
-
|
|
7694
|
+
const mcpManager = activeMcpManager();
|
|
7695
|
+
if (!mcpManager) {
|
|
7533
7696
|
return { connected: false, toolCount: 0, perServer: [] };
|
|
7534
7697
|
}
|
|
7535
|
-
const status =
|
|
7536
|
-
const toolCount = typeof
|
|
7537
|
-
?
|
|
7698
|
+
const status = mcpManager.status() || [];
|
|
7699
|
+
const toolCount = typeof mcpManager.toolCount === 'number'
|
|
7700
|
+
? mcpManager.toolCount
|
|
7538
7701
|
: status.reduce((sum, s) => sum + (s.toolCount || 0), 0);
|
|
7539
7702
|
return {
|
|
7540
|
-
connected: !!
|
|
7703
|
+
connected: !!mcpManager.hasServers,
|
|
7541
7704
|
toolCount,
|
|
7542
7705
|
perServer: status.map(s => ({
|
|
7543
7706
|
name: s.name,
|
|
@@ -7553,23 +7716,34 @@ function mcpRuntimeSnapshot() {
|
|
|
7553
7716
|
* pick up the change.
|
|
7554
7717
|
*/
|
|
7555
7718
|
function hotSwapMcpTools() {
|
|
7556
|
-
|
|
7719
|
+
const owner = captureRuntimeOwner();
|
|
7720
|
+
return replaceSessionMcpTools(owner, activeMcpManager(owner));
|
|
7557
7721
|
}
|
|
7558
7722
|
|
|
7559
7723
|
/**
|
|
7560
7724
|
* Broadcast a `yeaft_mcp_updated` event so any client subscribed to the
|
|
7561
7725
|
* Yeaft view (Settings panel, status badge) refreshes without needing
|
|
7562
|
-
* to re-open the panel.
|
|
7563
|
-
*
|
|
7726
|
+
* to re-open the panel. A CRUD/reload caller should pass its confirmed
|
|
7727
|
+
* `configuredServers` snapshot rather than rereading config after async
|
|
7728
|
+
* runtime work. Fallback reads preserve an existing UI cache on failure by
|
|
7729
|
+
* omitting `servers` and reporting the strict-read error.
|
|
7564
7730
|
*/
|
|
7565
|
-
function broadcastMcpUpdated(extra = {}) {
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7731
|
+
function broadcastMcpUpdated({ configuredServers, ...extra } = {}) {
|
|
7732
|
+
let servers = configuredServers;
|
|
7733
|
+
let error = null;
|
|
7734
|
+
if (!Array.isArray(servers)) {
|
|
7735
|
+
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
7736
|
+
const listed = listMcpServers(yeaftDir);
|
|
7737
|
+
if (listed.error) error = listed.error;
|
|
7738
|
+
else servers = listed.servers;
|
|
7739
|
+
}
|
|
7740
|
+
|
|
7741
|
+
return sendToServer({
|
|
7569
7742
|
type: 'yeaft_mcp_updated',
|
|
7570
|
-
servers: listed.servers || [],
|
|
7571
7743
|
runtime: mcpRuntimeSnapshot(),
|
|
7572
7744
|
...extra,
|
|
7745
|
+
...(Array.isArray(servers) ? { servers } : {}),
|
|
7746
|
+
error,
|
|
7573
7747
|
});
|
|
7574
7748
|
}
|
|
7575
7749
|
|
|
@@ -7585,11 +7759,11 @@ export function handleYeaftMcpList(msg = {}) {
|
|
|
7585
7759
|
});
|
|
7586
7760
|
}
|
|
7587
7761
|
|
|
7588
|
-
|
|
7762
|
+
async function runYeaftMcpAddMutation(msg = {}) {
|
|
7589
7763
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
7590
7764
|
const result = upsertMcpServer(msg.server || {}, yeaftDir);
|
|
7591
7765
|
if (result.error) {
|
|
7592
|
-
sendToServer({
|
|
7766
|
+
await sendToServer({
|
|
7593
7767
|
type: 'yeaft_mcp_add_result',
|
|
7594
7768
|
requestId: msg.requestId || null,
|
|
7595
7769
|
servers: [],
|
|
@@ -7599,22 +7773,28 @@ export async function handleYeaftMcpAdd(msg = {}) {
|
|
|
7599
7773
|
return;
|
|
7600
7774
|
}
|
|
7601
7775
|
|
|
7602
|
-
//
|
|
7603
|
-
//
|
|
7604
|
-
//
|
|
7776
|
+
// A queued runtime bootstrap may have made a project manager active before
|
|
7777
|
+
// this mutation runs. Capture the manager after the config write, then
|
|
7778
|
+
// retire inactive caches and update the manager that owns live tools.
|
|
7779
|
+
const owner = captureRuntimeOwner();
|
|
7780
|
+
const mcpManager = activeMcpManager(owner);
|
|
7781
|
+
await retireInactiveMcpRuntimes(owner, mcpManager);
|
|
7605
7782
|
let connectError = null;
|
|
7606
|
-
if (
|
|
7783
|
+
if (mcpManager && !isMcpServerEnabled(result.server.name)) {
|
|
7784
|
+
try {
|
|
7785
|
+
await mcpManager.disconnect(result.server.name);
|
|
7786
|
+
} catch { /* no active connection to retire */ }
|
|
7787
|
+
} else if (mcpManager) {
|
|
7607
7788
|
try {
|
|
7608
|
-
await
|
|
7789
|
+
await mcpManager.connect(result.server);
|
|
7609
7790
|
} catch (err) {
|
|
7610
7791
|
connectError = err?.message || String(err);
|
|
7611
7792
|
console.warn(`[Yeaft] MCP connect "${result.server.name}" failed:`, connectError);
|
|
7612
7793
|
}
|
|
7613
7794
|
}
|
|
7614
7795
|
|
|
7615
|
-
const swap = replaceSessionMcpTools(
|
|
7616
|
-
|
|
7617
|
-
sendToServer({
|
|
7796
|
+
const swap = replaceSessionMcpTools(owner, mcpManager);
|
|
7797
|
+
await sendToServer({
|
|
7618
7798
|
type: 'yeaft_mcp_add_result',
|
|
7619
7799
|
requestId: msg.requestId || null,
|
|
7620
7800
|
servers: result.servers,
|
|
@@ -7623,15 +7803,24 @@ export async function handleYeaftMcpAdd(msg = {}) {
|
|
|
7623
7803
|
connectError,
|
|
7624
7804
|
error: null,
|
|
7625
7805
|
});
|
|
7626
|
-
broadcastMcpUpdated({
|
|
7806
|
+
await broadcastMcpUpdated({
|
|
7807
|
+
reason: 'add',
|
|
7808
|
+
name: result.server.name,
|
|
7809
|
+
connectError,
|
|
7810
|
+
configuredServers: result.servers,
|
|
7811
|
+
});
|
|
7627
7812
|
}
|
|
7628
7813
|
|
|
7629
|
-
export
|
|
7814
|
+
export function handleYeaftMcpAdd(msg = {}) {
|
|
7815
|
+
return enqueueMcpTransition(() => runYeaftMcpAddMutation(msg));
|
|
7816
|
+
}
|
|
7817
|
+
|
|
7818
|
+
async function runYeaftMcpRemoveMutation(msg = {}) {
|
|
7630
7819
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
7631
7820
|
const name = typeof msg.name === 'string' ? msg.name : '';
|
|
7632
7821
|
const result = removeMcpServer(name, yeaftDir);
|
|
7633
7822
|
if (result.error) {
|
|
7634
|
-
sendToServer({
|
|
7823
|
+
await sendToServer({
|
|
7635
7824
|
type: 'yeaft_mcp_remove_result',
|
|
7636
7825
|
requestId: msg.requestId || null,
|
|
7637
7826
|
servers: [],
|
|
@@ -7641,17 +7830,19 @@ export async function handleYeaftMcpRemove(msg = {}) {
|
|
|
7641
7830
|
return;
|
|
7642
7831
|
}
|
|
7643
7832
|
|
|
7644
|
-
|
|
7833
|
+
const owner = captureRuntimeOwner();
|
|
7834
|
+
const mcpManager = activeMcpManager(owner);
|
|
7835
|
+
await retireInactiveMcpRuntimes(owner, mcpManager);
|
|
7836
|
+
if (mcpManager) {
|
|
7645
7837
|
try {
|
|
7646
|
-
await
|
|
7838
|
+
await mcpManager.disconnect(name);
|
|
7647
7839
|
} catch (err) {
|
|
7648
7840
|
console.warn(`[Yeaft] MCP disconnect "${name}" failed:`, err?.message || err);
|
|
7649
7841
|
}
|
|
7650
7842
|
}
|
|
7651
7843
|
|
|
7652
|
-
const swap = replaceSessionMcpTools(
|
|
7653
|
-
|
|
7654
|
-
sendToServer({
|
|
7844
|
+
const swap = replaceSessionMcpTools(owner, mcpManager);
|
|
7845
|
+
await sendToServer({
|
|
7655
7846
|
type: 'yeaft_mcp_remove_result',
|
|
7656
7847
|
requestId: msg.requestId || null,
|
|
7657
7848
|
servers: result.servers,
|
|
@@ -7660,46 +7851,71 @@ export async function handleYeaftMcpRemove(msg = {}) {
|
|
|
7660
7851
|
swap,
|
|
7661
7852
|
error: null,
|
|
7662
7853
|
});
|
|
7663
|
-
broadcastMcpUpdated({
|
|
7854
|
+
await broadcastMcpUpdated({
|
|
7855
|
+
reason: 'remove',
|
|
7856
|
+
name,
|
|
7857
|
+
configuredServers: result.servers,
|
|
7858
|
+
});
|
|
7859
|
+
}
|
|
7860
|
+
|
|
7861
|
+
export function handleYeaftMcpRemove(msg = {}) {
|
|
7862
|
+
return enqueueMcpTransition(() => runYeaftMcpRemoveMutation(msg));
|
|
7664
7863
|
}
|
|
7665
7864
|
|
|
7666
|
-
|
|
7865
|
+
async function runYeaftMcpReloadMutation(msg = {}) {
|
|
7667
7866
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
7668
7867
|
const targetName = typeof msg.name === 'string' && msg.name ? msg.name : null;
|
|
7868
|
+
const listed = listMcpServers(yeaftDir);
|
|
7869
|
+
|
|
7870
|
+
// Do not turn a failed strict config read into an empty successful reload.
|
|
7871
|
+
// In particular, leave an existing MCP runtime and its flattened tools alone
|
|
7872
|
+
// until the user repairs config.json.
|
|
7873
|
+
if (listed.error) {
|
|
7874
|
+
await sendToServer({
|
|
7875
|
+
type: 'yeaft_mcp_reload_result',
|
|
7876
|
+
requestId: msg.requestId || null,
|
|
7877
|
+
servers: [],
|
|
7878
|
+
runtime: mcpRuntimeSnapshot(),
|
|
7879
|
+
error: listed.error,
|
|
7880
|
+
});
|
|
7881
|
+
return;
|
|
7882
|
+
}
|
|
7669
7883
|
|
|
7670
|
-
|
|
7671
|
-
|
|
7884
|
+
const owner = captureRuntimeOwner();
|
|
7885
|
+
const mcpManager = activeMcpManager(owner);
|
|
7886
|
+
if (!mcpManager) {
|
|
7887
|
+
// Session not yet alive; just echo the current config + an empty
|
|
7672
7888
|
// runtime so the UI knows to wait for session boot.
|
|
7673
|
-
|
|
7674
|
-
sendToServer({
|
|
7889
|
+
await sendToServer({
|
|
7675
7890
|
type: 'yeaft_mcp_reload_result',
|
|
7676
7891
|
requestId: msg.requestId || null,
|
|
7677
|
-
servers: listed.servers
|
|
7892
|
+
servers: listed.servers,
|
|
7678
7893
|
runtime: mcpRuntimeSnapshot(),
|
|
7679
7894
|
error: null,
|
|
7680
7895
|
});
|
|
7681
7896
|
return;
|
|
7682
7897
|
}
|
|
7683
7898
|
|
|
7684
|
-
|
|
7685
|
-
|
|
7899
|
+
// Full reload applies an externally changed Agent MCP config, so stale
|
|
7900
|
+
// runtime caches are just as unsafe as after add/remove. A named reload is
|
|
7901
|
+
// only a local reconnect and must keep inactive base/project caches warm.
|
|
7902
|
+
if (!targetName) await retireInactiveMcpRuntimes(owner, mcpManager);
|
|
7686
7903
|
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
// config.json. The latter is what the user clicks "Reload all" for.
|
|
7904
|
+
const configured = listed.servers;
|
|
7905
|
+
const enabled = configured.filter(server => isMcpServerEnabled(server.name));
|
|
7690
7906
|
const failures = [];
|
|
7691
7907
|
try {
|
|
7692
7908
|
if (targetName) {
|
|
7693
|
-
const cfg =
|
|
7694
|
-
try { await
|
|
7909
|
+
const cfg = enabled.find(server => server.name === targetName);
|
|
7910
|
+
try { await mcpManager.disconnect(targetName); } catch { /* ignore */ }
|
|
7695
7911
|
if (cfg) {
|
|
7696
|
-
try { await
|
|
7912
|
+
try { await mcpManager.connect(cfg); }
|
|
7697
7913
|
catch (err) { failures.push({ name: targetName, error: err?.message || String(err) }); }
|
|
7698
7914
|
}
|
|
7699
7915
|
} else {
|
|
7700
|
-
try { await
|
|
7701
|
-
for (const cfg of
|
|
7702
|
-
try { await
|
|
7916
|
+
try { await mcpManager.disconnectAll(); } catch { /* ignore */ }
|
|
7917
|
+
for (const cfg of enabled) {
|
|
7918
|
+
try { await mcpManager.connect(cfg); }
|
|
7703
7919
|
catch (err) { failures.push({ name: cfg.name, error: err?.message || String(err) }); }
|
|
7704
7920
|
}
|
|
7705
7921
|
}
|
|
@@ -7707,9 +7923,8 @@ export async function handleYeaftMcpReload(msg = {}) {
|
|
|
7707
7923
|
console.warn('[Yeaft] MCP reload failed:', err?.message || err);
|
|
7708
7924
|
}
|
|
7709
7925
|
|
|
7710
|
-
const swap = replaceSessionMcpTools(
|
|
7711
|
-
|
|
7712
|
-
sendToServer({
|
|
7926
|
+
const swap = replaceSessionMcpTools(owner, mcpManager);
|
|
7927
|
+
await sendToServer({
|
|
7713
7928
|
type: 'yeaft_mcp_reload_result',
|
|
7714
7929
|
requestId: msg.requestId || null,
|
|
7715
7930
|
servers: configured,
|
|
@@ -7718,7 +7933,16 @@ export async function handleYeaftMcpReload(msg = {}) {
|
|
|
7718
7933
|
swap,
|
|
7719
7934
|
error: null,
|
|
7720
7935
|
});
|
|
7721
|
-
broadcastMcpUpdated({
|
|
7936
|
+
await broadcastMcpUpdated({
|
|
7937
|
+
reason: 'reload',
|
|
7938
|
+
name: targetName,
|
|
7939
|
+
failures,
|
|
7940
|
+
configuredServers: configured,
|
|
7941
|
+
});
|
|
7942
|
+
}
|
|
7943
|
+
|
|
7944
|
+
export function handleYeaftMcpReload(msg = {}) {
|
|
7945
|
+
return enqueueMcpTransition(() => runYeaftMcpReloadMutation(msg));
|
|
7722
7946
|
}
|
|
7723
7947
|
|
|
7724
7948
|
export const __testHooks = {
|
|
@@ -7742,6 +7966,9 @@ export const __testHooks = {
|
|
|
7742
7966
|
setSessionForTest(nextSession) {
|
|
7743
7967
|
__testSetSession(nextSession || null);
|
|
7744
7968
|
},
|
|
7969
|
+
broadcastMcpUpdatedForTest(extra) {
|
|
7970
|
+
return broadcastMcpUpdated(extra);
|
|
7971
|
+
},
|
|
7745
7972
|
setRuntimeFactoriesForTest({
|
|
7746
7973
|
createSkillManager: nextCreateSkillManager,
|
|
7747
7974
|
createMcpManager: nextCreateMcpManager,
|
|
@@ -7888,7 +8115,8 @@ export const __testHooks = {
|
|
|
7888
8115
|
skillManager: runtime?.skillManager || { list: () => [] },
|
|
7889
8116
|
mcpManager: runtime?.mcpManager || { listTools: () => [], disconnectAll: async () => {} },
|
|
7890
8117
|
mcpStatus: runtime?.mcpStatus || { connected: [], failed: [] },
|
|
7891
|
-
|
|
8118
|
+
configuredMcpConfig: runtime?.configuredMcpConfig || runtime?.mcpConfig || { servers: [], skipped: [] },
|
|
8119
|
+
effectiveMcpConfig: runtime?.effectiveMcpConfig || runtime?.mcpConfig || { servers: [], skipped: [] },
|
|
7892
8120
|
status: runtime?.status || { skills: 0, mcpServers: [], mcpFailed: [], mcpSkipped: [], tools: 0 },
|
|
7893
8121
|
};
|
|
7894
8122
|
projectRuntimes.set(projectRuntimeKey(normalizedWorkDir), seeded);
|