wave-agent-sdk 0.16.9 → 0.16.10
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.map +1 -1
- package/dist/agent.js +3 -0
- package/dist/services/configurationService.d.ts +1 -0
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +44 -5
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +11 -0
- package/dist/services/remoteSettingsService.d.ts +21 -0
- package/dist/services/remoteSettingsService.d.ts.map +1 -0
- package/dist/services/remoteSettingsService.js +279 -0
- package/dist/types/configuration.d.ts +20 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +10 -0
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +4 -2
- package/package.json +1 -1
- package/src/agent.ts +3 -0
- package/src/services/configurationService.ts +52 -5
- package/src/services/initializationService.ts +13 -0
- package/src/services/remoteSettingsService.ts +314 -0
- package/src/types/configuration.ts +23 -0
- package/src/utils/containerSetup.ts +10 -0
- package/src/utils/openaiClient.ts +5 -2
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAYA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAO9E,OAAO,EACL,YAAY,EACZ,kBAAkB,EAElB,YAAY,EACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EACV,OAAO,EAEP,eAAe,EACf,aAAa,EACb,WAAW,EACX,KAAK,EACL,cAAc,EACd,cAAc,EACf,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAYA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAO9E,OAAO,EACL,YAAY,EACZ,kBAAkB,EAElB,YAAY,EACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EACV,OAAO,EAEP,eAAe,EACf,aAAa,EACb,WAAW,EACX,KAAK,EACL,cAAc,EACd,cAAc,EACf,MAAM,kBAAkB,CAAC;AAqB1B,qBAAa,KAAK;IAChB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,SAAS,CAAY;IAE7B,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,qBAAqB,CAAwB;IACrD,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,2BAA2B,CAAuB;IAC1D,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,qBAAqB,CAAwB;IACrD,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,gBAAgB,CAAsB;IAG9C,OAAO,CAAC,OAAO,CAAe;IAGvB,gBAAgB,IAAI,aAAa;IAIjC,cAAc,IAAI,WAAW;IAS7B,iBAAiB,IAAI,MAAM;IAI3B,WAAW,IAAI,MAAM,GAAG,SAAS;IAIxC;;;OAGG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKpC;;;OAGG;IACI,mBAAmB,IAAI,MAAM,EAAE;IAItC;;;;;;;;OAQG;IACH,OAAO;IAwHP,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,QAAQ,IAAI,OAAO,EAAE,CAE/B;IAED,IAAW,MAAM,IAAI,KAAK,EAAE,CAE3B;IAED,IAAW,eAAe,IAAI,MAAM,CAEnC;IAED,IAAW,iBAAiB,IAAI,MAAM,CAErC;IAED,4BAA4B;IAC5B,IAAW,gBAAgB,IAAI,MAAM,CAEpC;IAED,iCAAiC;IACjC,IAAW,aAAa,IAAI,MAAM,CAMjC;IAED,8BAA8B;IAC9B,IAAW,UAAU,IAAI,MAAM,CAM9B;IAED,mEAAmE;IACtD,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjD,4BAA4B;IAC5B,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,oCAAoC;IACpC,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,wCAAwC;IACxC,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED,0BAA0B;IAC1B,IAAW,cAAc,IAAI,aAAa,EAAE,CAE3C;IAED;;;;OAIG;IACI,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAQlD;;;OAGG;YACW,oBAAoB;IAclC,uCAAuC;IAChC,wBAAwB,CAC7B,EAAE,EAAE,MAAM,EACV,MAAM,CAAC,EAAE,MAAM,GACd;QACD,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI;IAIR,iCAAiC;IAC1B,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/C,iCAAiC;IAC1B,uBAAuB,CAC5B,EAAE,EAAE,MAAM,EACV,MAAM,CAAC,EAAE,MAAM,GACd;QACD,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI;IAIR,2BAA2B;IACpB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI9C;;;;;;;;OAQG;IACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;WACU,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;IAW1D;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;IAUhC,wEAAwE;YAC1D,UAAU;IA4CxB;;;OAGG;IACU,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBtD,cAAc,IAAI,IAAI;IAI7B;;;OAGG;YACW,2BAA2B;IAqBzC,0CAA0C;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3C,kFAAkF;IAC3E,YAAY,IAAI,IAAI;IAU3B,uCAAuC;IAChC,gBAAgB,IAAI,IAAI;IAI/B,wCAAwC;IACjC,iBAAiB,IAAI,IAAI;IAIhC;;OAEG;IACI,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAIzD;;OAEG;IACI,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIjD;;OAEG;IACU,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKnD;;;;OAIG;IACU,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC3E,2CAA2C;IAC9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkErC;;OAEG;IACI,iBAAiB,IAAI,IAAI;IAIhC;;;;OAIG;IACU,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWtD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACU,WAAW,CACtB,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;IA6BhB,gCAAgC;IACzB,aAAa,IAAI,eAAe,EAAE;IAIzC,yBAAyB;IACZ,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAInE,4BAA4B;IACf,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMtE,uCAAuC;IAChC,gBAAgB,IAAI,YAAY,EAAE;IAIzC,oCAAoC;IAC7B,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlD,6BAA6B;IAChB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQlD,iCAAiC;IAC1B,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAI1E,8BAA8B;IACvB,iBAAiB,IAAI,kBAAkB,EAAE;IAIhD;;OAEG;IACI,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAIxD;;OAEG;IACI,iBAAiB,IAAI,cAAc;IAI1C;;;OAGG;IACI,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IASpD;;;OAGG;IACU,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1D;;OAEG;IACU,oBAAoB,IAAI,OAAO,CAAC;QAC3C,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;IAIF;;OAEG;IACI,eAAe,IAAI,MAAM,GAAG,SAAS;IAI5C;;OAEG;IACI,eAAe,IAAI,MAAM,EAAE;IAOlC;;OAEG;IACI,mBAAmB,IAAI,MAAM,EAAE;IAItC;;OAEG;IACU,eAAe,CAC1B,OAAO,EAAE,OAAO,wBAAwB,EAAE,qBAAqB,GAC9D,OAAO,CAAC,OAAO,wBAAwB,EAAE,kBAAkB,CAAC;IAI/D;;;OAGG;IACU,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;;;OAIG;IACI,mBAAmB,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,+BAA+B,EAAE,gBAAgB,GAAG,IAAI;IAIlE;;OAEG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;OAEG;IACH,IAAW,wBAAwB,IAAI,OAAO,CAO7C;CACF"}
|
package/dist/agent.js
CHANGED
|
@@ -9,6 +9,7 @@ import { ConfigurationService } from "./services/configurationService.js";
|
|
|
9
9
|
import { setupAgentContainer } from "./utils/containerSetup.js";
|
|
10
10
|
import { initializeTelemetry, shutdownTelemetry, } from "./telemetry/instrumentation.js";
|
|
11
11
|
import { logOTelEvent } from "./telemetry/events.js";
|
|
12
|
+
import { remoteSettingsService } from "./services/remoteSettingsService.js";
|
|
12
13
|
export class Agent {
|
|
13
14
|
// Dynamic configuration getter methods
|
|
14
15
|
getGatewayConfig() {
|
|
@@ -524,6 +525,8 @@ export class Agent {
|
|
|
524
525
|
catch (error) {
|
|
525
526
|
this.logger?.error("Error shutting down live configuration reload:", error);
|
|
526
527
|
}
|
|
528
|
+
// Cleanup remote settings polling
|
|
529
|
+
remoteSettingsService.shutdown();
|
|
527
530
|
// Cleanup memory store
|
|
528
531
|
}
|
|
529
532
|
/**
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"configurationService.d.ts","sourceRoot":"","sources":["../../src/services/configurationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ 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";
|
|
17
18
|
/**
|
|
18
19
|
* Default ConfigurationService implementation
|
|
19
20
|
*
|
|
@@ -62,17 +63,22 @@ export class ConfigurationService {
|
|
|
62
63
|
warnings: validation.warnings,
|
|
63
64
|
};
|
|
64
65
|
}
|
|
66
|
+
// Merge remote settings (highest priority: Remote > Local > Project > User)
|
|
67
|
+
const remoteSettings = getRemoteSettingsSync();
|
|
68
|
+
const finalConfig = remoteSettings
|
|
69
|
+
? mergeRemoteSettings(mergedConfig, remoteSettings)
|
|
70
|
+
: mergedConfig;
|
|
65
71
|
// Success case
|
|
66
|
-
this.currentConfiguration =
|
|
72
|
+
this.currentConfiguration = finalConfig;
|
|
67
73
|
// Set environment variables from merged config and inject system variables
|
|
68
74
|
const env = {
|
|
69
|
-
...(
|
|
75
|
+
...(finalConfig.env || {}),
|
|
70
76
|
WAVE_PROJECT_DIR: workdir,
|
|
71
77
|
};
|
|
72
78
|
this.setEnvironmentVars(env);
|
|
73
|
-
|
|
79
|
+
finalConfig.env = env;
|
|
74
80
|
return {
|
|
75
|
-
configuration:
|
|
81
|
+
configuration: finalConfig,
|
|
76
82
|
success: true,
|
|
77
83
|
sourcePath: "merged configuration",
|
|
78
84
|
warnings: validation.warnings,
|
|
@@ -394,7 +400,10 @@ export class ConfigurationService {
|
|
|
394
400
|
*/
|
|
395
401
|
resolveModelConfig(model, fastModel, maxTokens, permissionMode) {
|
|
396
402
|
// Resolve agent model: override > options > process.env (includes settings.json env)
|
|
397
|
-
const resolvedAgentModel = model ||
|
|
403
|
+
const resolvedAgentModel = model ||
|
|
404
|
+
this.options.model ||
|
|
405
|
+
process.env.WAVE_MODEL ||
|
|
406
|
+
this.currentConfiguration?.model;
|
|
398
407
|
// Resolve fast model: override > options > process.env (includes settings.json env)
|
|
399
408
|
const resolvedFastModel = fastModel || this.options.fastModel || process.env.WAVE_FAST_MODEL;
|
|
400
409
|
// Validate required fields
|
|
@@ -547,6 +556,26 @@ export class ConfigurationService {
|
|
|
547
556
|
*/
|
|
548
557
|
setModel(model) {
|
|
549
558
|
this.options.model = model;
|
|
559
|
+
this.persistModelToSettings(model);
|
|
560
|
+
}
|
|
561
|
+
async persistModelToSettings(model) {
|
|
562
|
+
const configPath = getUserConfigPaths()[0]; // ~/.wave/settings.json
|
|
563
|
+
const configDir = path.dirname(configPath);
|
|
564
|
+
if (!existsSync(configDir)) {
|
|
565
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
566
|
+
}
|
|
567
|
+
let config = {};
|
|
568
|
+
if (existsSync(configPath)) {
|
|
569
|
+
try {
|
|
570
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
571
|
+
config = JSON.parse(content);
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
// Start fresh if corrupted
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
config.model = model;
|
|
578
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
550
579
|
}
|
|
551
580
|
/**
|
|
552
581
|
* Get all configured models from settings.json and environment
|
|
@@ -558,6 +587,10 @@ export class ConfigurationService {
|
|
|
558
587
|
if (currentModel) {
|
|
559
588
|
models.add(currentModel);
|
|
560
589
|
}
|
|
590
|
+
// Persisted model from settings (includes remote-merged)
|
|
591
|
+
if (this.currentConfiguration?.model) {
|
|
592
|
+
models.add(this.currentConfiguration.model);
|
|
593
|
+
}
|
|
561
594
|
// Add models from merged configuration
|
|
562
595
|
if (this.currentConfiguration?.models) {
|
|
563
596
|
Object.keys(this.currentConfiguration.models).forEach((model) => {
|
|
@@ -894,6 +927,7 @@ export function loadWaveConfigFromFile(filePath) {
|
|
|
894
927
|
permissions: config.permissions || undefined,
|
|
895
928
|
enabledPlugins: config.enabledPlugins || undefined,
|
|
896
929
|
language: config.language || undefined,
|
|
930
|
+
model: config.model || undefined,
|
|
897
931
|
autoMemoryEnabled: config.autoMemoryEnabled !== undefined
|
|
898
932
|
? config.autoMemoryEnabled
|
|
899
933
|
: undefined,
|
|
@@ -1007,6 +1041,10 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1007
1041
|
if (config.language !== undefined) {
|
|
1008
1042
|
mergedConfig.language = config.language;
|
|
1009
1043
|
}
|
|
1044
|
+
// Merge model (last one wins)
|
|
1045
|
+
if (config.model !== undefined) {
|
|
1046
|
+
mergedConfig.model = config.model;
|
|
1047
|
+
}
|
|
1010
1048
|
// Merge autoMemoryEnabled (last one wins)
|
|
1011
1049
|
if (config.autoMemoryEnabled !== undefined) {
|
|
1012
1050
|
mergedConfig.autoMemoryEnabled = config.autoMemoryEnabled;
|
|
@@ -1049,6 +1087,7 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1049
1087
|
? mergedConfig.enabledPlugins
|
|
1050
1088
|
: undefined,
|
|
1051
1089
|
language: mergedConfig.language,
|
|
1090
|
+
model: mergedConfig.model,
|
|
1052
1091
|
autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
|
|
1053
1092
|
marketplaces: mergedConfig.marketplaces &&
|
|
1054
1093
|
Object.keys(mergedConfig.marketplaces).length > 0
|
|
@@ -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;
|
|
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
|
|
@@ -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;AA0JnE,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"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { authService } from "./authService.js";
|
|
5
|
+
import { logger } from "../utils/globalLogger.js";
|
|
6
|
+
const CACHE_FILE = path.join(homedir(), ".wave", "remote-settings.json");
|
|
7
|
+
const POLLING_INTERVAL_MS = 60 * 60 * 1000; // 60 minutes
|
|
8
|
+
const FETCH_TIMEOUT_MS = 10000;
|
|
9
|
+
let _cachedSettings = null;
|
|
10
|
+
let _pollingTimer = null;
|
|
11
|
+
function loadCacheFromDisk() {
|
|
12
|
+
try {
|
|
13
|
+
if (!fs.existsSync(CACHE_FILE)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const raw = fs.readFileSync(CACHE_FILE, "utf-8");
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
_cachedSettings = parsed;
|
|
19
|
+
logger.debug("remoteSettings: loaded cache from disk", {
|
|
20
|
+
checksum: parsed.checksum,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
logger.debug("remoteSettings: failed to load cache from disk", { err });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function writeCacheToDisk() {
|
|
28
|
+
if (!_cachedSettings) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const dir = path.dirname(CACHE_FILE);
|
|
33
|
+
if (!fs.existsSync(dir)) {
|
|
34
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(_cachedSettings, null, 2), {
|
|
37
|
+
mode: 0o600,
|
|
38
|
+
});
|
|
39
|
+
logger.debug("remoteSettings: wrote cache to disk", {
|
|
40
|
+
checksum: _cachedSettings.checksum,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.debug("remoteSettings: failed to write cache to disk", { err });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function removeCacheFromDisk() {
|
|
48
|
+
try {
|
|
49
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
50
|
+
fs.unlinkSync(CACHE_FILE);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logger.debug("remoteSettings: failed to remove cache file", { err });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function fetchRemoteSettings() {
|
|
58
|
+
if (!authService.isSSOAuthenticated()) {
|
|
59
|
+
logger.debug("remoteSettings: skipping fetch — not SSO authenticated");
|
|
60
|
+
return { success: false, error: "Not SSO authenticated" };
|
|
61
|
+
}
|
|
62
|
+
const token = authService.getSSOToken();
|
|
63
|
+
const serverUrl = authService.getServerUrl();
|
|
64
|
+
if (!token || !serverUrl) {
|
|
65
|
+
return { success: false, error: "Missing SSO token or server URL" };
|
|
66
|
+
}
|
|
67
|
+
const headers = {
|
|
68
|
+
Authorization: `Bearer ${token}`,
|
|
69
|
+
};
|
|
70
|
+
if (_cachedSettings?.checksum) {
|
|
71
|
+
headers["If-None-Match"] = _cachedSettings.checksum;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(`${serverUrl}/api/wave/settings`, {
|
|
75
|
+
method: "GET",
|
|
76
|
+
headers,
|
|
77
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
78
|
+
});
|
|
79
|
+
if (response.status === 304) {
|
|
80
|
+
logger.debug("remoteSettings: 304 unchanged", {
|
|
81
|
+
checksum: _cachedSettings?.checksum,
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
settings: _cachedSettings.settings,
|
|
86
|
+
checksum: _cachedSettings.checksum,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (response.status === 404) {
|
|
90
|
+
logger.debug("remoteSettings: 404 not configured — clearing stale cache");
|
|
91
|
+
_cachedSettings = null;
|
|
92
|
+
removeCacheFromDisk();
|
|
93
|
+
return { success: true, notConfigured: true, settings: null };
|
|
94
|
+
}
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const body = await response.text().catch(() => "");
|
|
97
|
+
logger.debug("remoteSettings: fetch failed", {
|
|
98
|
+
status: response.status,
|
|
99
|
+
body: body.slice(0, 200),
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: `HTTP ${response.status}`,
|
|
104
|
+
settings: _cachedSettings?.settings ?? null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const data = (await response.json());
|
|
108
|
+
_cachedSettings = {
|
|
109
|
+
uuid: data.uuid,
|
|
110
|
+
checksum: data.checksum,
|
|
111
|
+
settings: data.settings,
|
|
112
|
+
fetchedAt: new Date().toISOString(),
|
|
113
|
+
};
|
|
114
|
+
writeCacheToDisk();
|
|
115
|
+
logger.debug("remoteSettings: fetched new settings", {
|
|
116
|
+
checksum: data.checksum,
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
settings: data.settings,
|
|
121
|
+
checksum: data.checksum,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
logger.debug("remoteSettings: network error, using cache", { err });
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
error: err instanceof Error ? err.message : String(err),
|
|
129
|
+
settings: _cachedSettings?.settings ?? null,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function startPolling() {
|
|
134
|
+
if (_pollingTimer) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
_pollingTimer = setInterval(async () => {
|
|
138
|
+
try {
|
|
139
|
+
await fetchRemoteSettings();
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
logger.debug("remoteSettings: polling fetch error", { err });
|
|
143
|
+
}
|
|
144
|
+
}, POLLING_INTERVAL_MS);
|
|
145
|
+
_pollingTimer.unref();
|
|
146
|
+
}
|
|
147
|
+
export function initialize() {
|
|
148
|
+
loadCacheFromDisk();
|
|
149
|
+
// Fire-and-forget the initial fetch, then start background polling
|
|
150
|
+
fetchRemoteSettings()
|
|
151
|
+
.then(() => startPolling())
|
|
152
|
+
.catch((err) => {
|
|
153
|
+
logger.debug("remoteSettings: initial fetch failed", { err });
|
|
154
|
+
startPolling();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
export function getRemoteSettingsSync() {
|
|
158
|
+
return _cachedSettings?.settings ?? null;
|
|
159
|
+
}
|
|
160
|
+
export async function refresh() {
|
|
161
|
+
// Clear in-memory so we force a fresh fetch
|
|
162
|
+
_cachedSettings = null;
|
|
163
|
+
removeCacheFromDisk();
|
|
164
|
+
return fetchRemoteSettings();
|
|
165
|
+
}
|
|
166
|
+
export function clear() {
|
|
167
|
+
_cachedSettings = null;
|
|
168
|
+
removeCacheFromDisk();
|
|
169
|
+
if (_pollingTimer) {
|
|
170
|
+
clearInterval(_pollingTimer);
|
|
171
|
+
_pollingTimer = null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export function shutdown() {
|
|
175
|
+
if (_pollingTimer) {
|
|
176
|
+
clearInterval(_pollingTimer);
|
|
177
|
+
_pollingTimer = null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function dedupe(arr) {
|
|
181
|
+
return [...new Set(arr)];
|
|
182
|
+
}
|
|
183
|
+
function mergeHooks(local, remote) {
|
|
184
|
+
if (!remote && !local) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
if (!remote) {
|
|
188
|
+
return local;
|
|
189
|
+
}
|
|
190
|
+
if (!local) {
|
|
191
|
+
return remote;
|
|
192
|
+
}
|
|
193
|
+
const merged = { ...local };
|
|
194
|
+
for (const [event, remoteHooks] of Object.entries(remote)) {
|
|
195
|
+
const localHooks = merged[event] ?? [];
|
|
196
|
+
// Concatenate + dedupe by JSON serialization
|
|
197
|
+
const combined = [...localHooks, ...(remoteHooks ?? [])];
|
|
198
|
+
const seen = new Set();
|
|
199
|
+
merged[event] = combined.filter((h) => {
|
|
200
|
+
const key = JSON.stringify(h);
|
|
201
|
+
if (seen.has(key)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
seen.add(key);
|
|
205
|
+
return true;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return merged;
|
|
209
|
+
}
|
|
210
|
+
export function mergeRemoteSettings(localMerged, remote) {
|
|
211
|
+
const result = { ...localMerged };
|
|
212
|
+
// env: merge by key, remote wins per-key
|
|
213
|
+
if (remote.env || localMerged.env) {
|
|
214
|
+
result.env = { ...localMerged.env, ...remote.env };
|
|
215
|
+
}
|
|
216
|
+
// permissions
|
|
217
|
+
if (remote.permissions || localMerged.permissions) {
|
|
218
|
+
const lp = localMerged.permissions ?? {};
|
|
219
|
+
const rp = remote.permissions ?? {};
|
|
220
|
+
result.permissions = {
|
|
221
|
+
// allow: concatenate + dedupe
|
|
222
|
+
allow: lp.allow || rp.allow
|
|
223
|
+
? dedupe([...(lp.allow ?? []), ...(rp.allow ?? [])])
|
|
224
|
+
: undefined,
|
|
225
|
+
// deny: concatenate + dedupe
|
|
226
|
+
deny: lp.deny || rp.deny
|
|
227
|
+
? dedupe([...(lp.deny ?? []), ...(rp.deny ?? [])])
|
|
228
|
+
: undefined,
|
|
229
|
+
// permissionMode: remote wins (scalar)
|
|
230
|
+
permissionMode: rp.permissionMode ?? lp.permissionMode,
|
|
231
|
+
// additionalDirectories: concatenate + dedupe
|
|
232
|
+
additionalDirectories: lp.additionalDirectories || rp.additionalDirectories
|
|
233
|
+
? dedupe([
|
|
234
|
+
...(lp.additionalDirectories ?? []),
|
|
235
|
+
...(rp.additionalDirectories ?? []),
|
|
236
|
+
])
|
|
237
|
+
: undefined,
|
|
238
|
+
};
|
|
239
|
+
// Clean up undefined keys
|
|
240
|
+
if (!result.permissions.allow)
|
|
241
|
+
delete result.permissions.allow;
|
|
242
|
+
if (!result.permissions.deny)
|
|
243
|
+
delete result.permissions.deny;
|
|
244
|
+
if (!result.permissions.permissionMode)
|
|
245
|
+
delete result.permissions.permissionMode;
|
|
246
|
+
if (!result.permissions.additionalDirectories)
|
|
247
|
+
delete result.permissions.additionalDirectories;
|
|
248
|
+
}
|
|
249
|
+
// hooks: concatenate per-event
|
|
250
|
+
result.hooks = mergeHooks(localMerged.hooks, remote.hooks);
|
|
251
|
+
// Scalar / last-write-wins fields: remote wins
|
|
252
|
+
if (remote.language !== undefined)
|
|
253
|
+
result.language = remote.language;
|
|
254
|
+
if (remote.model !== undefined)
|
|
255
|
+
result.model = remote.model;
|
|
256
|
+
if (remote.autoMemoryEnabled !== undefined)
|
|
257
|
+
result.autoMemoryEnabled = remote.autoMemoryEnabled;
|
|
258
|
+
if (remote.autoMemoryFrequency !== undefined)
|
|
259
|
+
result.autoMemoryFrequency = remote.autoMemoryFrequency;
|
|
260
|
+
if (remote.models !== undefined)
|
|
261
|
+
result.models = remote.models;
|
|
262
|
+
if (remote.marketplaces !== undefined)
|
|
263
|
+
result.marketplaces = remote.marketplaces;
|
|
264
|
+
if (remote.enabledPlugins !== undefined)
|
|
265
|
+
result.enabledPlugins = remote.enabledPlugins;
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Singleton object for consumers that prefer a namespace-style import.
|
|
270
|
+
* Usage: import { remoteSettingsService } from "./remoteSettingsService.js"
|
|
271
|
+
*/
|
|
272
|
+
export const remoteSettingsService = {
|
|
273
|
+
initialize,
|
|
274
|
+
getRemoteSettingsSync,
|
|
275
|
+
refresh,
|
|
276
|
+
clear,
|
|
277
|
+
shutdown,
|
|
278
|
+
mergeRemoteSettings,
|
|
279
|
+
};
|
|
@@ -40,6 +40,8 @@ export interface WaveConfiguration {
|
|
|
40
40
|
autoMemoryEnabled?: boolean;
|
|
41
41
|
/** Frequency of auto-memory extraction turns */
|
|
42
42
|
autoMemoryFrequency?: number;
|
|
43
|
+
/** Persisted model selection (from /model command) */
|
|
44
|
+
model?: string;
|
|
43
45
|
/** Model-specific configuration overrides */
|
|
44
46
|
models?: Record<string, Partial<ModelConfig>>;
|
|
45
47
|
/** Scoped marketplace declarations */
|
|
@@ -124,5 +126,23 @@ interface Logger {
|
|
|
124
126
|
info: (...args: unknown[]) => void;
|
|
125
127
|
debug: (...args: unknown[]) => void;
|
|
126
128
|
}
|
|
129
|
+
export interface RemoteSettingsResponse {
|
|
130
|
+
uuid: string;
|
|
131
|
+
checksum: string;
|
|
132
|
+
settings: WaveConfiguration;
|
|
133
|
+
}
|
|
134
|
+
export interface RemoteSettingsCache {
|
|
135
|
+
uuid: string;
|
|
136
|
+
checksum: string;
|
|
137
|
+
settings: WaveConfiguration;
|
|
138
|
+
fetchedAt: string;
|
|
139
|
+
}
|
|
140
|
+
export interface RemoteSettingsFetchResult {
|
|
141
|
+
success: boolean;
|
|
142
|
+
settings?: WaveConfiguration | null;
|
|
143
|
+
checksum?: string;
|
|
144
|
+
error?: string;
|
|
145
|
+
notConfigured?: boolean;
|
|
146
|
+
}
|
|
127
147
|
export {};
|
|
128
148
|
//# sourceMappingURL=configuration.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/types/configuration.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/types/configuration.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerSetup.d.ts","sourceRoot":"","sources":["../../src/utils/containerSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAwB3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAAc,EAEf,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"containerSetup.d.ts","sourceRoot":"","sources":["../../src/utils/containerSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAwB3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAGhB,uBAAuB,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvC,sBAAsB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,wBAAwB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzD,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,0BAA0B,GACvC,SAAS,CAsSX"}
|
|
@@ -29,6 +29,7 @@ import { USER_MEMORY_FILE } from "./constants.js";
|
|
|
29
29
|
import { getGitMainRepoRoot } from "./gitUtils.js";
|
|
30
30
|
import { logger } from "./globalLogger.js";
|
|
31
31
|
import { authService } from "../services/authService.js";
|
|
32
|
+
import { remoteSettingsService } from "../services/remoteSettingsService.js";
|
|
32
33
|
export function setupAgentContainer(setupOptions) {
|
|
33
34
|
const { options, workdir, configurationService, systemPrompt, stream, onBackgroundTasksChange, onTasksChange, onPermissionModeChange, handlePlanModeTransition, setPermissionMode, addPermissionRule, addUsage, } = setupOptions;
|
|
34
35
|
const callbacks = options.callbacks || {};
|
|
@@ -109,6 +110,15 @@ export function setupAgentContainer(setupOptions) {
|
|
|
109
110
|
mcpManager.refreshCredentials(newServerUrl, newToken);
|
|
110
111
|
}
|
|
111
112
|
});
|
|
113
|
+
// Wire up auth change callback to refresh/clear remote settings
|
|
114
|
+
authService.onAuthChange((event) => {
|
|
115
|
+
if (event === "login") {
|
|
116
|
+
remoteSettingsService.refresh();
|
|
117
|
+
}
|
|
118
|
+
else if (event === "logout") {
|
|
119
|
+
remoteSettingsService.clear();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
112
122
|
const lspManager = options.lspManager || new LspManager(container);
|
|
113
123
|
container.register("LspManager", lspManager);
|
|
114
124
|
const permissionManager = new PermissionManager(container, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openaiClient.d.ts","sourceRoot":"","sources":["../../src/utils/openaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sCAAsC,EACtC,mCAAmC,EACnC,mBAAmB,EACnB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,KAAK,YAAY,GACb,sCAAsC,GACtC,mCAAmC,CAAC;AAExC,UAAU,WAAW,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IACxC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC,IAAI,IAAI;;qBAGO,CAAC,SAAS,YAAY,UACrB,CAAC,YACC;gBAAE,MAAM,CAAC,EAAE,WAAW,CAAA;aAAE,KACjC,UAAU,CACX,CAAC,SAAS,mCAAmC,GACzC,aAAa,CAAC,mBAAmB,CAAC,GAClC,cAAc,CACnB;;MA2BN;YAEa,OAAO;
|
|
1
|
+
{"version":3,"file":"openaiClient.d.ts","sourceRoot":"","sources":["../../src/utils/openaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sCAAsC,EACtC,mCAAmC,EACnC,mBAAmB,EACnB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,KAAK,YAAY,GACb,sCAAsC,GACtC,mCAAmC,CAAC;AAExC,UAAU,WAAW,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IACxC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC,IAAI,IAAI;;qBAGO,CAAC,SAAS,YAAY,UACrB,CAAC,YACC;gBAAE,MAAM,CAAC,EAAE,WAAW,CAAA;aAAE,KACjC,UAAU,CACX,CAAC,SAAS,mCAAmC,GACzC,aAAa,CAAC,mBAAmB,CAAC,GAClC,cAAc,CACnB;;MA2BN;YAEa,OAAO;YAwIN,oBAAoB;CAqCpC"}
|
|
@@ -107,8 +107,10 @@ export class OpenAIClient {
|
|
|
107
107
|
: response.statusText);
|
|
108
108
|
error.status = response.status;
|
|
109
109
|
error.body = errorBody;
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
const retryableStatus = response.status === 429 ||
|
|
111
|
+
(response.status >= 500 && response.status !== 501);
|
|
112
|
+
if (retryableStatus && attempt < maxRetries) {
|
|
113
|
+
logger.warn("OpenAI API error, retrying...", {
|
|
112
114
|
attempt: attempt + 1,
|
|
113
115
|
status: response.status,
|
|
114
116
|
});
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
shutdownTelemetry,
|
|
52
52
|
} from "./telemetry/instrumentation.js";
|
|
53
53
|
import { logOTelEvent } from "./telemetry/events.js";
|
|
54
|
+
import { remoteSettingsService } from "./services/remoteSettingsService.js";
|
|
54
55
|
|
|
55
56
|
export class Agent {
|
|
56
57
|
private messageManager: MessageManager;
|
|
@@ -720,6 +721,8 @@ export class Agent {
|
|
|
720
721
|
error,
|
|
721
722
|
);
|
|
722
723
|
}
|
|
724
|
+
// Cleanup remote settings polling
|
|
725
|
+
remoteSettingsService.shutdown();
|
|
723
726
|
// Cleanup memory store
|
|
724
727
|
}
|
|
725
728
|
|
|
@@ -44,6 +44,10 @@ import {
|
|
|
44
44
|
} from "../utils/constants.js";
|
|
45
45
|
import { ClientOptions } from "openai";
|
|
46
46
|
import { parseCustomHeaders } from "../utils/stringUtils.js";
|
|
47
|
+
import {
|
|
48
|
+
getRemoteSettingsSync,
|
|
49
|
+
mergeRemoteSettings,
|
|
50
|
+
} from "./remoteSettingsService.js";
|
|
47
51
|
|
|
48
52
|
/**
|
|
49
53
|
* Default ConfigurationService implementation
|
|
@@ -101,19 +105,25 @@ export class ConfigurationService {
|
|
|
101
105
|
};
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
// Merge remote settings (highest priority: Remote > Local > Project > User)
|
|
109
|
+
const remoteSettings = getRemoteSettingsSync();
|
|
110
|
+
const finalConfig = remoteSettings
|
|
111
|
+
? mergeRemoteSettings(mergedConfig, remoteSettings)
|
|
112
|
+
: mergedConfig;
|
|
113
|
+
|
|
104
114
|
// Success case
|
|
105
|
-
this.currentConfiguration =
|
|
115
|
+
this.currentConfiguration = finalConfig;
|
|
106
116
|
|
|
107
117
|
// Set environment variables from merged config and inject system variables
|
|
108
118
|
const env = {
|
|
109
|
-
...(
|
|
119
|
+
...(finalConfig.env || {}),
|
|
110
120
|
WAVE_PROJECT_DIR: workdir,
|
|
111
121
|
};
|
|
112
122
|
this.setEnvironmentVars(env);
|
|
113
|
-
|
|
123
|
+
finalConfig.env = env;
|
|
114
124
|
|
|
115
125
|
return {
|
|
116
|
-
configuration:
|
|
126
|
+
configuration: finalConfig,
|
|
117
127
|
success: true,
|
|
118
128
|
sourcePath: "merged configuration",
|
|
119
129
|
warnings: validation.warnings,
|
|
@@ -498,7 +508,10 @@ export class ConfigurationService {
|
|
|
498
508
|
): ModelConfig {
|
|
499
509
|
// Resolve agent model: override > options > process.env (includes settings.json env)
|
|
500
510
|
const resolvedAgentModel =
|
|
501
|
-
model ||
|
|
511
|
+
model ||
|
|
512
|
+
this.options.model ||
|
|
513
|
+
process.env.WAVE_MODEL ||
|
|
514
|
+
this.currentConfiguration?.model;
|
|
502
515
|
|
|
503
516
|
// Resolve fast model: override > options > process.env (includes settings.json env)
|
|
504
517
|
const resolvedFastModel =
|
|
@@ -686,6 +699,28 @@ export class ConfigurationService {
|
|
|
686
699
|
*/
|
|
687
700
|
setModel(model: string): void {
|
|
688
701
|
this.options.model = model;
|
|
702
|
+
this.persistModelToSettings(model);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
private async persistModelToSettings(model: string): Promise<void> {
|
|
706
|
+
const configPath = getUserConfigPaths()[0]; // ~/.wave/settings.json
|
|
707
|
+
const configDir = path.dirname(configPath);
|
|
708
|
+
if (!existsSync(configDir)) {
|
|
709
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
let config: WaveConfiguration = {};
|
|
713
|
+
if (existsSync(configPath)) {
|
|
714
|
+
try {
|
|
715
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
716
|
+
config = JSON.parse(content);
|
|
717
|
+
} catch {
|
|
718
|
+
// Start fresh if corrupted
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
config.model = model;
|
|
723
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
689
724
|
}
|
|
690
725
|
|
|
691
726
|
/**
|
|
@@ -700,6 +735,11 @@ export class ConfigurationService {
|
|
|
700
735
|
models.add(currentModel);
|
|
701
736
|
}
|
|
702
737
|
|
|
738
|
+
// Persisted model from settings (includes remote-merged)
|
|
739
|
+
if (this.currentConfiguration?.model) {
|
|
740
|
+
models.add(this.currentConfiguration.model);
|
|
741
|
+
}
|
|
742
|
+
|
|
703
743
|
// Add models from merged configuration
|
|
704
744
|
if (this.currentConfiguration?.models) {
|
|
705
745
|
Object.keys(this.currentConfiguration.models).forEach((model) => {
|
|
@@ -1129,6 +1169,7 @@ export function loadWaveConfigFromFile(
|
|
|
1129
1169
|
permissions: config.permissions || undefined,
|
|
1130
1170
|
enabledPlugins: config.enabledPlugins || undefined,
|
|
1131
1171
|
language: config.language || undefined,
|
|
1172
|
+
model: config.model || undefined,
|
|
1132
1173
|
autoMemoryEnabled:
|
|
1133
1174
|
config.autoMemoryEnabled !== undefined
|
|
1134
1175
|
? config.autoMemoryEnabled
|
|
@@ -1258,6 +1299,11 @@ export function loadMergedWaveConfig(
|
|
|
1258
1299
|
mergedConfig.language = config.language;
|
|
1259
1300
|
}
|
|
1260
1301
|
|
|
1302
|
+
// Merge model (last one wins)
|
|
1303
|
+
if (config.model !== undefined) {
|
|
1304
|
+
mergedConfig.model = config.model;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1261
1307
|
// Merge autoMemoryEnabled (last one wins)
|
|
1262
1308
|
if (config.autoMemoryEnabled !== undefined) {
|
|
1263
1309
|
mergedConfig.autoMemoryEnabled = config.autoMemoryEnabled;
|
|
@@ -1306,6 +1352,7 @@ export function loadMergedWaveConfig(
|
|
|
1306
1352
|
? mergedConfig.enabledPlugins
|
|
1307
1353
|
: undefined,
|
|
1308
1354
|
language: mergedConfig.language,
|
|
1355
|
+
model: mergedConfig.model,
|
|
1309
1356
|
autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
|
|
1310
1357
|
marketplaces:
|
|
1311
1358
|
mergedConfig.marketplaces &&
|
|
@@ -22,6 +22,7 @@ import type { MemoryRuleManager } from "../managers/MemoryRuleManager.js";
|
|
|
22
22
|
import type { LiveConfigManager } from "../managers/liveConfigManager.js";
|
|
23
23
|
import type { TaskManager } from "./taskManager.js";
|
|
24
24
|
import type { PermissionManager } from "../managers/permissionManager.js";
|
|
25
|
+
import { remoteSettingsService } from "./remoteSettingsService.js";
|
|
25
26
|
|
|
26
27
|
export interface InitializationContext {
|
|
27
28
|
skillManager: SkillManager;
|
|
@@ -288,6 +289,18 @@ export class InitializationService {
|
|
|
288
289
|
// Don't throw error to prevent app startup failure - continue without live reload
|
|
289
290
|
}
|
|
290
291
|
|
|
292
|
+
// Initialize remote settings (fetch server-managed config)
|
|
293
|
+
try {
|
|
294
|
+
const phaseStart = performance.now();
|
|
295
|
+
await remoteSettingsService.initialize();
|
|
296
|
+
logger?.debug(
|
|
297
|
+
`Initialization Phase [Remote Settings] took ${(performance.now() - phaseStart).toFixed(2)}ms`,
|
|
298
|
+
);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
logger?.error("Failed to initialize remote settings:", error);
|
|
301
|
+
// Don't throw error to prevent app startup failure - continue without remote settings
|
|
302
|
+
}
|
|
303
|
+
|
|
291
304
|
// Memory is lazy-cached on first getCombinedMemoryContent call
|
|
292
305
|
// No explicit loading needed during initialization
|
|
293
306
|
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { authService } from "./authService.js";
|
|
6
|
+
import type {
|
|
7
|
+
RemoteSettingsCache,
|
|
8
|
+
RemoteSettingsFetchResult,
|
|
9
|
+
RemoteSettingsResponse,
|
|
10
|
+
} from "../types/configuration.js";
|
|
11
|
+
import type { WaveConfiguration } from "../types/configuration.js";
|
|
12
|
+
import type { HookEvent, HookEventConfig } from "../types/hooks.js";
|
|
13
|
+
import { logger } from "../utils/globalLogger.js";
|
|
14
|
+
|
|
15
|
+
const CACHE_FILE = path.join(homedir(), ".wave", "remote-settings.json");
|
|
16
|
+
const POLLING_INTERVAL_MS = 60 * 60 * 1000; // 60 minutes
|
|
17
|
+
const FETCH_TIMEOUT_MS = 10_000;
|
|
18
|
+
|
|
19
|
+
let _cachedSettings: RemoteSettingsCache | null = null;
|
|
20
|
+
let _pollingTimer: ReturnType<typeof setInterval> | null = null;
|
|
21
|
+
|
|
22
|
+
function loadCacheFromDisk(): void {
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(CACHE_FILE)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const raw = fs.readFileSync(CACHE_FILE, "utf-8");
|
|
28
|
+
const parsed: RemoteSettingsCache = JSON.parse(raw);
|
|
29
|
+
_cachedSettings = parsed;
|
|
30
|
+
logger.debug("remoteSettings: loaded cache from disk", {
|
|
31
|
+
checksum: parsed.checksum,
|
|
32
|
+
});
|
|
33
|
+
} catch (err) {
|
|
34
|
+
logger.debug("remoteSettings: failed to load cache from disk", { err });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function writeCacheToDisk(): void {
|
|
39
|
+
if (!_cachedSettings) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const dir = path.dirname(CACHE_FILE);
|
|
44
|
+
if (!fs.existsSync(dir)) {
|
|
45
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(_cachedSettings, null, 2), {
|
|
48
|
+
mode: 0o600,
|
|
49
|
+
});
|
|
50
|
+
logger.debug("remoteSettings: wrote cache to disk", {
|
|
51
|
+
checksum: _cachedSettings.checksum,
|
|
52
|
+
});
|
|
53
|
+
} catch (err) {
|
|
54
|
+
logger.debug("remoteSettings: failed to write cache to disk", { err });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function removeCacheFromDisk(): void {
|
|
59
|
+
try {
|
|
60
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
61
|
+
fs.unlinkSync(CACHE_FILE);
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
logger.debug("remoteSettings: failed to remove cache file", { err });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function fetchRemoteSettings(): Promise<RemoteSettingsFetchResult> {
|
|
69
|
+
if (!authService.isSSOAuthenticated()) {
|
|
70
|
+
logger.debug("remoteSettings: skipping fetch — not SSO authenticated");
|
|
71
|
+
return { success: false, error: "Not SSO authenticated" };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const token = authService.getSSOToken();
|
|
75
|
+
const serverUrl = authService.getServerUrl();
|
|
76
|
+
if (!token || !serverUrl) {
|
|
77
|
+
return { success: false, error: "Missing SSO token or server URL" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const headers: Record<string, string> = {
|
|
81
|
+
Authorization: `Bearer ${token}`,
|
|
82
|
+
};
|
|
83
|
+
if (_cachedSettings?.checksum) {
|
|
84
|
+
headers["If-None-Match"] = _cachedSettings.checksum;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const response = await fetch(`${serverUrl}/api/wave/settings`, {
|
|
89
|
+
method: "GET",
|
|
90
|
+
headers,
|
|
91
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (response.status === 304) {
|
|
95
|
+
logger.debug("remoteSettings: 304 unchanged", {
|
|
96
|
+
checksum: _cachedSettings?.checksum,
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
settings: _cachedSettings!.settings,
|
|
101
|
+
checksum: _cachedSettings!.checksum,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (response.status === 404) {
|
|
106
|
+
logger.debug("remoteSettings: 404 not configured — clearing stale cache");
|
|
107
|
+
_cachedSettings = null;
|
|
108
|
+
removeCacheFromDisk();
|
|
109
|
+
return { success: true, notConfigured: true, settings: null };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
const body = await response.text().catch(() => "");
|
|
114
|
+
logger.debug("remoteSettings: fetch failed", {
|
|
115
|
+
status: response.status,
|
|
116
|
+
body: body.slice(0, 200),
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
error: `HTTP ${response.status}`,
|
|
121
|
+
settings: _cachedSettings?.settings ?? null,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const data = (await response.json()) as RemoteSettingsResponse;
|
|
126
|
+
_cachedSettings = {
|
|
127
|
+
uuid: data.uuid,
|
|
128
|
+
checksum: data.checksum,
|
|
129
|
+
settings: data.settings,
|
|
130
|
+
fetchedAt: new Date().toISOString(),
|
|
131
|
+
};
|
|
132
|
+
writeCacheToDisk();
|
|
133
|
+
logger.debug("remoteSettings: fetched new settings", {
|
|
134
|
+
checksum: data.checksum,
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
settings: data.settings,
|
|
139
|
+
checksum: data.checksum,
|
|
140
|
+
};
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.debug("remoteSettings: network error, using cache", { err });
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
error: err instanceof Error ? err.message : String(err),
|
|
146
|
+
settings: _cachedSettings?.settings ?? null,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function startPolling(): void {
|
|
152
|
+
if (_pollingTimer) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
_pollingTimer = setInterval(async () => {
|
|
156
|
+
try {
|
|
157
|
+
await fetchRemoteSettings();
|
|
158
|
+
} catch (err) {
|
|
159
|
+
logger.debug("remoteSettings: polling fetch error", { err });
|
|
160
|
+
}
|
|
161
|
+
}, POLLING_INTERVAL_MS);
|
|
162
|
+
_pollingTimer.unref();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function initialize(): void {
|
|
166
|
+
loadCacheFromDisk();
|
|
167
|
+
// Fire-and-forget the initial fetch, then start background polling
|
|
168
|
+
fetchRemoteSettings()
|
|
169
|
+
.then(() => startPolling())
|
|
170
|
+
.catch((err) => {
|
|
171
|
+
logger.debug("remoteSettings: initial fetch failed", { err });
|
|
172
|
+
startPolling();
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function getRemoteSettingsSync(): WaveConfiguration | null {
|
|
177
|
+
return _cachedSettings?.settings ?? null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function refresh(): Promise<RemoteSettingsFetchResult> {
|
|
181
|
+
// Clear in-memory so we force a fresh fetch
|
|
182
|
+
_cachedSettings = null;
|
|
183
|
+
removeCacheFromDisk();
|
|
184
|
+
return fetchRemoteSettings();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function clear(): void {
|
|
188
|
+
_cachedSettings = null;
|
|
189
|
+
removeCacheFromDisk();
|
|
190
|
+
if (_pollingTimer) {
|
|
191
|
+
clearInterval(_pollingTimer);
|
|
192
|
+
_pollingTimer = null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function shutdown(): void {
|
|
197
|
+
if (_pollingTimer) {
|
|
198
|
+
clearInterval(_pollingTimer);
|
|
199
|
+
_pollingTimer = null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function dedupe(arr: string[]): string[] {
|
|
204
|
+
return [...new Set(arr)];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function mergeHooks(
|
|
208
|
+
local: Partial<Record<HookEvent, HookEventConfig[]>> | undefined,
|
|
209
|
+
remote: Partial<Record<HookEvent, HookEventConfig[]>> | undefined,
|
|
210
|
+
): Partial<Record<HookEvent, HookEventConfig[]>> | undefined {
|
|
211
|
+
if (!remote && !local) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
if (!remote) {
|
|
215
|
+
return local;
|
|
216
|
+
}
|
|
217
|
+
if (!local) {
|
|
218
|
+
return remote;
|
|
219
|
+
}
|
|
220
|
+
const merged: Partial<Record<HookEvent, HookEventConfig[]>> = { ...local };
|
|
221
|
+
for (const [event, remoteHooks] of Object.entries(remote)) {
|
|
222
|
+
const localHooks = merged[event as HookEvent] ?? [];
|
|
223
|
+
// Concatenate + dedupe by JSON serialization
|
|
224
|
+
const combined = [...localHooks, ...(remoteHooks ?? [])];
|
|
225
|
+
const seen = new Set<string>();
|
|
226
|
+
merged[event as HookEvent] = combined.filter((h) => {
|
|
227
|
+
const key = JSON.stringify(h);
|
|
228
|
+
if (seen.has(key)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
seen.add(key);
|
|
232
|
+
return true;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return merged;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function mergeRemoteSettings(
|
|
239
|
+
localMerged: WaveConfiguration,
|
|
240
|
+
remote: WaveConfiguration,
|
|
241
|
+
): WaveConfiguration {
|
|
242
|
+
const result: WaveConfiguration = { ...localMerged };
|
|
243
|
+
|
|
244
|
+
// env: merge by key, remote wins per-key
|
|
245
|
+
if (remote.env || localMerged.env) {
|
|
246
|
+
result.env = { ...localMerged.env, ...remote.env };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// permissions
|
|
250
|
+
if (remote.permissions || localMerged.permissions) {
|
|
251
|
+
const lp = localMerged.permissions ?? {};
|
|
252
|
+
const rp = remote.permissions ?? {};
|
|
253
|
+
result.permissions = {
|
|
254
|
+
// allow: concatenate + dedupe
|
|
255
|
+
allow:
|
|
256
|
+
lp.allow || rp.allow
|
|
257
|
+
? dedupe([...(lp.allow ?? []), ...(rp.allow ?? [])])
|
|
258
|
+
: undefined,
|
|
259
|
+
// deny: concatenate + dedupe
|
|
260
|
+
deny:
|
|
261
|
+
lp.deny || rp.deny
|
|
262
|
+
? dedupe([...(lp.deny ?? []), ...(rp.deny ?? [])])
|
|
263
|
+
: undefined,
|
|
264
|
+
// permissionMode: remote wins (scalar)
|
|
265
|
+
permissionMode: rp.permissionMode ?? lp.permissionMode,
|
|
266
|
+
// additionalDirectories: concatenate + dedupe
|
|
267
|
+
additionalDirectories:
|
|
268
|
+
lp.additionalDirectories || rp.additionalDirectories
|
|
269
|
+
? dedupe([
|
|
270
|
+
...(lp.additionalDirectories ?? []),
|
|
271
|
+
...(rp.additionalDirectories ?? []),
|
|
272
|
+
])
|
|
273
|
+
: undefined,
|
|
274
|
+
};
|
|
275
|
+
// Clean up undefined keys
|
|
276
|
+
if (!result.permissions.allow) delete result.permissions.allow;
|
|
277
|
+
if (!result.permissions.deny) delete result.permissions.deny;
|
|
278
|
+
if (!result.permissions.permissionMode)
|
|
279
|
+
delete result.permissions.permissionMode;
|
|
280
|
+
if (!result.permissions.additionalDirectories)
|
|
281
|
+
delete result.permissions.additionalDirectories;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// hooks: concatenate per-event
|
|
285
|
+
result.hooks = mergeHooks(localMerged.hooks, remote.hooks);
|
|
286
|
+
|
|
287
|
+
// Scalar / last-write-wins fields: remote wins
|
|
288
|
+
if (remote.language !== undefined) result.language = remote.language;
|
|
289
|
+
if (remote.model !== undefined) result.model = remote.model;
|
|
290
|
+
if (remote.autoMemoryEnabled !== undefined)
|
|
291
|
+
result.autoMemoryEnabled = remote.autoMemoryEnabled;
|
|
292
|
+
if (remote.autoMemoryFrequency !== undefined)
|
|
293
|
+
result.autoMemoryFrequency = remote.autoMemoryFrequency;
|
|
294
|
+
if (remote.models !== undefined) result.models = remote.models;
|
|
295
|
+
if (remote.marketplaces !== undefined)
|
|
296
|
+
result.marketplaces = remote.marketplaces;
|
|
297
|
+
if (remote.enabledPlugins !== undefined)
|
|
298
|
+
result.enabledPlugins = remote.enabledPlugins;
|
|
299
|
+
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Singleton object for consumers that prefer a namespace-style import.
|
|
305
|
+
* Usage: import { remoteSettingsService } from "./remoteSettingsService.js"
|
|
306
|
+
*/
|
|
307
|
+
export const remoteSettingsService = {
|
|
308
|
+
initialize,
|
|
309
|
+
getRemoteSettingsSync,
|
|
310
|
+
refresh,
|
|
311
|
+
clear,
|
|
312
|
+
shutdown,
|
|
313
|
+
mergeRemoteSettings,
|
|
314
|
+
} as const;
|
|
@@ -44,6 +44,8 @@ export interface WaveConfiguration {
|
|
|
44
44
|
autoMemoryEnabled?: boolean;
|
|
45
45
|
/** Frequency of auto-memory extraction turns */
|
|
46
46
|
autoMemoryFrequency?: number;
|
|
47
|
+
/** Persisted model selection (from /model command) */
|
|
48
|
+
model?: string;
|
|
47
49
|
/** Model-specific configuration overrides */
|
|
48
50
|
models?: Record<string, Partial<ModelConfig>>;
|
|
49
51
|
/** Scoped marketplace declarations */
|
|
@@ -138,3 +140,24 @@ interface Logger {
|
|
|
138
140
|
info: (...args: unknown[]) => void;
|
|
139
141
|
debug: (...args: unknown[]) => void;
|
|
140
142
|
}
|
|
143
|
+
|
|
144
|
+
export interface RemoteSettingsResponse {
|
|
145
|
+
uuid: string;
|
|
146
|
+
checksum: string;
|
|
147
|
+
settings: WaveConfiguration;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface RemoteSettingsCache {
|
|
151
|
+
uuid: string;
|
|
152
|
+
checksum: string;
|
|
153
|
+
settings: WaveConfiguration;
|
|
154
|
+
fetchedAt: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface RemoteSettingsFetchResult {
|
|
158
|
+
success: boolean;
|
|
159
|
+
settings?: WaveConfiguration | null;
|
|
160
|
+
checksum?: string;
|
|
161
|
+
error?: string;
|
|
162
|
+
notConfigured?: boolean;
|
|
163
|
+
}
|
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
|
|
40
40
|
import { logger } from "./globalLogger.js";
|
|
41
41
|
import { authService } from "../services/authService.js";
|
|
42
|
+
import { remoteSettingsService } from "../services/remoteSettingsService.js";
|
|
42
43
|
|
|
43
44
|
export interface AgentContainerSetupOptions {
|
|
44
45
|
options: AgentOptions;
|
|
@@ -170,6 +171,15 @@ export function setupAgentContainer(
|
|
|
170
171
|
}
|
|
171
172
|
});
|
|
172
173
|
|
|
174
|
+
// Wire up auth change callback to refresh/clear remote settings
|
|
175
|
+
authService.onAuthChange((event) => {
|
|
176
|
+
if (event === "login") {
|
|
177
|
+
remoteSettingsService.refresh();
|
|
178
|
+
} else if (event === "logout") {
|
|
179
|
+
remoteSettingsService.clear();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
173
183
|
const lspManager = options.lspManager || new LspManager(container);
|
|
174
184
|
container.register("LspManager", lspManager);
|
|
175
185
|
|
|
@@ -176,8 +176,11 @@ export class OpenAIClient {
|
|
|
176
176
|
error.status = response.status;
|
|
177
177
|
error.body = errorBody;
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
const retryableStatus =
|
|
180
|
+
response.status === 429 ||
|
|
181
|
+
(response.status >= 500 && response.status !== 501);
|
|
182
|
+
if (retryableStatus && attempt < maxRetries) {
|
|
183
|
+
logger.warn("OpenAI API error, retrying...", {
|
|
181
184
|
attempt: attempt + 1,
|
|
182
185
|
status: response.status,
|
|
183
186
|
});
|