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.
Files changed (85) hide show
  1. package/dist/agent.d.ts +5 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +18 -0
  4. package/dist/constants/toolLimits.d.ts +2 -0
  5. package/dist/constants/toolLimits.d.ts.map +1 -1
  6. package/dist/constants/toolLimits.js +2 -0
  7. package/dist/managers/aiManager.d.ts +5 -0
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +21 -0
  10. package/dist/managers/hookManager.d.ts +6 -3
  11. package/dist/managers/hookManager.d.ts.map +1 -1
  12. package/dist/managers/hookManager.js +36 -13
  13. package/dist/managers/mcpManager.d.ts +4 -28
  14. package/dist/managers/mcpManager.d.ts.map +1 -1
  15. package/dist/managers/mcpManager.js +10 -127
  16. package/dist/services/authService.d.ts +33 -1
  17. package/dist/services/authService.d.ts.map +1 -1
  18. package/dist/services/authService.js +212 -11
  19. package/dist/services/configurationService.d.ts +1 -0
  20. package/dist/services/configurationService.d.ts.map +1 -1
  21. package/dist/services/configurationService.js +48 -6
  22. package/dist/services/hook.d.ts +4 -0
  23. package/dist/services/hook.d.ts.map +1 -1
  24. package/dist/services/hook.js +10 -0
  25. package/dist/services/initializationService.d.ts.map +1 -1
  26. package/dist/services/initializationService.js +11 -0
  27. package/dist/services/interactionService.d.ts.map +1 -1
  28. package/dist/services/interactionService.js +0 -12
  29. package/dist/services/remoteSettingsService.d.ts +21 -0
  30. package/dist/services/remoteSettingsService.d.ts.map +1 -0
  31. package/dist/services/remoteSettingsService.js +280 -0
  32. package/dist/tools/bashTool.d.ts.map +1 -1
  33. package/dist/tools/bashTool.js +58 -32
  34. package/dist/tools/types.d.ts +4 -0
  35. package/dist/tools/types.d.ts.map +1 -1
  36. package/dist/types/agent.d.ts +7 -0
  37. package/dist/types/agent.d.ts.map +1 -1
  38. package/dist/types/auth.d.ts +12 -0
  39. package/dist/types/auth.d.ts.map +1 -1
  40. package/dist/types/configuration.d.ts +20 -0
  41. package/dist/types/configuration.d.ts.map +1 -1
  42. package/dist/types/hooks.d.ts +5 -1
  43. package/dist/types/hooks.d.ts.map +1 -1
  44. package/dist/types/hooks.js +1 -0
  45. package/dist/types/mcp.d.ts +1 -1
  46. package/dist/types/mcp.d.ts.map +1 -1
  47. package/dist/utils/containerSetup.d.ts.map +1 -1
  48. package/dist/utils/containerSetup.js +9 -8
  49. package/dist/utils/gitUtils.d.ts +18 -1
  50. package/dist/utils/gitUtils.d.ts.map +1 -1
  51. package/dist/utils/gitUtils.js +120 -49
  52. package/dist/utils/mcpUtils.d.ts.map +1 -1
  53. package/dist/utils/mcpUtils.js +6 -1
  54. package/dist/utils/openaiClient.d.ts.map +1 -1
  55. package/dist/utils/openaiClient.js +4 -2
  56. package/dist/utils/toolResultStorage.d.ts +46 -0
  57. package/dist/utils/toolResultStorage.d.ts.map +1 -0
  58. package/dist/utils/toolResultStorage.js +90 -0
  59. package/dist/utils/worktreeUtils.d.ts.map +1 -1
  60. package/dist/utils/worktreeUtils.js +58 -0
  61. package/package.json +3 -3
  62. package/src/agent.ts +20 -0
  63. package/src/constants/toolLimits.ts +3 -0
  64. package/src/managers/aiManager.ts +37 -0
  65. package/src/managers/hookManager.ts +42 -17
  66. package/src/managers/mcpManager.ts +10 -178
  67. package/src/services/authService.ts +243 -16
  68. package/src/services/configurationService.ts +58 -6
  69. package/src/services/hook.ts +15 -0
  70. package/src/services/initializationService.ts +13 -0
  71. package/src/services/interactionService.ts +0 -18
  72. package/src/services/remoteSettingsService.ts +315 -0
  73. package/src/tools/bashTool.ts +70 -38
  74. package/src/tools/types.ts +4 -0
  75. package/src/types/agent.ts +7 -0
  76. package/src/types/auth.ts +10 -0
  77. package/src/types/configuration.ts +23 -0
  78. package/src/types/hooks.ts +7 -1
  79. package/src/types/mcp.ts +1 -1
  80. package/src/utils/containerSetup.ts +8 -8
  81. package/src/utils/gitUtils.ts +123 -48
  82. package/src/utils/mcpUtils.ts +12 -1
  83. package/src/utils/openaiClient.ts +5 -2
  84. package/src/utils/toolResultStorage.ts +117 -0
  85. package/src/utils/worktreeUtils.ts +63 -0
@@ -9,6 +9,9 @@ export declare class AuthService {
9
9
  private static instance;
10
10
  private _serverUrl;
11
11
  private onAuthChangeCallbacks;
12
+ private _refreshPromise;
13
+ private _authFileMtime;
14
+ private static readonly REFRESH_BUFFER_MS;
12
15
  static getInstance(): AuthService;
13
16
  /**
14
17
  * Set server URL programmatically (e.g. from AgentOptions.serverUrl).
@@ -37,15 +40,44 @@ export declare class AuthService {
37
40
  }): Promise<string>;
38
41
  /**
39
42
  * Exchange a short-lived authorization code for a JWT token.
40
- * Returns both the token and user info.
43
+ * Returns token, optional refresh token, optional expiresIn, and user info.
41
44
  */
42
45
  private exchangeCode;
43
46
  private startLocalAuthServer;
44
47
  private openBrowser;
45
48
  isSSOAuthenticated(): boolean;
49
+ /**
50
+ * Check if the current token is expired or within the refresh buffer.
51
+ * Returns false if no expiry info (backward compat — treated as never-expiring).
52
+ */
53
+ isTokenExpired(): boolean;
54
+ /**
55
+ * Check if the token needs refresh and refresh it if possible.
56
+ * Deduplicates concurrent refresh calls (401 dedup).
57
+ */
58
+ checkAndRefreshTokenIfNeeded(): Promise<boolean>;
59
+ /**
60
+ * Refresh the access token using the stored refresh token.
61
+ * Returns true on success, false on failure.
62
+ */
63
+ private refreshToken;
64
+ /**
65
+ * Check if another process has refreshed the token on disk.
66
+ * Returns true if a fresh token was found and loaded.
67
+ */
68
+ /** @internal Check if another process has refreshed the token on disk */
69
+ tryReadRefreshedTokenFromDisk(): boolean;
46
70
  getAuthUser(): AuthUser | undefined;
47
71
  }
48
72
  export declare const authService: AuthService;
73
+ /**
74
+ * Create a fetch wrapper that handles SSO token refresh transparently.
75
+ *
76
+ * 1. Proactive refresh: calls checkAndRefreshTokenIfNeeded() before each request
77
+ * 2. Updates Authorization header with fresh token
78
+ * 3. Reactive 401/403 recovery: tries disk refresh then force refresh, retries once
79
+ */
80
+ export declare function createAuthAwareFetch(innerFetch: typeof fetch): typeof fetch;
49
81
  /**
50
82
  * Get or create a persistent anonymous ID for telemetry.
51
83
  *
@@ -1 +1 @@
1
- {"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAO7D,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAc;IACrC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,qBAAqB,CACxB;IAEL,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI;IASvE,OAAO,CAAC,gBAAgB;IAUxB,WAAW,IAAI,MAAM;IAKrB,QAAQ,IAAI,UAAU;IAatB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAUlC,SAAS,IAAI,IAAI;IAcjB,WAAW,IAAI,MAAM,GAAG,SAAS;IAKjC,YAAY,IAAI,MAAM;IAUhB,KAAK,CAAC,OAAO,CAAC,EAAE;QACpB,6DAA6D;QAC7D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,mGAAmG;QACnG,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;QAClC,oFAAoF;QACpF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,MAAM,CAAC;IAqBnB;;;OAGG;YACW,YAAY;IA0B1B,OAAO,CAAC,oBAAoB;YAoGd,WAAW;IAmBzB,kBAAkB,IAAI,OAAO;IAI7B,WAAW,IAAI,QAAQ,GAAG,SAAS;CAIpC;AAED,eAAO,MAAM,WAAW,aAA4B,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAsC/C;AAED,4DAA4D;AAC5D,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
1
+ {"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAiB,MAAM,kBAAkB,CAAC;AAQ5E,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAc;IACrC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,qBAAqB,CACxB;IACL,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAiB;IAE1D,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI;IASvE,OAAO,CAAC,gBAAgB;IAUxB,WAAW,IAAI,MAAM;IAKrB,QAAQ,IAAI,UAAU;IAmBtB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAgBlC,SAAS,IAAI,IAAI;IAgBjB,WAAW,IAAI,MAAM,GAAG,SAAS;IAKjC,YAAY,IAAI,MAAM;IAUhB,KAAK,CAAC,OAAO,CAAC,EAAE;QACpB,6DAA6D;QAC7D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,mGAAmG;QACnG,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;QAClC,oFAAoF;QACpF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCnB;;;OAGG;YACW,YAAY;IAyB1B,OAAO,CAAC,oBAAoB;YAoGd,WAAW;IAmBzB,kBAAkB,IAAI,OAAO;IAW7B;;;OAGG;IACH,cAAc,IAAI,OAAO;IAgBzB;;;OAGG;IACG,4BAA4B,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBtD;;;OAGG;YACW,YAAY;IA4D1B;;;OAGG;IACH,yEAAyE;IACzE,6BAA6B,IAAI,OAAO;IAwBxC,WAAW,IAAI,QAAQ,GAAG,SAAS;CAIpC;AAED,eAAO,MAAM,WAAW,aAA4B,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,KAAK,GAAG,OAAO,KAAK,CA+C3E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAsC/C;AAED,4DAA4D;AAC5D,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
@@ -4,7 +4,7 @@
4
4
  * Handles SSO authentication via the admin server.
5
5
  * Manages auth token storage in ~/.wave/auth.json.
6
6
  */
7
- import { readFileSync, writeFileSync, existsSync, chmodSync, rmSync, mkdirSync, } from "fs";
7
+ import { readFileSync, writeFileSync, existsSync, chmodSync, rmSync, mkdirSync, statSync, } from "fs";
8
8
  import * as path from "path";
9
9
  import * as os from "os";
10
10
  import { randomBytes } from "crypto";
@@ -12,12 +12,15 @@ import { createServer } from "http";
12
12
  import { URL } from "url";
13
13
  import { execFile } from "child_process";
14
14
  import { promisify } from "util";
15
+ import { logger } from "../utils/globalLogger.js";
15
16
  /** Persistent anonymous ID for telemetry fallback when SSO is not authenticated. */
16
17
  let _anonymousId;
17
18
  const execFileAsync = promisify(execFile);
18
19
  export class AuthService {
19
20
  constructor() {
20
21
  this.onAuthChangeCallbacks = [];
22
+ this._refreshPromise = null;
23
+ this._authFileMtime = 0;
21
24
  }
22
25
  static getInstance() {
23
26
  if (!AuthService.instance) {
@@ -63,6 +66,13 @@ export class AuthService {
63
66
  }
64
67
  try {
65
68
  const content = readFileSync(authPath, "utf-8");
69
+ // Best-effort mtime tracking for multi-process detection
70
+ try {
71
+ this._authFileMtime = statSync(authPath).mtimeMs;
72
+ }
73
+ catch {
74
+ // ignore stat errors
75
+ }
66
76
  return JSON.parse(content);
67
77
  }
68
78
  catch {
@@ -77,10 +87,19 @@ export class AuthService {
77
87
  }
78
88
  writeFileSync(authPath, JSON.stringify(config, null, 2), "utf-8");
79
89
  chmodSync(authPath, 0o600);
90
+ // Update mtime after write
91
+ try {
92
+ this._authFileMtime = statSync(authPath).mtimeMs;
93
+ }
94
+ catch {
95
+ // ignore stat errors
96
+ }
80
97
  }
81
98
  clearAuth() {
82
99
  const config = this.loadAuth();
83
100
  delete config.SSO_TOKEN;
101
+ delete config.SSO_REFRESH_TOKEN;
102
+ delete config.SSO_TOKEN_EXPIRES_AT;
84
103
  if (Object.keys(config).length === 0) {
85
104
  const authPath = this.getAuthPath();
86
105
  if (existsSync(authPath)) {
@@ -111,33 +130,41 @@ export class AuthService {
111
130
  readToken: options?.readToken,
112
131
  });
113
132
  // Exchange authorization code for JWT (includes user info)
114
- const { token, user } = await this.exchangeCode(serverUrl, code);
133
+ const { token, refreshToken, expiresIn, user } = await this.exchangeCode(serverUrl, code);
115
134
  // Save the token and user info (preserve existing keys)
116
135
  const existing = this.loadAuth();
117
- this.saveAuth({ ...existing, SSO_TOKEN: token, user });
136
+ this.saveAuth({
137
+ ...existing,
138
+ SSO_TOKEN: token,
139
+ SSO_REFRESH_TOKEN: refreshToken,
140
+ SSO_TOKEN_EXPIRES_AT: expiresIn
141
+ ? Date.now() + expiresIn * 1000
142
+ : undefined,
143
+ user,
144
+ });
118
145
  this.notifyAuthChange("login");
119
146
  return token;
120
147
  }
121
148
  /**
122
149
  * Exchange a short-lived authorization code for a JWT token.
123
- * Returns both the token and user info.
150
+ * Returns token, optional refresh token, optional expiresIn, and user info.
124
151
  */
125
152
  async exchangeCode(serverUrl, code) {
126
- const exchangeUrl = `${serverUrl}/api/auth/exchange`;
153
+ const exchangeUrl = `${serverUrl}/api/auth/token`;
154
+ logger.info(`[Auth] Exchanging authorization code at ${exchangeUrl}`);
127
155
  const response = await fetch(exchangeUrl, {
128
156
  method: "POST",
129
157
  headers: { "Content-Type": "application/json" },
130
- body: JSON.stringify({ code }),
158
+ body: JSON.stringify({ grant_type: "authorization_code", code }),
131
159
  });
132
160
  if (!response.ok) {
133
161
  const text = await response.text();
162
+ logger.info(`[Auth] Authorization code exchange failed (${response.status}): ${text}`);
134
163
  throw new Error(`Token exchange failed (${response.status}): ${text}`);
135
164
  }
136
165
  const data = (await response.json());
137
- return {
138
- token: data.token,
139
- user: { id: data.user.id, email: data.user.email },
140
- };
166
+ logger.info("[Auth] Authorization code exchanged successfully");
167
+ return data;
141
168
  }
142
169
  startLocalAuthServer(serverUrl, options) {
143
170
  return new Promise((resolve, reject) => {
@@ -233,14 +260,188 @@ export class AuthService {
233
260
  await execFileAsync(command, args);
234
261
  }
235
262
  isSSOAuthenticated() {
236
- return this.getSSOToken() !== undefined;
263
+ const config = this.loadAuth();
264
+ if (!config.SSO_TOKEN)
265
+ return false;
266
+ if (config.SSO_TOKEN_EXPIRES_AT &&
267
+ Date.now() >= config.SSO_TOKEN_EXPIRES_AT)
268
+ return false;
269
+ return true;
270
+ }
271
+ /**
272
+ * Check if the current token is expired or within the refresh buffer.
273
+ * Returns false if no expiry info (backward compat — treated as never-expiring).
274
+ */
275
+ isTokenExpired() {
276
+ const config = this.loadAuth();
277
+ if (!config.SSO_TOKEN_EXPIRES_AT)
278
+ return false;
279
+ const expiresAt = config.SSO_TOKEN_EXPIRES_AT;
280
+ const bufferMs = AuthService.REFRESH_BUFFER_MS;
281
+ const now = Date.now();
282
+ const remaining = expiresAt - now;
283
+ const expired = now >= expiresAt - bufferMs;
284
+ if (expired) {
285
+ logger.info(`[Auth] Token expired or within refresh buffer: remaining=${Math.round(remaining / 1000)}s, buffer=${bufferMs / 1000}s`);
286
+ }
287
+ return expired;
288
+ }
289
+ /**
290
+ * Check if the token needs refresh and refresh it if possible.
291
+ * Deduplicates concurrent refresh calls (401 dedup).
292
+ */
293
+ async checkAndRefreshTokenIfNeeded() {
294
+ if (!this.isTokenExpired())
295
+ return true;
296
+ // Dedup: if a refresh is already in-flight, reuse the same promise
297
+ if (this._refreshPromise) {
298
+ logger.info("[Auth] Token refresh already in-flight, reusing existing promise");
299
+ return this._refreshPromise;
300
+ }
301
+ logger.info("[Auth] Starting token refresh");
302
+ this._refreshPromise = this.refreshToken();
303
+ try {
304
+ return await this._refreshPromise;
305
+ }
306
+ finally {
307
+ this._refreshPromise = null;
308
+ }
309
+ }
310
+ /**
311
+ * Refresh the access token using the stored refresh token.
312
+ * Returns true on success, false on failure.
313
+ */
314
+ async refreshToken() {
315
+ const config = this.loadAuth();
316
+ if (!config.SSO_REFRESH_TOKEN) {
317
+ logger.info("[Auth] No refresh token available, cannot refresh");
318
+ return false;
319
+ }
320
+ const serverUrl = this.getServerUrl();
321
+ try {
322
+ logger.info(`[Auth] Refreshing token via ${serverUrl}/api/auth/token`);
323
+ const response = await fetch(`${serverUrl}/api/auth/token`, {
324
+ method: "POST",
325
+ headers: { "Content-Type": "application/json" },
326
+ body: JSON.stringify({
327
+ grant_type: "refresh_token",
328
+ refresh_token: config.SSO_REFRESH_TOKEN,
329
+ }),
330
+ });
331
+ if (response.status === 400 || response.status === 401) {
332
+ // Refresh token revoked — clear auth
333
+ logger.info(`[Auth] Refresh token rejected (${response.status}), clearing auth`);
334
+ this.clearAuth();
335
+ return false;
336
+ }
337
+ if (!response.ok) {
338
+ logger.info(`[Auth] Token refresh failed with status ${response.status}`);
339
+ return false;
340
+ }
341
+ const data = (await response.json());
342
+ const newExpiresAt = data.expiresIn
343
+ ? Date.now() + data.expiresIn * 1000
344
+ : undefined;
345
+ this.saveAuth({
346
+ ...config,
347
+ SSO_TOKEN: data.token,
348
+ SSO_REFRESH_TOKEN: data.refreshToken ?? config.SSO_REFRESH_TOKEN,
349
+ SSO_TOKEN_EXPIRES_AT: newExpiresAt,
350
+ user: data.user
351
+ ? { id: data.user.id, email: data.user.email }
352
+ : config.user,
353
+ });
354
+ logger.info(`[Auth] Token refreshed successfully, new token expires at ${newExpiresAt ? new Date(newExpiresAt).toISOString() : "never"}`);
355
+ this.notifyAuthChange("login");
356
+ return true;
357
+ }
358
+ catch (err) {
359
+ // Network error — don't clear auth (might be transient)
360
+ logger.info(`[Auth] Token refresh failed with network error: ${err}`);
361
+ return false;
362
+ }
363
+ }
364
+ /**
365
+ * Check if another process has refreshed the token on disk.
366
+ * Returns true if a fresh token was found and loaded.
367
+ */
368
+ /** @internal Check if another process has refreshed the token on disk */
369
+ tryReadRefreshedTokenFromDisk() {
370
+ try {
371
+ const authPath = this.getAuthPath();
372
+ if (!existsSync(authPath))
373
+ return false;
374
+ const stat = statSync(authPath);
375
+ if (stat.mtimeMs <= this._authFileMtime)
376
+ return false;
377
+ // File was modified by another process — check if token is fresh
378
+ const config = this.loadAuth();
379
+ if (config.SSO_TOKEN_EXPIRES_AT &&
380
+ Date.now() < config.SSO_TOKEN_EXPIRES_AT) {
381
+ logger.info(`[Auth] Detected token refreshed by another process (auth.json mtime changed)`);
382
+ this._authFileMtime = stat.mtimeMs;
383
+ return true;
384
+ }
385
+ return false;
386
+ }
387
+ catch {
388
+ return false;
389
+ }
237
390
  }
238
391
  getAuthUser() {
239
392
  const config = this.loadAuth();
240
393
  return config.user;
241
394
  }
242
395
  }
396
+ AuthService.REFRESH_BUFFER_MS = 5 * 60 * 1000;
243
397
  export const authService = AuthService.getInstance();
398
+ /**
399
+ * Create a fetch wrapper that handles SSO token refresh transparently.
400
+ *
401
+ * 1. Proactive refresh: calls checkAndRefreshTokenIfNeeded() before each request
402
+ * 2. Updates Authorization header with fresh token
403
+ * 3. Reactive 401/403 recovery: tries disk refresh then force refresh, retries once
404
+ */
405
+ export function createAuthAwareFetch(innerFetch) {
406
+ return async (input, init) => {
407
+ // Proactive refresh
408
+ await authService.checkAndRefreshTokenIfNeeded();
409
+ // Update Authorization header with fresh token
410
+ const freshToken = authService.getSSOToken();
411
+ const headers = new Headers(init?.headers);
412
+ if (freshToken) {
413
+ headers.set("Authorization", `Bearer ${freshToken}`);
414
+ }
415
+ const modifiedInit = { ...init, headers };
416
+ const response = await innerFetch(input, modifiedInit);
417
+ // Reactive 401/403 recovery (single retry)
418
+ if (response.status === 401 || response.status === 403) {
419
+ logger.info(`[Auth] Received ${response.status}, attempting token recovery`);
420
+ // Try disk refresh first (another process may have refreshed)
421
+ if (authService.tryReadRefreshedTokenFromDisk()) {
422
+ const retryToken = authService.getSSOToken();
423
+ const retryHeaders = new Headers(init?.headers);
424
+ if (retryToken) {
425
+ retryHeaders.set("Authorization", `Bearer ${retryToken}`);
426
+ }
427
+ logger.info("[Auth] Retrying request with disk-refreshed token");
428
+ return innerFetch(input, { ...init, headers: retryHeaders });
429
+ }
430
+ // Try force refresh
431
+ if (await authService.checkAndRefreshTokenIfNeeded()) {
432
+ const retryToken = authService.getSSOToken();
433
+ if (retryToken) {
434
+ const retryHeaders = new Headers(init?.headers);
435
+ retryHeaders.set("Authorization", `Bearer ${retryToken}`);
436
+ logger.info("[Auth] Retrying request with force-refreshed token");
437
+ return innerFetch(input, { ...init, headers: retryHeaders });
438
+ }
439
+ }
440
+ logger.info("[Auth] Token recovery failed, returning original response");
441
+ }
442
+ return response;
443
+ };
444
+ }
244
445
  /**
245
446
  * Get or create a persistent anonymous ID for telemetry.
246
447
  *
@@ -102,6 +102,7 @@ export declare class ConfigurationService {
102
102
  * Set the active model in the session
103
103
  */
104
104
  setModel(model: string): void;
105
+ private persistModelToSettings;
105
106
  /**
106
107
  * Get all configured models from settings.json and environment
107
108
  */
@@ -1 +1 @@
1
- {"version":3,"file":"configurationService.d.ts","sourceRoot":"","sources":["../../src/services/configurationService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EACV,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,EACL,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAOnC,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAE7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,aAAa,EACb,WAAW,EAGX,cAAc,EACd,YAAY,EACb,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAGvC;;;;;GAKG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAMvC;;OAEG;IACG,uBAAuB,CAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,uBAAuB,CAAC;IA4DnC;;OAEG;IACH,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,gBAAgB;IAkLlE;;OAEG;IACH,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IAwC7D;;;OAGG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAcrD;;OAEG;IACH,OAAO,CAAC,YAAY;IAepB;;;;;;;;;;OAUG;IACH,oBAAoB,CAClB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,YAAY,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,EAC5C,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,GAC7B,aAAa;IA0EhB;;;;;;;;OAQG;IACH,kBAAkB,CAChB,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,cAAc,GAC9B,WAAW;IAoDd;;;;;OAKG;IACH,qBAAqB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAwBxD;;;;;OAKG;IACH,eAAe,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAmBjE;;;;OAIG;IACH,wBAAwB,IAAI,OAAO;IAkBnC;;;;OAIG;IACH,0BAA0B,IAAI,MAAM;IAqBpC;;;;;OAKG;IACH,sBAAsB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAwBzD;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,mBAAmB,IAAI,MAAM,EAAE;IAmB/B;;OAEG;IACH,sBAAsB,IAClB,OAAO,CAAC,OAAO,uBAAuB,EAAE,eAAe,CAAC,GACxD,SAAS;IAIb;;OAEG;IACH,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB;IAa1D;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoClE;;OAEG;IACG,mBAAmB,CACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC;IAuChB;;OAEG;IACH,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAKzE;;OAEG;IACH,qBAAqB,CACnB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GACX,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAapC;;OAEG;IACG,qBAAqB,CACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC;IAyChB;;OAEG;IACG,0BAA0B,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAmChB;;OAEG;IACG,mBAAmB,CACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmChB;;OAEG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAKjE;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;CAGnE;AAKD;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,EACZ,UAAU,CAAC,EAAE,MAAM,GAClB,2BAA2B,CAsD7B;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC3C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC9C,OAAO,GAAE,uBAA4B,GACpC,wBAAwB,CAoC1B;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GACf,iBAAiB,GAAG,IAAI,CA8B1B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,GACd,iBAAiB,GAAG,IAAI,CAoK1B"}
1
+ {"version":3,"file":"configurationService.d.ts","sourceRoot":"","sources":["../../src/services/configurationService.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,8 @@ import { isValidEnvironmentVars, } from "../types/environment.js";
14
14
  import { ConfigurationError, CONFIG_ERRORS, } from "../types/index.js";
15
15
  import { DEFAULT_WAVE_MAX_INPUT_TOKENS, DEFAULT_WAVE_MAX_OUTPUT_TOKENS, } from "../utils/constants.js";
16
16
  import { parseCustomHeaders } from "../utils/stringUtils.js";
17
+ import { getRemoteSettingsSync, mergeRemoteSettings, } from "./remoteSettingsService.js";
18
+ import { createAuthAwareFetch } from "./authService.js";
17
19
  /**
18
20
  * Default ConfigurationService implementation
19
21
  *
@@ -62,17 +64,22 @@ export class ConfigurationService {
62
64
  warnings: validation.warnings,
63
65
  };
64
66
  }
67
+ // Merge remote settings (highest priority: Remote > Local > Project > User)
68
+ const remoteSettings = getRemoteSettingsSync();
69
+ const finalConfig = remoteSettings
70
+ ? mergeRemoteSettings(mergedConfig, remoteSettings)
71
+ : mergedConfig;
65
72
  // Success case
66
- this.currentConfiguration = mergedConfig;
73
+ this.currentConfiguration = finalConfig;
67
74
  // Set environment variables from merged config and inject system variables
68
75
  const env = {
69
- ...(mergedConfig.env || {}),
76
+ ...(finalConfig.env || {}),
70
77
  WAVE_PROJECT_DIR: workdir,
71
78
  };
72
79
  this.setEnvironmentVars(env);
73
- mergedConfig.env = env;
80
+ finalConfig.env = env;
74
81
  return {
75
- configuration: mergedConfig,
82
+ configuration: finalConfig,
76
83
  success: true,
77
84
  sourcePath: "merged configuration",
78
85
  warnings: validation.warnings,
@@ -321,6 +328,8 @@ export class ConfigurationService {
321
328
  const ssoToken = this.readSSOToken();
322
329
  const serverUrl = this.options.serverUrl || process.env.WAVE_SERVER_URL;
323
330
  if (ssoToken && serverUrl) {
331
+ const baseFetch = fetch ?? this.options.fetch ?? globalThis.fetch;
332
+ const authAwareFetch = createAuthAwareFetch(baseFetch);
324
333
  return {
325
334
  apiKey: ssoToken,
326
335
  baseURL: `${serverUrl}/api/v1`,
@@ -328,7 +337,7 @@ export class ConfigurationService {
328
337
  ? defaultHeaders
329
338
  : undefined,
330
339
  fetchOptions: fetchOptions ?? this.options.fetchOptions,
331
- fetch: fetch ?? this.options.fetch,
340
+ fetch: authAwareFetch,
332
341
  };
333
342
  }
334
343
  // Resolve API key: override > options > env (settings.json) > process.env
@@ -394,7 +403,10 @@ export class ConfigurationService {
394
403
  */
395
404
  resolveModelConfig(model, fastModel, maxTokens, permissionMode) {
396
405
  // Resolve agent model: override > options > process.env (includes settings.json env)
397
- const resolvedAgentModel = model || this.options.model || process.env.WAVE_MODEL;
406
+ const resolvedAgentModel = model ||
407
+ this.options.model ||
408
+ process.env.WAVE_MODEL ||
409
+ this.currentConfiguration?.model;
398
410
  // Resolve fast model: override > options > process.env (includes settings.json env)
399
411
  const resolvedFastModel = fastModel || this.options.fastModel || process.env.WAVE_FAST_MODEL;
400
412
  // Validate required fields
@@ -547,6 +559,26 @@ export class ConfigurationService {
547
559
  */
548
560
  setModel(model) {
549
561
  this.options.model = model;
562
+ this.persistModelToSettings(model);
563
+ }
564
+ async persistModelToSettings(model) {
565
+ const configPath = getUserConfigPaths()[0]; // ~/.wave/settings.json
566
+ const configDir = path.dirname(configPath);
567
+ if (!existsSync(configDir)) {
568
+ await fs.mkdir(configDir, { recursive: true });
569
+ }
570
+ let config = {};
571
+ if (existsSync(configPath)) {
572
+ try {
573
+ const content = await fs.readFile(configPath, "utf-8");
574
+ config = JSON.parse(content);
575
+ }
576
+ catch {
577
+ // Start fresh if corrupted
578
+ }
579
+ }
580
+ config.model = model;
581
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
550
582
  }
551
583
  /**
552
584
  * Get all configured models from settings.json and environment
@@ -558,6 +590,10 @@ export class ConfigurationService {
558
590
  if (currentModel) {
559
591
  models.add(currentModel);
560
592
  }
593
+ // Persisted model from settings (includes remote-merged)
594
+ if (this.currentConfiguration?.model) {
595
+ models.add(this.currentConfiguration.model);
596
+ }
561
597
  // Add models from merged configuration
562
598
  if (this.currentConfiguration?.models) {
563
599
  Object.keys(this.currentConfiguration.models).forEach((model) => {
@@ -894,6 +930,7 @@ export function loadWaveConfigFromFile(filePath) {
894
930
  permissions: config.permissions || undefined,
895
931
  enabledPlugins: config.enabledPlugins || undefined,
896
932
  language: config.language || undefined,
933
+ model: config.model || undefined,
897
934
  autoMemoryEnabled: config.autoMemoryEnabled !== undefined
898
935
  ? config.autoMemoryEnabled
899
936
  : undefined,
@@ -1007,6 +1044,10 @@ export function loadMergedWaveConfig(workdir) {
1007
1044
  if (config.language !== undefined) {
1008
1045
  mergedConfig.language = config.language;
1009
1046
  }
1047
+ // Merge model (last one wins)
1048
+ if (config.model !== undefined) {
1049
+ mergedConfig.model = config.model;
1050
+ }
1010
1051
  // Merge autoMemoryEnabled (last one wins)
1011
1052
  if (config.autoMemoryEnabled !== undefined) {
1012
1053
  mergedConfig.autoMemoryEnabled = config.autoMemoryEnabled;
@@ -1049,6 +1090,7 @@ export function loadMergedWaveConfig(workdir) {
1049
1090
  ? mergedConfig.enabledPlugins
1050
1091
  : undefined,
1051
1092
  language: mergedConfig.language,
1093
+ model: mergedConfig.model,
1052
1094
  autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
1053
1095
  marketplaces: mergedConfig.marketplaces &&
1054
1096
  Object.keys(mergedConfig.marketplaces).length > 0
@@ -14,6 +14,10 @@ export declare function executeCommand(command: string, context: HookExecutionCo
14
14
  * Execute multiple commands in sequence
15
15
  */
16
16
  export declare function executeCommands(commands: string[], context: HookExecutionContext | ExtendedHookExecutionContext, options?: HookExecutionOptions): Promise<HookExecutionResult[]>;
17
+ /**
18
+ * Execute a CwdChanged hook
19
+ */
20
+ export declare function executeCwdChangedHooks(oldCwd: string, newCwd: string, context: ExtendedHookExecutionContext): Promise<HookExecutionResult[]>;
17
21
  /**
18
22
  * Validate command safety (basic checks)
19
23
  */
@@ -1 +1 @@
1
- {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/services/hook.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EAElC,MAAM,mBAAmB,CAAC;AA2F3B;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA6I9B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAchC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsBtD"}
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/services/hook.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EAElC,MAAM,mBAAmB,CAAC;AA2F3B;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA6I9B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAchC;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAMhC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsBtD"}
@@ -223,6 +223,16 @@ export async function executeCommands(commands, context, options) {
223
223
  }
224
224
  return results;
225
225
  }
226
+ /**
227
+ * Execute a CwdChanged hook
228
+ */
229
+ export async function executeCwdChangedHooks(oldCwd, newCwd, context) {
230
+ // CwdChanged hooks are executed through HookManager.executeCwdChangedHooks()
231
+ void context;
232
+ void oldCwd;
233
+ void newCwd;
234
+ return [];
235
+ }
226
236
  /**
227
237
  * Validate command safety (basic checks)
228
238
  */
@@ -1 +1 @@
1
- {"version":3,"file":"initializationService.d.ts","sourceRoot":"","sources":["../../src/services/initializationService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACN,YAAY,EACZ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,WAAW,CAAC;IACxB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,WAAW,EAAE,WAAW,CAAC;IACzB,wBAAwB,EAAE,MAAM,IAAI,CAAC;CACtC;AAED,qBAAa,qBAAqB;WACZ,UAAU,CAC5B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QACR,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;KACtB,GACA,OAAO,CAAC,IAAI,CAAC;CAqRjB"}
1
+ {"version":3,"file":"initializationService.d.ts","sourceRoot":"","sources":["../../src/services/initializationService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACN,YAAY,EACZ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,WAAW,CAAC;IACxB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,WAAW,EAAE,WAAW,CAAC;IACzB,wBAAwB,EAAE,MAAM,IAAI,CAAC;CACtC;AAED,qBAAa,qBAAqB;WACZ,UAAU,CAC5B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QACR,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;KACtB,GACA,OAAO,CAAC,IAAI,CAAC;CAiSjB"}
@@ -2,6 +2,7 @@ import { handleSessionRestoration } from "./session.js";
2
2
  import { setGlobalLogger } from "../utils/globalLogger.js";
3
3
  import { LspManager } from "../managers/lspManager.js";
4
4
  import { USER_MEMORY_FILE } from "../utils/constants.js";
5
+ import { remoteSettingsService } from "./remoteSettingsService.js";
5
6
  export class InitializationService {
6
7
  static async initialize(context, options) {
7
8
  const { skillManager, subagentManager, container, toolManager, pluginManager, options: agentOptions, slashCommandManager, logger, mcpManager, workdir, lspManager, configurationService, hookManager, messageManager, memoryRuleManager, liveConfigManager, taskManager, resolveAndValidateConfig, } = context;
@@ -162,6 +163,16 @@ export class InitializationService {
162
163
  logger?.error("Failed to initialize live configuration reload:", error);
163
164
  // Don't throw error to prevent app startup failure - continue without live reload
164
165
  }
166
+ // Initialize remote settings (fetch server-managed config)
167
+ try {
168
+ const phaseStart = performance.now();
169
+ await remoteSettingsService.initialize();
170
+ logger?.debug(`Initialization Phase [Remote Settings] took ${(performance.now() - phaseStart).toFixed(2)}ms`);
171
+ }
172
+ catch (error) {
173
+ logger?.error("Failed to initialize remote settings:", error);
174
+ // Don't throw error to prevent app startup failure - continue without remote settings
175
+ }
165
176
  // Memory is lazy-cached on first getCombinedMemoryContent call
166
177
  // No explicit loading needed during initialization
167
178
  // Handle session restoration or set provided messages
@@ -1 +1 @@
1
- {"version":3,"file":"interactionService.d.ts","sourceRoot":"","sources":["../../src/services/interactionService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,qBAAa,kBAAkB;WACT,WAAW,CAC7B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACjD,OAAO,CAAC,IAAI,CAAC;WA8GI,cAAc,CAChC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;CAiDjB"}
1
+ {"version":3,"file":"interactionService.d.ts","sourceRoot":"","sources":["../../src/services/interactionService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,qBAAa,kBAAkB;WACT,WAAW,CAC7B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACjD,OAAO,CAAC,IAAI,CAAC;WA6FI,cAAc,CAChC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;CAiDjB"}
@@ -21,18 +21,6 @@ export class InteractionService {
21
21
  // If command doesn't exist, continue as normal message processing
22
22
  // Don't add to history, let normal message processing logic below handle it
23
23
  }
24
- // Inject pending notifications from background tasks
25
- const notificationQueue = context.aiManager["container"].has("NotificationQueue")
26
- ? context.aiManager["container"].get("NotificationQueue")
27
- : undefined;
28
- if (notificationQueue && notificationQueue.hasPending()) {
29
- const notifications = notificationQueue.dequeueAll();
30
- for (const notification of notifications) {
31
- messageManager.addUserMessage({
32
- content: notification,
33
- });
34
- }
35
- }
36
24
  // Handle normal AI message
37
25
  // Add user message first, will automatically sync to UI
38
26
  messageManager.addUserMessage({
@@ -0,0 +1,21 @@
1
+ import type { RemoteSettingsFetchResult } from "../types/configuration.js";
2
+ import type { WaveConfiguration } from "../types/configuration.js";
3
+ export declare function initialize(): void;
4
+ export declare function getRemoteSettingsSync(): WaveConfiguration | null;
5
+ export declare function refresh(): Promise<RemoteSettingsFetchResult>;
6
+ export declare function clear(): void;
7
+ export declare function shutdown(): void;
8
+ export declare function mergeRemoteSettings(localMerged: WaveConfiguration, remote: WaveConfiguration): WaveConfiguration;
9
+ /**
10
+ * Singleton object for consumers that prefer a namespace-style import.
11
+ * Usage: import { remoteSettingsService } from "./remoteSettingsService.js"
12
+ */
13
+ export declare const remoteSettingsService: {
14
+ readonly initialize: typeof initialize;
15
+ readonly getRemoteSettingsSync: typeof getRemoteSettingsSync;
16
+ readonly refresh: typeof refresh;
17
+ readonly clear: typeof clear;
18
+ readonly shutdown: typeof shutdown;
19
+ readonly mergeRemoteSettings: typeof mergeRemoteSettings;
20
+ };
21
+ //# sourceMappingURL=remoteSettingsService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remoteSettingsService.d.ts","sourceRoot":"","sources":["../../src/services/remoteSettingsService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AA2JnE,wBAAgB,UAAU,IAAI,IAAI,CASjC;AAED,wBAAgB,qBAAqB,IAAI,iBAAiB,GAAG,IAAI,CAEhE;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,yBAAyB,CAAC,CAKlE;AAED,wBAAgB,KAAK,IAAI,IAAI,CAO5B;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAK/B;AAqCD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,iBAAiB,EAC9B,MAAM,EAAE,iBAAiB,GACxB,iBAAiB,CA4DnB;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB;;;;;;;CAOxB,CAAC"}