wave-agent-sdk 0.16.9 → 0.16.12
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/dist/agent.d.ts +5 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +18 -0
- package/dist/constants/toolLimits.d.ts +2 -0
- package/dist/constants/toolLimits.d.ts.map +1 -1
- package/dist/constants/toolLimits.js +2 -0
- package/dist/managers/aiManager.d.ts +5 -0
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +21 -0
- package/dist/managers/hookManager.d.ts +6 -3
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +36 -13
- package/dist/managers/mcpManager.d.ts +4 -28
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +10 -127
- package/dist/services/authService.d.ts +33 -1
- package/dist/services/authService.d.ts.map +1 -1
- package/dist/services/authService.js +212 -11
- package/dist/services/configurationService.d.ts +1 -0
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +48 -6
- package/dist/services/hook.d.ts +4 -0
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +10 -0
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +11 -0
- package/dist/services/interactionService.d.ts.map +1 -1
- package/dist/services/interactionService.js +0 -12
- package/dist/services/remoteSettingsService.d.ts +21 -0
- package/dist/services/remoteSettingsService.d.ts.map +1 -0
- package/dist/services/remoteSettingsService.js +280 -0
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +58 -32
- package/dist/tools/types.d.ts +4 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/agent.d.ts +7 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/auth.d.ts +12 -0
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +20 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +5 -1
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +1 -0
- package/dist/types/mcp.d.ts +1 -1
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +9 -8
- package/dist/utils/gitUtils.d.ts +18 -1
- package/dist/utils/gitUtils.d.ts.map +1 -1
- package/dist/utils/gitUtils.js +120 -49
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +6 -1
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +4 -2
- package/dist/utils/toolResultStorage.d.ts +46 -0
- package/dist/utils/toolResultStorage.d.ts.map +1 -0
- package/dist/utils/toolResultStorage.js +90 -0
- package/dist/utils/worktreeUtils.d.ts.map +1 -1
- package/dist/utils/worktreeUtils.js +58 -0
- package/package.json +3 -3
- package/src/agent.ts +20 -0
- package/src/constants/toolLimits.ts +3 -0
- package/src/managers/aiManager.ts +37 -0
- package/src/managers/hookManager.ts +42 -17
- package/src/managers/mcpManager.ts +10 -178
- package/src/services/authService.ts +243 -16
- package/src/services/configurationService.ts +58 -6
- package/src/services/hook.ts +15 -0
- package/src/services/initializationService.ts +13 -0
- package/src/services/interactionService.ts +0 -18
- package/src/services/remoteSettingsService.ts +315 -0
- package/src/tools/bashTool.ts +70 -38
- package/src/tools/types.ts +4 -0
- package/src/types/agent.ts +7 -0
- package/src/types/auth.ts +10 -0
- package/src/types/configuration.ts +23 -0
- package/src/types/hooks.ts +7 -1
- package/src/types/mcp.ts +1 -1
- package/src/utils/containerSetup.ts +8 -8
- package/src/utils/gitUtils.ts +123 -48
- package/src/utils/mcpUtils.ts +12 -1
- package/src/utils/openaiClient.ts +5 -2
- package/src/utils/toolResultStorage.ts +117 -0
- package/src/utils/worktreeUtils.ts +63 -0
|
@@ -40,6 +40,7 @@ import { logOTelEvent } from "../telemetry/events.js";
|
|
|
40
40
|
export interface AIManagerCallbacks {
|
|
41
41
|
onCompactionStateChange?: (isCompacting: boolean) => void;
|
|
42
42
|
onUsageAdded?: (usage: Usage) => void;
|
|
43
|
+
onCwdChange?: (newCwd: string) => void;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export interface AIManagerOptions {
|
|
@@ -64,6 +65,8 @@ export class AIManager {
|
|
|
64
65
|
private subagentType?: string; // Store subagent type for hook context
|
|
65
66
|
private stream: boolean; // Streaming mode flag
|
|
66
67
|
private modelOverride?: string;
|
|
68
|
+
private _onCwdChange?: (newCwd: string) => void; // Store callback for CWD changes
|
|
69
|
+
private originalWorkdir: string;
|
|
67
70
|
private consecutiveCompactionFailures: number = 0;
|
|
68
71
|
private readonly maxTurns?: number;
|
|
69
72
|
|
|
@@ -77,6 +80,8 @@ export class AIManager {
|
|
|
77
80
|
this.stream = options.stream ?? true; // Default to true if not specified
|
|
78
81
|
this.callbacks = options.callbacks ?? {};
|
|
79
82
|
this.modelOverride = options.modelOverride;
|
|
83
|
+
this._onCwdChange = options.callbacks?.onCwdChange; // Initialize onCwdChange
|
|
84
|
+
this.originalWorkdir = options.workdir;
|
|
80
85
|
this.maxTurns = options.maxTurns;
|
|
81
86
|
}
|
|
82
87
|
|
|
@@ -174,6 +179,10 @@ export class AIManager {
|
|
|
174
179
|
return this.container.get<string>("Workdir") ?? process.cwd();
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
public getOriginalWorkdir(): string {
|
|
183
|
+
return this.originalWorkdir;
|
|
184
|
+
}
|
|
185
|
+
|
|
177
186
|
/**
|
|
178
187
|
* Update the working directory mid-session (e.g., when entering/exiting a worktree).
|
|
179
188
|
* Also updates process.chdir() so bash commands use the new directory.
|
|
@@ -183,6 +192,10 @@ export class AIManager {
|
|
|
183
192
|
process.chdir(newWorkdir);
|
|
184
193
|
}
|
|
185
194
|
|
|
195
|
+
public setOnCwdChange(callback: (newCwd: string) => void): void {
|
|
196
|
+
this._onCwdChange = callback;
|
|
197
|
+
}
|
|
198
|
+
|
|
186
199
|
private isCompacting: boolean = false;
|
|
187
200
|
private callbacks: AIManagerCallbacks;
|
|
188
201
|
|
|
@@ -248,6 +261,7 @@ export class AIManager {
|
|
|
248
261
|
if (toolPlugin?.formatCompactParams) {
|
|
249
262
|
const context: ToolContext = {
|
|
250
263
|
workdir: this.getWorkdir(),
|
|
264
|
+
originalWorkdir: this.originalWorkdir,
|
|
251
265
|
taskManager: this.taskManager,
|
|
252
266
|
};
|
|
253
267
|
return toolPlugin.formatCompactParams(toolArgs, context);
|
|
@@ -998,6 +1012,7 @@ export class AIManager {
|
|
|
998
1012
|
abortSignal: toolAbortController.signal,
|
|
999
1013
|
backgroundTaskManager: this.backgroundTaskManager,
|
|
1000
1014
|
workdir: this.getWorkdir(),
|
|
1015
|
+
originalWorkdir: this.originalWorkdir,
|
|
1001
1016
|
messageId: this.messageManager.getMessages().slice(-1)[0]?.id,
|
|
1002
1017
|
sessionId: this.messageManager.getSessionId(),
|
|
1003
1018
|
toolCallId: toolId,
|
|
@@ -1016,6 +1031,28 @@ export class AIManager {
|
|
|
1016
1031
|
stage: "running", // Keep it in running stage while updating result
|
|
1017
1032
|
});
|
|
1018
1033
|
},
|
|
1034
|
+
onCwdChange: async (newCwd: string) => {
|
|
1035
|
+
const oldCwd = this.getWorkdir();
|
|
1036
|
+
this.container.register("Workdir", newCwd);
|
|
1037
|
+
this._onCwdChange?.(newCwd);
|
|
1038
|
+
if (this.hookManager) {
|
|
1039
|
+
const sessionId = this.messageManager.getSessionId();
|
|
1040
|
+
const transcriptPath =
|
|
1041
|
+
this.messageManager.getTranscriptPath();
|
|
1042
|
+
const env = Object.fromEntries(
|
|
1043
|
+
Object.entries(process.env).filter(
|
|
1044
|
+
(e) => e[1] !== undefined,
|
|
1045
|
+
),
|
|
1046
|
+
) as Record<string, string>;
|
|
1047
|
+
await this.hookManager.executeCwdChangedHooks(
|
|
1048
|
+
oldCwd,
|
|
1049
|
+
newCwd,
|
|
1050
|
+
sessionId,
|
|
1051
|
+
transcriptPath,
|
|
1052
|
+
env,
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1019
1056
|
};
|
|
1020
1057
|
|
|
1021
1058
|
// Execute tool
|
|
@@ -44,23 +44,13 @@ export class HookManager {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* Load
|
|
48
|
-
* Project settings take precedence over user settings
|
|
47
|
+
* Load hook configuration from programmatic source (AgentOptions.hooks)
|
|
49
48
|
*/
|
|
50
|
-
loadConfiguration(
|
|
51
|
-
userHooks?: PartialHookConfiguration,
|
|
52
|
-
projectHooks?: PartialHookConfiguration,
|
|
53
|
-
): void {
|
|
49
|
+
loadConfiguration(hooks?: PartialHookConfiguration): void {
|
|
54
50
|
const merged: PartialHookConfiguration = {};
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.mergeHooksConfiguration(merged, userHooks);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Override with project hooks (project settings take precedence)
|
|
62
|
-
if (projectHooks) {
|
|
63
|
-
this.mergeHooksConfiguration(merged, projectHooks);
|
|
52
|
+
if (hooks) {
|
|
53
|
+
this.mergeHooksConfiguration(merged, hooks);
|
|
64
54
|
}
|
|
65
55
|
|
|
66
56
|
// Validate merged configuration
|
|
@@ -654,9 +644,12 @@ export class HookManager {
|
|
|
654
644
|
): void {
|
|
655
645
|
for (const [event, configs] of Object.entries(source)) {
|
|
656
646
|
if (isValidHookEvent(event)) {
|
|
657
|
-
//
|
|
658
|
-
|
|
659
|
-
|
|
647
|
+
// Concatenate hook configs so multiple sources (programmatic, file-based, plugins) coexist
|
|
648
|
+
if (!target[event]) {
|
|
649
|
+
target[event] = [...configs];
|
|
650
|
+
} else {
|
|
651
|
+
target[event] = [...target[event], ...configs];
|
|
652
|
+
}
|
|
660
653
|
}
|
|
661
654
|
}
|
|
662
655
|
}
|
|
@@ -676,6 +669,7 @@ export class HookManager {
|
|
|
676
669
|
event === "SubagentStop" ||
|
|
677
670
|
event === "WorktreeCreate" ||
|
|
678
671
|
event === "WorktreeRemove" ||
|
|
672
|
+
event === "CwdChanged" ||
|
|
679
673
|
event === "SessionStart" ||
|
|
680
674
|
event === "SessionEnd"
|
|
681
675
|
) {
|
|
@@ -781,6 +775,7 @@ export class HookManager {
|
|
|
781
775
|
PermissionRequest: 0,
|
|
782
776
|
WorktreeCreate: 0,
|
|
783
777
|
WorktreeRemove: 0,
|
|
778
|
+
CwdChanged: 0,
|
|
784
779
|
SessionStart: 0,
|
|
785
780
|
SessionEnd: 0,
|
|
786
781
|
},
|
|
@@ -796,6 +791,7 @@ export class HookManager {
|
|
|
796
791
|
PermissionRequest: 0,
|
|
797
792
|
WorktreeCreate: 0,
|
|
798
793
|
WorktreeRemove: 0,
|
|
794
|
+
CwdChanged: 0,
|
|
799
795
|
SessionStart: 0,
|
|
800
796
|
SessionEnd: 0,
|
|
801
797
|
};
|
|
@@ -822,6 +818,35 @@ export class HookManager {
|
|
|
822
818
|
};
|
|
823
819
|
}
|
|
824
820
|
|
|
821
|
+
/**
|
|
822
|
+
* Execute CwdChanged hooks.
|
|
823
|
+
*/
|
|
824
|
+
async executeCwdChangedHooks(
|
|
825
|
+
oldCwd: string,
|
|
826
|
+
newCwd: string,
|
|
827
|
+
sessionId: string,
|
|
828
|
+
transcriptPath: string,
|
|
829
|
+
env: Record<string, string>,
|
|
830
|
+
): Promise<HookExecutionResult[]> {
|
|
831
|
+
const context: ExtendedHookExecutionContext = {
|
|
832
|
+
event: "CwdChanged",
|
|
833
|
+
projectDir: this.workdir,
|
|
834
|
+
timestamp: new Date(),
|
|
835
|
+
sessionId,
|
|
836
|
+
transcriptPath,
|
|
837
|
+
cwd: newCwd,
|
|
838
|
+
oldCwd,
|
|
839
|
+
newCwd,
|
|
840
|
+
env,
|
|
841
|
+
};
|
|
842
|
+
const results = await this.executeHooks("CwdChanged", context);
|
|
843
|
+
if (results.length > 0) {
|
|
844
|
+
// For CwdChanged hooks, we don't block, just log errors
|
|
845
|
+
this.processHookResults("CwdChanged", results);
|
|
846
|
+
}
|
|
847
|
+
return results;
|
|
848
|
+
}
|
|
849
|
+
|
|
825
850
|
/**
|
|
826
851
|
* Register hooks provided by a plugin
|
|
827
852
|
*/
|
|
@@ -34,27 +34,19 @@ export interface McpManagerOptions {
|
|
|
34
34
|
logger?: Logger;
|
|
35
35
|
/** Pre-configured MCP servers passed from constructor options */
|
|
36
36
|
mcpServers?: Record<string, McpServerConfig>;
|
|
37
|
-
/** Wave server URL for resolving ${WAVE_SERVER_URL} templates in MCP configs */
|
|
38
|
-
serverUrl?: string;
|
|
39
|
-
/** SSO token for resolving ${WAVE_SSO_TOKEN} templates in MCP configs */
|
|
40
|
-
ssoToken?: string;
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
/**
|
|
44
40
|
* Expand environment variables in a string value.
|
|
45
41
|
* Supports ${VAR} and ${VAR:-default} patterns.
|
|
46
42
|
*/
|
|
47
|
-
const WAVE_TEMPLATE_VARS = [
|
|
48
|
-
"WAVE_SERVER_URL",
|
|
49
|
-
"WAVE_SSO_TOKEN",
|
|
50
|
-
"WAVE_PLUGIN_ROOT",
|
|
51
|
-
];
|
|
43
|
+
const WAVE_TEMPLATE_VARS = ["WAVE_PLUGIN_ROOT"];
|
|
52
44
|
|
|
53
45
|
export function expandEnvVars(value: string): string {
|
|
54
46
|
return value.replace(/\$\{([^}]+)\}/g, (_match, expr: string) => {
|
|
55
47
|
const [varName, ...rest] = expr.split(":-");
|
|
56
48
|
const defaultValue = rest.join(":-");
|
|
57
|
-
// Skip Wave-specific template variables — they are handled
|
|
49
|
+
// Skip Wave-specific template variables — they are handled at spawn time
|
|
58
50
|
if (WAVE_TEMPLATE_VARS.includes(varName)) {
|
|
59
51
|
return _match; // return original ${...} string untouched
|
|
60
52
|
}
|
|
@@ -63,81 +55,15 @@ export function expandEnvVars(value: string): string {
|
|
|
63
55
|
}
|
|
64
56
|
|
|
65
57
|
/**
|
|
66
|
-
*
|
|
58
|
+
* Walk an MCP config and resolve environment variables in all string fields.
|
|
59
|
+
* Only expands ${VAR} from process.env (skipping WAVE_PLUGIN_ROOT which is
|
|
60
|
+
* handled at spawn time).
|
|
67
61
|
*/
|
|
68
|
-
export
|
|
69
|
-
serverUrl?: string; // resolves ${WAVE_SERVER_URL}
|
|
70
|
-
ssoToken?: string; // resolves ${WAVE_SSO_TOKEN}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Walk a single McpServerConfig and replace Wave template variables.
|
|
75
|
-
* Only replaces ${WAVE_SERVER_URL} and ${WAVE_SSO_TOKEN} — does not touch
|
|
76
|
-
* arbitrary env vars (that is what expandEnvVars handles).
|
|
77
|
-
*/
|
|
78
|
-
export function resolveMcpTemplates(
|
|
79
|
-
config: McpServerConfig,
|
|
80
|
-
ctx: McpResolverContext,
|
|
81
|
-
): McpServerConfig {
|
|
82
|
-
const resolved: McpServerConfig = { ...config };
|
|
83
|
-
|
|
84
|
-
const replace = (value: string): string => {
|
|
85
|
-
let result = value;
|
|
86
|
-
if (ctx.serverUrl !== undefined) {
|
|
87
|
-
result = result.replace(/\$\{WAVE_SERVER_URL\}/g, ctx.serverUrl);
|
|
88
|
-
}
|
|
89
|
-
if (ctx.ssoToken !== undefined) {
|
|
90
|
-
result = result.replace(/\$\{WAVE_SSO_TOKEN\}/g, ctx.ssoToken);
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
if (resolved.command) {
|
|
96
|
-
resolved.command = replace(resolved.command);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (resolved.args) {
|
|
100
|
-
resolved.args = resolved.args.map(replace);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (resolved.env) {
|
|
104
|
-
const resolvedEnv: Record<string, string> = {};
|
|
105
|
-
for (const [key, val] of Object.entries(resolved.env)) {
|
|
106
|
-
resolvedEnv[key] = replace(val);
|
|
107
|
-
}
|
|
108
|
-
resolved.env = resolvedEnv;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (resolved.url) {
|
|
112
|
-
resolved.url = replace(resolved.url);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (resolved.headers) {
|
|
116
|
-
const resolvedHeaders: Record<string, string> = {};
|
|
117
|
-
for (const [key, val] of Object.entries(resolved.headers)) {
|
|
118
|
-
resolvedHeaders[key] = replace(val);
|
|
119
|
-
}
|
|
120
|
-
resolved.headers = resolvedHeaders;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return resolved;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Walk an MCP config and resolve variables in all string fields.
|
|
128
|
-
* Applies two steps in order:
|
|
129
|
-
* 1. expandEnvVars — resolves ${VAR} from process.env
|
|
130
|
-
* 2. resolveMcpTemplates — resolves ${WAVE_SERVER_URL}, ${WAVE_SSO_TOKEN} from context
|
|
131
|
-
*/
|
|
132
|
-
export function resolveMcpConfig(
|
|
133
|
-
config: McpConfig,
|
|
134
|
-
ctx?: McpResolverContext,
|
|
135
|
-
): McpConfig {
|
|
62
|
+
export function resolveMcpConfig(config: McpConfig): McpConfig {
|
|
136
63
|
const resolved: McpConfig = { mcpServers: {} };
|
|
137
64
|
|
|
138
65
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
139
|
-
|
|
140
|
-
let resolvedServer: McpServerConfig = { ...serverConfig };
|
|
66
|
+
const resolvedServer: McpServerConfig = { ...serverConfig };
|
|
141
67
|
|
|
142
68
|
if (resolvedServer.command) {
|
|
143
69
|
resolvedServer.command = expandEnvVars(resolvedServer.command);
|
|
@@ -167,11 +93,6 @@ export function resolveMcpConfig(
|
|
|
167
93
|
resolvedServer.headers = resolvedHeaders;
|
|
168
94
|
}
|
|
169
95
|
|
|
170
|
-
// Step 2: resolve Wave template variables from context
|
|
171
|
-
if (ctx) {
|
|
172
|
-
resolvedServer = resolveMcpTemplates(resolvedServer, ctx);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
96
|
resolved.mcpServers[name] = resolvedServer;
|
|
176
97
|
}
|
|
177
98
|
|
|
@@ -187,7 +108,6 @@ export class McpManager {
|
|
|
187
108
|
private callbacks: McpManagerCallbacks;
|
|
188
109
|
private mcpServers: Record<string, McpServerConfig> | undefined;
|
|
189
110
|
|
|
190
|
-
private resolverCtx: McpResolverContext | undefined;
|
|
191
111
|
private reconnectTimers: Map<string, NodeJS.Timeout> = new Map();
|
|
192
112
|
private reconnectAttempts: Map<string, number> = new Map();
|
|
193
113
|
|
|
@@ -197,10 +117,6 @@ export class McpManager {
|
|
|
197
117
|
) {
|
|
198
118
|
this.callbacks = options.callbacks || {};
|
|
199
119
|
this.mcpServers = options.mcpServers;
|
|
200
|
-
this.resolverCtx = {
|
|
201
|
-
serverUrl: options.serverUrl,
|
|
202
|
-
ssoToken: options.ssoToken,
|
|
203
|
-
};
|
|
204
120
|
}
|
|
205
121
|
|
|
206
122
|
/**
|
|
@@ -273,7 +189,7 @@ export class McpManager {
|
|
|
273
189
|
try {
|
|
274
190
|
const configContent = await fs.readFile(this.configPath, "utf-8");
|
|
275
191
|
const rawConfig: McpConfig = JSON.parse(configContent);
|
|
276
|
-
const workspaceConfig = resolveMcpConfig(rawConfig
|
|
192
|
+
const workspaceConfig = resolveMcpConfig(rawConfig);
|
|
277
193
|
|
|
278
194
|
// Extract original (pre-resolution) URLs for safe display
|
|
279
195
|
const originalUrls: Record<string, string | undefined> = {};
|
|
@@ -368,8 +284,8 @@ export class McpManager {
|
|
|
368
284
|
// Capture original URL before any resolution for safe display
|
|
369
285
|
const originalUrl = config.url;
|
|
370
286
|
|
|
371
|
-
//
|
|
372
|
-
|
|
287
|
+
// Expand env vars from process.env (e.g. ${TAVILY_API_KEY})
|
|
288
|
+
const resolvedConfig: McpServerConfig = { ...config };
|
|
373
289
|
if (resolvedConfig.command) {
|
|
374
290
|
resolvedConfig.command = expandEnvVars(resolvedConfig.command);
|
|
375
291
|
}
|
|
@@ -394,12 +310,6 @@ export class McpManager {
|
|
|
394
310
|
resolvedConfig.headers = resolvedHeaders;
|
|
395
311
|
}
|
|
396
312
|
|
|
397
|
-
// Step 2: resolve Wave template variables (e.g. ${WAVE_SERVER_URL}, ${WAVE_SSO_TOKEN})
|
|
398
|
-
resolvedConfig = resolveMcpTemplates(
|
|
399
|
-
resolvedConfig,
|
|
400
|
-
this.resolverCtx ?? { serverUrl: undefined, ssoToken: undefined },
|
|
401
|
-
);
|
|
402
|
-
|
|
403
313
|
const newServer: McpServerStatus = {
|
|
404
314
|
name,
|
|
405
315
|
config: resolvedConfig,
|
|
@@ -921,84 +831,6 @@ export class McpManager {
|
|
|
921
831
|
await Promise.all(disconnectPromises);
|
|
922
832
|
}
|
|
923
833
|
|
|
924
|
-
/**
|
|
925
|
-
* Update credentials and reconnect MCP servers that use template variables.
|
|
926
|
-
* Called after SSO login to refresh ${WAVE_SSO_TOKEN} and ${WAVE_SERVER_URL}.
|
|
927
|
-
*/
|
|
928
|
-
async refreshCredentials(
|
|
929
|
-
serverUrl?: string,
|
|
930
|
-
ssoToken?: string,
|
|
931
|
-
): Promise<void> {
|
|
932
|
-
// Update resolver context
|
|
933
|
-
this.resolverCtx = {
|
|
934
|
-
serverUrl: serverUrl ?? this.resolverCtx?.serverUrl,
|
|
935
|
-
ssoToken: ssoToken ?? this.resolverCtx?.ssoToken,
|
|
936
|
-
};
|
|
937
|
-
|
|
938
|
-
logger?.info(
|
|
939
|
-
`MCP refreshCredentials: serverUrl=${serverUrl}, hasToken=${!!ssoToken}`,
|
|
940
|
-
);
|
|
941
|
-
|
|
942
|
-
// Collect servers that need reconnection
|
|
943
|
-
const serversToReconnect: string[] = [];
|
|
944
|
-
|
|
945
|
-
for (const [name, server] of this.servers) {
|
|
946
|
-
// Re-resolve config with new credentials
|
|
947
|
-
const originalConfig = server.config;
|
|
948
|
-
const resolvedConfig = resolveMcpTemplates(
|
|
949
|
-
originalConfig,
|
|
950
|
-
this.resolverCtx,
|
|
951
|
-
);
|
|
952
|
-
|
|
953
|
-
// Update the stored config, preserving originalUrl
|
|
954
|
-
this.servers.set(name, {
|
|
955
|
-
...server,
|
|
956
|
-
config: resolvedConfig,
|
|
957
|
-
});
|
|
958
|
-
|
|
959
|
-
if (this.config && this.config.mcpServers[name]) {
|
|
960
|
-
this.config.mcpServers[name] = resolvedConfig;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
// Determine if reconnection is needed
|
|
964
|
-
const wasConnected = this.connections.has(name);
|
|
965
|
-
const wasDisconnected =
|
|
966
|
-
server.status === "disconnected" ||
|
|
967
|
-
server.status === "error" ||
|
|
968
|
-
server.status === "reconnecting";
|
|
969
|
-
|
|
970
|
-
if (wasConnected) {
|
|
971
|
-
// Disconnect first, then reconnect with new resolved config
|
|
972
|
-
await this.disconnectServer(name);
|
|
973
|
-
serversToReconnect.push(name);
|
|
974
|
-
} else if (wasDisconnected) {
|
|
975
|
-
// Was disconnected or errored — try to reconnect now that we have credentials
|
|
976
|
-
serversToReconnect.push(name);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// Reconnect servers
|
|
981
|
-
for (const name of serversToReconnect) {
|
|
982
|
-
logger?.debug(
|
|
983
|
-
`Reconnecting MCP server after credential refresh: ${name}`,
|
|
984
|
-
);
|
|
985
|
-
this.connectServer(name)
|
|
986
|
-
.then((success) => {
|
|
987
|
-
if (success) {
|
|
988
|
-
logger?.info(`Successfully reconnected MCP server: ${name}`);
|
|
989
|
-
} else {
|
|
990
|
-
logger?.warn(`Failed to reconnect MCP server: ${name}`);
|
|
991
|
-
}
|
|
992
|
-
})
|
|
993
|
-
.catch((error) => {
|
|
994
|
-
logger?.error(`Reconnection to MCP server ${name} failed:`, error);
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
// Trigger state change callback
|
|
999
|
-
this.callbacks.onMcpServersChange?.(this.getAllServers());
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
834
|
// ========== Tools Registry Methods ==========
|
|
1003
835
|
|
|
1004
836
|
/**
|