wave-agent-sdk 1.1.5 → 1.3.0
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 +128 -28
- package/dist/agent.js +201 -49
- package/dist/builtin/index.js +2 -0
- package/dist/builtin/plugins.js +11 -20
- package/dist/builtin/skills/settings.js +7 -20
- package/dist/builtin/skills/wave-daemon.d.ts +1 -0
- package/dist/builtin/skills/wave-daemon.js +194 -0
- package/dist/constants/images.d.ts +26 -0
- package/dist/constants/images.js +26 -0
- package/dist/constants/index.d.ts +16 -0
- package/dist/constants/index.js +16 -0
- package/dist/constants/memory.d.ts +26 -0
- package/dist/constants/memory.js +34 -0
- package/dist/constants/messages.d.ts +11 -0
- package/dist/constants/messages.js +11 -0
- package/dist/constants/plugins.d.ts +8 -0
- package/dist/constants/plugins.js +8 -0
- package/dist/constants/tools.d.ts +1 -0
- package/dist/constants/tools.js +1 -0
- package/dist/core/plugin.d.ts +54 -10
- package/dist/core/plugin.js +137 -23
- package/dist/core/session.d.ts +1 -1
- package/dist/core/session.js +1 -1
- package/dist/exec/catalog.d.ts +140 -0
- package/dist/exec/catalog.js +470 -0
- package/dist/exec/catalogAnnouncement.d.ts +89 -0
- package/dist/exec/catalogAnnouncement.js +293 -0
- package/dist/exec/constants.d.ts +51 -0
- package/dist/exec/constants.js +51 -0
- package/dist/exec/execRuntime.d.ts +55 -0
- package/dist/exec/execRuntime.js +217 -0
- package/dist/exec/workerSource.d.ts +28 -0
- package/dist/exec/workerSource.js +299 -0
- package/dist/host/index.d.ts +23 -0
- package/dist/host/index.js +23 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +8 -1
- package/dist/managers/MemoryRuleManager.d.ts +6 -0
- package/dist/managers/MemoryRuleManager.js +12 -0
- package/dist/managers/aiManager.d.ts +35 -25
- package/dist/managers/aiManager.js +204 -202
- package/dist/managers/backgroundTaskManager.js +14 -0
- package/dist/managers/bashModeManager.d.ts +33 -0
- package/dist/managers/bashModeManager.js +110 -0
- package/dist/managers/hookManager.d.ts +18 -0
- package/dist/managers/hookManager.js +37 -3
- package/dist/managers/liveConfigManager.d.ts +33 -0
- package/dist/managers/liveConfigManager.js +106 -11
- package/dist/managers/lspManager.d.ts +9 -0
- package/dist/managers/lspManager.js +47 -18
- package/dist/managers/mcpManager.d.ts +68 -10
- package/dist/managers/mcpManager.js +265 -15
- package/dist/managers/messageManager.d.ts +60 -18
- package/dist/managers/messageManager.js +170 -81
- package/dist/managers/permissionManager.d.ts +69 -0
- package/dist/managers/permissionManager.js +221 -78
- package/dist/managers/planManager.d.ts +9 -0
- package/dist/managers/planManager.js +19 -1
- package/dist/managers/pluginManager.d.ts +46 -2
- package/dist/managers/pluginManager.js +117 -11
- package/dist/managers/pluginScopeManager.d.ts +15 -2
- package/dist/managers/pluginScopeManager.js +20 -1
- package/dist/managers/skillManager.d.ts +50 -0
- package/dist/managers/skillManager.js +166 -12
- package/dist/managers/slashCommandManager.d.ts +10 -0
- package/dist/managers/slashCommandManager.js +44 -31
- package/dist/managers/subagentManager.d.ts +15 -0
- package/dist/managers/subagentManager.js +81 -9
- package/dist/managers/toolManager.d.ts +29 -3
- package/dist/managers/toolManager.js +87 -13
- package/dist/managers/workflowManager.js +6 -0
- package/dist/prompts/autoMemory.d.ts +9 -0
- package/dist/prompts/autoMemory.js +30 -31
- package/dist/prompts/autoMemoryExtraction.d.ts +4 -0
- package/dist/prompts/autoMemoryExtraction.js +8 -111
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- package/dist/prompts/memoryTypes.d.ts +63 -0
- package/dist/prompts/memoryTypes.js +191 -0
- package/dist/services/GitService.d.ts +7 -0
- package/dist/services/GitService.js +23 -0
- package/dist/services/MarketplaceService.d.ts +101 -17
- package/dist/services/MarketplaceService.js +323 -102
- package/dist/services/artifactContent.d.ts +84 -0
- package/dist/services/artifactContent.js +204 -0
- package/dist/services/artifactSession.d.ts +6 -0
- package/dist/services/artifactSession.js +17 -0
- package/dist/services/autoMemoryService.js +5 -13
- package/dist/services/configurationService.d.ts +92 -9
- package/dist/services/configurationService.js +246 -64
- package/dist/services/contentSummarizer.d.ts +15 -0
- package/dist/services/contentSummarizer.js +45 -0
- package/dist/services/execAvailability.d.ts +9 -0
- package/dist/services/execAvailability.js +32 -0
- package/dist/services/fileWatcher.js +61 -6
- package/dist/services/initializationService.js +21 -17
- package/dist/services/interactionService.d.ts +9 -1
- package/dist/services/interactionService.js +28 -8
- package/dist/services/jsonlHandler.d.ts +98 -0
- package/dist/services/jsonlHandler.js +250 -12
- package/dist/services/memory.d.ts +17 -1
- package/dist/services/memory.js +44 -7
- package/dist/services/officialMarketplaceMirror.d.ts +85 -0
- package/dist/services/officialMarketplaceMirror.js +290 -0
- package/dist/services/pluginLoader.d.ts +12 -4
- package/dist/services/pluginLoader.js +38 -7
- package/dist/services/remoteSettingsService.js +20 -6
- package/dist/services/session.d.ts +74 -0
- package/dist/services/session.js +174 -16
- package/dist/services/sessionEntries.d.ts +2 -0
- package/dist/services/sessionEntries.js +20 -0
- package/dist/services/worktreeHooks.js +6 -1
- package/dist/stdio/index.d.ts +12 -0
- package/dist/stdio/index.js +12 -0
- package/dist/stdio/notificationRouter.d.ts +38 -0
- package/dist/stdio/notificationRouter.js +97 -0
- package/dist/stdio/rpcClient.d.ts +18 -0
- package/dist/stdio/rpcClient.js +10 -0
- package/dist/stdio/stdioAgent.d.ts +229 -0
- package/dist/stdio/stdioAgent.js +360 -0
- package/dist/tools/artifactTool.js +406 -273
- package/dist/tools/bashTool.js +10 -6
- package/dist/tools/editTool.js +6 -3
- package/dist/tools/execTool.d.ts +2 -0
- package/dist/tools/execTool.js +165 -0
- package/dist/tools/exitPlanMode.js +10 -2
- package/dist/tools/grepTool.js +7 -1
- package/dist/tools/readTool.js +30 -2
- package/dist/tools/types.d.ts +34 -8
- package/dist/tools/webFetchTool.js +15 -166
- package/dist/tools/workflowTool.js +40 -8
- package/dist/tools/writeTool.js +6 -3
- package/dist/types/agent.d.ts +24 -1
- package/dist/types/commands.d.ts +7 -0
- package/dist/types/configuration.d.ts +45 -2
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/hooks.js +19 -0
- package/dist/types/marketplace.d.ts +40 -2
- package/dist/types/mcp.d.ts +42 -0
- package/dist/types/messaging.d.ts +1 -8
- package/dist/types/permissions.d.ts +22 -0
- package/dist/types/permissions.js +17 -0
- package/dist/types/plugins.d.ts +26 -2
- package/dist/types/skills.d.ts +26 -0
- package/dist/utils/bashParser.d.ts +17 -0
- package/dist/utils/bashParser.js +72 -0
- package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
- package/dist/utils/bashStructure/bashLexer.js +676 -0
- package/dist/utils/bashStructure/bashParser.d.ts +144 -0
- package/dist/utils/bashStructure/bashParser.js +606 -0
- package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
- package/dist/utils/bashStructure/bashSemantics.js +477 -0
- package/dist/utils/bashStructure/index.d.ts +26 -0
- package/dist/utils/bashStructure/index.js +27 -0
- package/dist/utils/bashStructure/types.d.ts +62 -0
- package/dist/utils/bashStructure/types.js +47 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +10 -0
- package/dist/utils/containerSetup.js +48 -6
- package/dist/utils/convertMessagesForAPI.d.ts +7 -1
- package/dist/utils/convertMessagesForAPI.js +64 -14
- package/dist/utils/fileChangeReminder.d.ts +20 -0
- package/dist/utils/fileChangeReminder.js +153 -0
- package/dist/utils/fileSearch.js +4 -3
- package/dist/utils/fileUtils.d.ts +44 -0
- package/dist/utils/fileUtils.js +118 -0
- package/dist/utils/frontmatterYaml.d.ts +33 -0
- package/dist/utils/frontmatterYaml.js +192 -0
- package/dist/utils/imageBudget.d.ts +85 -0
- package/dist/utils/imageBudget.js +109 -0
- package/dist/utils/imageDimensions.d.ts +83 -0
- package/dist/utils/imageDimensions.js +232 -0
- package/dist/utils/imageProcessor.d.ts +66 -0
- package/dist/utils/imageProcessor.js +84 -0
- package/dist/utils/imageRewrite.d.ts +29 -0
- package/dist/utils/imageRewrite.js +251 -0
- package/dist/utils/markdownParser.d.ts +5 -1
- package/dist/utils/markdownParser.js +9 -51
- package/dist/utils/mcpInstructions.d.ts +61 -0
- package/dist/utils/mcpInstructions.js +126 -0
- package/dist/utils/mcpUtils.d.ts +7 -0
- package/dist/utils/mcpUtils.js +11 -2
- package/dist/utils/memoryAge.d.ts +32 -0
- package/dist/utils/memoryAge.js +47 -0
- package/dist/utils/memoryEntrypoint.d.ts +20 -0
- package/dist/utils/memoryEntrypoint.js +49 -0
- package/dist/utils/memoryIndex.d.ts +30 -0
- package/dist/utils/memoryIndex.js +76 -0
- package/dist/utils/messageOperations.d.ts +6 -20
- package/dist/utils/messageOperations.js +40 -91
- package/dist/utils/nestedMemory.d.ts +22 -0
- package/dist/utils/nestedMemory.js +61 -0
- package/dist/utils/npmTarball.d.ts +19 -0
- package/dist/utils/npmTarball.js +92 -0
- package/dist/utils/pluginSource.d.ts +37 -0
- package/dist/utils/pluginSource.js +73 -0
- package/dist/utils/ripgrep.d.ts +18 -4
- package/dist/utils/ripgrep.js +56 -4
- package/dist/utils/runtimeDeps.d.ts +35 -0
- package/dist/utils/runtimeDeps.js +426 -0
- package/dist/utils/skillParser.js +22 -52
- package/dist/utils/subagentParser.js +48 -45
- package/dist/utils/tokenCalculation.js +0 -8
- package/dist/utils/userSettings.d.ts +90 -0
- package/dist/utils/userSettings.js +291 -0
- package/dist/utils/worktreeUtils.d.ts +2 -1
- package/dist/utils/worktreeUtils.js +64 -34
- package/package.json +12 -4
- package/dist/managers/bangManager.d.ts +0 -26
- package/dist/managers/bangManager.js +0 -78
|
@@ -11,7 +11,7 @@ import { isValidHookEvent } from "../types/hooks.js";
|
|
|
11
11
|
import { logger } from "../utils/globalLogger.js";
|
|
12
12
|
import { getAllConfigPaths, getExistingConfigPaths, getUserConfigPaths, getProjectConfigPaths, } from "../utils/configPaths.js";
|
|
13
13
|
import { isValidEnvironmentVars, } from "../types/environment.js";
|
|
14
|
-
import { DEFAULT_WAVE_MAX_INPUT_TOKENS, DEFAULT_WAVE_MAX_OUTPUT_TOKENS, DEFAULT_SERVER_URL, } from "../utils/constants.js";
|
|
14
|
+
import { DEFAULT_WAVE_MAX_INPUT_TOKENS, DEFAULT_WAVE_MAX_OUTPUT_TOKENS, DEFAULT_SERVER_URL, DEFAULT_LANGUAGE, } from "../utils/constants.js";
|
|
15
15
|
import { parseCustomHeaders } from "../utils/stringUtils.js";
|
|
16
16
|
import { getRemoteSettingsSync, mergeRemoteSettings, } from "./remoteSettingsService.js";
|
|
17
17
|
import { createAuthAwareFetch } from "./authService.js";
|
|
@@ -119,8 +119,8 @@ export function validateConfigurationObject(config) {
|
|
|
119
119
|
result.errors.push("All permission deny rules must be strings");
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
// Validate
|
|
123
|
-
if (config.permissions.
|
|
122
|
+
// Validate defaultMode if present
|
|
123
|
+
if (config.permissions.defaultMode !== undefined) {
|
|
124
124
|
const validModes = [
|
|
125
125
|
"default",
|
|
126
126
|
"bypassPermissions",
|
|
@@ -128,9 +128,9 @@ export function validateConfigurationObject(config) {
|
|
|
128
128
|
"plan",
|
|
129
129
|
"dontAsk",
|
|
130
130
|
];
|
|
131
|
-
if (!validModes.includes(config.permissions.
|
|
131
|
+
if (!validModes.includes(config.permissions.defaultMode)) {
|
|
132
132
|
result.isValid = false;
|
|
133
|
-
result.errors.push(`Invalid
|
|
133
|
+
result.errors.push(`Invalid defaultMode: "${config.permissions.defaultMode}". Must be one of: ${validModes.join(", ")}`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -199,22 +199,38 @@ export class ConfigurationService {
|
|
|
199
199
|
this._configuredEnvKeys = new Set();
|
|
200
200
|
// Per-session environment snapshot: settings.json `env` is stored here (NOT
|
|
201
201
|
// written to process.env) so multiple sessions in one `wave --stdio` process
|
|
202
|
-
// don't cross-pollute. Resolve methods read `this.
|
|
202
|
+
// don't cross-pollute. Resolve methods read `this.liveEnvSnapshot ?? process.env`.
|
|
203
203
|
this.envSnapshot = {};
|
|
204
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Register the turn-snapshot source (see {@link TurnConfigurationSnapshot}).
|
|
207
|
+
*/
|
|
208
|
+
setTurnSnapshotSource(source) {
|
|
209
|
+
this.turnSnapshotSource = source;
|
|
210
|
+
}
|
|
205
211
|
/**
|
|
206
212
|
* Set agent options for configuration resolution
|
|
207
213
|
*/
|
|
208
214
|
setOptions(options) {
|
|
209
215
|
this.options = options;
|
|
210
216
|
}
|
|
217
|
+
/** Configuration the current turn reads from (live configuration outside a turn). */
|
|
218
|
+
get liveConfiguration() {
|
|
219
|
+
const snapshot = this.turnSnapshotSource?.();
|
|
220
|
+
return snapshot ? snapshot.configuration : this.currentConfiguration;
|
|
221
|
+
}
|
|
222
|
+
/** Environment snapshot the current turn reads from. */
|
|
223
|
+
get liveEnvSnapshot() {
|
|
224
|
+
const snapshot = this.turnSnapshotSource?.();
|
|
225
|
+
return snapshot ? snapshot.env : this.envSnapshot;
|
|
226
|
+
}
|
|
211
227
|
/**
|
|
212
228
|
* Returns a copy of the per-session environment snapshot (settings.json `env`).
|
|
213
229
|
* Priority over OS env; does NOT include OS env. For subprocess spawning use
|
|
214
230
|
* {@link getMergedEnv} instead.
|
|
215
231
|
*/
|
|
216
232
|
getEnvSnapshot() {
|
|
217
|
-
return { ...this.
|
|
233
|
+
return { ...this.liveEnvSnapshot };
|
|
218
234
|
}
|
|
219
235
|
/**
|
|
220
236
|
* Returns OS env merged with the session snapshot (snapshot wins). Use this
|
|
@@ -222,7 +238,7 @@ export class ConfigurationService {
|
|
|
222
238
|
* so they inherit both OS env and the session's settings env.
|
|
223
239
|
*/
|
|
224
240
|
getMergedEnv() {
|
|
225
|
-
return Object.fromEntries(Object.entries({ ...process.env, ...this.
|
|
241
|
+
return Object.fromEntries(Object.entries({ ...process.env, ...this.liveEnvSnapshot }).filter(([, v]) => v !== undefined));
|
|
226
242
|
}
|
|
227
243
|
// Core loading operations
|
|
228
244
|
/**
|
|
@@ -420,7 +436,7 @@ export class ConfigurationService {
|
|
|
420
436
|
}
|
|
421
437
|
else {
|
|
422
438
|
resolvedApiKey =
|
|
423
|
-
this.
|
|
439
|
+
this.liveEnvSnapshot.WAVE_API_KEY ?? process.env.WAVE_API_KEY;
|
|
424
440
|
}
|
|
425
441
|
// Resolve base URL: override > options > env (settings.json) > process.env
|
|
426
442
|
// Note: Explicitly provided empty strings should be treated as invalid, not fall back to env
|
|
@@ -433,23 +449,23 @@ export class ConfigurationService {
|
|
|
433
449
|
}
|
|
434
450
|
else {
|
|
435
451
|
resolvedBaseURL =
|
|
436
|
-
this.
|
|
452
|
+
this.liveEnvSnapshot.WAVE_BASE_URL ?? process.env.WAVE_BASE_URL;
|
|
437
453
|
}
|
|
438
454
|
// Fallback to process.env if still not resolved (for dynamic updates in tests)
|
|
439
455
|
if (resolvedApiKey === undefined) {
|
|
440
456
|
resolvedApiKey =
|
|
441
|
-
this.
|
|
457
|
+
this.liveEnvSnapshot.WAVE_API_KEY ?? process.env.WAVE_API_KEY;
|
|
442
458
|
}
|
|
443
459
|
if (!resolvedBaseURL) {
|
|
444
460
|
resolvedBaseURL =
|
|
445
|
-
this.
|
|
461
|
+
this.liveEnvSnapshot.WAVE_BASE_URL ?? process.env.WAVE_BASE_URL;
|
|
446
462
|
}
|
|
447
463
|
// Treat empty string as not provided
|
|
448
464
|
if (resolvedBaseURL?.trim() === "") {
|
|
449
465
|
resolvedBaseURL = undefined;
|
|
450
466
|
}
|
|
451
467
|
// Resolve custom headers from environment: env (settings.json) > process.env
|
|
452
|
-
const envCustomHeaders = this.
|
|
468
|
+
const envCustomHeaders = this.liveEnvSnapshot.WAVE_CUSTOM_HEADERS ??
|
|
453
469
|
process.env.WAVE_CUSTOM_HEADERS ??
|
|
454
470
|
"";
|
|
455
471
|
const parsedEnvHeaders = parseCustomHeaders(envCustomHeaders);
|
|
@@ -482,16 +498,16 @@ export class ConfigurationService {
|
|
|
482
498
|
// If admin wants hard enforcement, they set the `model` scalar field (overwrites local in mergeRemoteSettings).
|
|
483
499
|
const resolvedAgentModel = model ||
|
|
484
500
|
this.options.model ||
|
|
485
|
-
this.
|
|
486
|
-
(this.
|
|
501
|
+
this.liveConfiguration?.model ||
|
|
502
|
+
(this.liveEnvSnapshot.WAVE_MODEL ?? process.env.WAVE_MODEL);
|
|
487
503
|
// Resolve fast model: override > options > process.env (includes settings.json env)
|
|
488
504
|
const resolvedFastModel = fastModel ||
|
|
489
505
|
this.options.fastModel ||
|
|
490
|
-
(this.
|
|
506
|
+
(this.liveEnvSnapshot.WAVE_FAST_MODEL ?? process.env.WAVE_FAST_MODEL);
|
|
491
507
|
// Resolve vision model: override > options > process.env (includes settings.json env)
|
|
492
508
|
const resolvedVisionModel = visionModel ||
|
|
493
509
|
this.options.visionModel ||
|
|
494
|
-
(this.
|
|
510
|
+
(this.liveEnvSnapshot.WAVE_VISION_MODEL ?? process.env.WAVE_VISION_MODEL);
|
|
495
511
|
const baseConfig = {
|
|
496
512
|
model: resolvedAgentModel,
|
|
497
513
|
fastModel: resolvedFastModel,
|
|
@@ -500,8 +516,7 @@ export class ConfigurationService {
|
|
|
500
516
|
};
|
|
501
517
|
// Resolve fast model generation params from models[fastModel].options.
|
|
502
518
|
// Set on baseConfig before the modelSpecificConfig spread so it isn't overwritten.
|
|
503
|
-
const fastModelSource = resolvedFastModel &&
|
|
504
|
-
this.currentConfiguration?.models?.[resolvedFastModel];
|
|
519
|
+
const fastModelSource = resolvedFastModel && this.liveConfiguration?.models?.[resolvedFastModel];
|
|
505
520
|
if (fastModelSource && fastModelSource.options) {
|
|
506
521
|
baseConfig.fastModelOptions = fastModelSource.options;
|
|
507
522
|
}
|
|
@@ -514,7 +529,7 @@ export class ConfigurationService {
|
|
|
514
529
|
}
|
|
515
530
|
// Merge model-specific settings from configuration
|
|
516
531
|
const modelSpecificConfig = resolvedAgentModel &&
|
|
517
|
-
this.
|
|
532
|
+
this.liveConfiguration?.models?.[resolvedAgentModel];
|
|
518
533
|
if (modelSpecificConfig) {
|
|
519
534
|
const resolved = {
|
|
520
535
|
...baseConfig,
|
|
@@ -549,14 +564,14 @@ export class ConfigurationService {
|
|
|
549
564
|
// same chain as resolveModelConfig: caller > options > currentConfiguration > env.
|
|
550
565
|
const resolvedModel = model ||
|
|
551
566
|
this.options.model ||
|
|
552
|
-
this.
|
|
553
|
-
(this.
|
|
554
|
-
const modelConfig = resolvedModel && this.
|
|
567
|
+
this.liveConfiguration?.model ||
|
|
568
|
+
(this.liveEnvSnapshot.WAVE_MODEL ?? process.env.WAVE_MODEL);
|
|
569
|
+
const modelConfig = resolvedModel && this.liveConfiguration?.models?.[resolvedModel];
|
|
555
570
|
if (modelConfig && modelConfig.maxInputTokens !== undefined) {
|
|
556
571
|
return modelConfig.maxInputTokens;
|
|
557
572
|
}
|
|
558
573
|
// Try env (settings.json snapshot) first, then process.env
|
|
559
|
-
const envMaxInputTokens = this.
|
|
574
|
+
const envMaxInputTokens = this.liveEnvSnapshot.WAVE_MAX_INPUT_TOKENS ??
|
|
560
575
|
process.env.WAVE_MAX_INPUT_TOKENS;
|
|
561
576
|
if (envMaxInputTokens) {
|
|
562
577
|
const parsed = parseInt(envMaxInputTokens, 10);
|
|
@@ -569,9 +584,14 @@ export class ConfigurationService {
|
|
|
569
584
|
}
|
|
570
585
|
/**
|
|
571
586
|
* Resolves preferred language with fallbacks
|
|
572
|
-
* Resolution priority: override > options > settings.json >
|
|
587
|
+
* Resolution priority: override > options > settings.json > default (`zh-CN`)
|
|
588
|
+
*
|
|
589
|
+
* 末尾的默认值保证「未设置」时也有明确生效值,且与设置页下拉的默认项一致
|
|
590
|
+
* (spec agent-config 边界说明「语言默认值」)。调用方不得再假定
|
|
591
|
+
* 「undefined = 不注入语言指令」。
|
|
592
|
+
*
|
|
573
593
|
* @param constructorLanguage - Language override (optional)
|
|
574
|
-
* @returns Resolved language
|
|
594
|
+
* @returns Resolved language (never empty)
|
|
575
595
|
*/
|
|
576
596
|
resolveLanguage(constructorLanguage) {
|
|
577
597
|
// 1. Override (highest priority)
|
|
@@ -582,29 +602,46 @@ export class ConfigurationService {
|
|
|
582
602
|
if (this.options.language !== undefined) {
|
|
583
603
|
return this.options.language;
|
|
584
604
|
}
|
|
585
|
-
//
|
|
586
|
-
if (this.
|
|
587
|
-
return this.
|
|
605
|
+
// 3. settings.json (merged)
|
|
606
|
+
if (this.liveConfiguration?.language) {
|
|
607
|
+
return this.liveConfiguration.language;
|
|
588
608
|
}
|
|
589
|
-
|
|
609
|
+
// 4. Default
|
|
610
|
+
return DEFAULT_LANGUAGE;
|
|
590
611
|
}
|
|
591
612
|
/**
|
|
592
613
|
* Resolves auto-memory enabled state with fallbacks
|
|
593
|
-
* Resolution priority: settings.json > WAVE_DISABLE_AUTO_MEMORY > default (true)
|
|
614
|
+
* Resolution priority: session options (session-level override) > settings.json > WAVE_DISABLE_AUTO_MEMORY > default (true)
|
|
594
615
|
* @returns Resolved auto-memory enabled state
|
|
595
616
|
*/
|
|
596
617
|
resolveAutoMemoryEnabled() {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
618
|
+
return this.resolveAutoMemoryEnabledFrom(this.liveConfiguration, this.liveEnvSnapshot);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Same resolution chain but read from the live merged configuration, ignoring
|
|
622
|
+
* the turn snapshot. The permission safe zone is a live mechanism (like
|
|
623
|
+
* permission rules, applied by LiveConfigManager on reload), so its sync must
|
|
624
|
+
* not be pinned to the turn-start value.
|
|
625
|
+
*/
|
|
626
|
+
resolveAutoMemoryEnabledNow() {
|
|
627
|
+
return this.resolveAutoMemoryEnabledFrom(this.currentConfiguration, this.envSnapshot);
|
|
628
|
+
}
|
|
629
|
+
resolveAutoMemoryEnabledFrom(config, env) {
|
|
630
|
+
// 1. Per-session options override (session-level override semantics; user
|
|
631
|
+
// preferences are no longer passed this way — see core/agent-config.md)
|
|
632
|
+
if (this.options.autoMemoryEnabled !== undefined) {
|
|
633
|
+
return this.options.autoMemoryEnabled;
|
|
634
|
+
}
|
|
635
|
+
// 2. settings.json (merged)
|
|
636
|
+
if (config?.autoMemoryEnabled !== undefined) {
|
|
637
|
+
return config.autoMemoryEnabled;
|
|
600
638
|
}
|
|
601
|
-
//
|
|
602
|
-
const disableAutoMemory =
|
|
603
|
-
process.env.WAVE_DISABLE_AUTO_MEMORY;
|
|
639
|
+
// 3. WAVE_DISABLE_AUTO_MEMORY environment variable (settings snapshot > OS env)
|
|
640
|
+
const disableAutoMemory = env.WAVE_DISABLE_AUTO_MEMORY ?? process.env.WAVE_DISABLE_AUTO_MEMORY;
|
|
604
641
|
if (disableAutoMemory === "1" || disableAutoMemory === "true") {
|
|
605
642
|
return false;
|
|
606
643
|
}
|
|
607
|
-
//
|
|
644
|
+
// 4. Default (true)
|
|
608
645
|
return true;
|
|
609
646
|
}
|
|
610
647
|
/**
|
|
@@ -613,7 +650,7 @@ export class ConfigurationService {
|
|
|
613
650
|
* @returns Resolved worktree base ref
|
|
614
651
|
*/
|
|
615
652
|
resolveWorktreeBaseRef() {
|
|
616
|
-
const baseRef = this.
|
|
653
|
+
const baseRef = this.liveConfiguration?.worktree?.baseRef;
|
|
617
654
|
if (baseRef === "head") {
|
|
618
655
|
return "head";
|
|
619
656
|
}
|
|
@@ -621,16 +658,25 @@ export class ConfigurationService {
|
|
|
621
658
|
}
|
|
622
659
|
/**
|
|
623
660
|
* Resolves auto-memory extraction frequency with fallbacks
|
|
624
|
-
* Resolution priority: settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
|
|
661
|
+
* Resolution priority: session options (程序化会话级覆盖) > settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
|
|
625
662
|
* @returns Resolved auto-memory extraction frequency (turns)
|
|
626
663
|
*/
|
|
627
664
|
resolveAutoMemoryFrequency() {
|
|
628
|
-
// 1.
|
|
629
|
-
|
|
630
|
-
|
|
665
|
+
// 1. Per-session options override (Agent.create → setOptions 的程序化会话级
|
|
666
|
+
// 覆盖;三端设置页的值不走这里——它们落用户级 settings.json,由实时重载
|
|
667
|
+
// 在下一轮生效,见 spec core/agent-config.md「设置实时重载」)。
|
|
668
|
+
// Only positive values are honored so a malformed 0/negative never
|
|
669
|
+
// degenerates into per-turn extraction.
|
|
670
|
+
if (this.options.autoMemoryFrequency !== undefined &&
|
|
671
|
+
this.options.autoMemoryFrequency > 0) {
|
|
672
|
+
return this.options.autoMemoryFrequency;
|
|
673
|
+
}
|
|
674
|
+
// 2. settings.json (merged)
|
|
675
|
+
if (this.liveConfiguration?.autoMemoryFrequency !== undefined) {
|
|
676
|
+
return this.liveConfiguration.autoMemoryFrequency;
|
|
631
677
|
}
|
|
632
|
-
//
|
|
633
|
-
const envFrequency = this.
|
|
678
|
+
// 3. WAVE_AUTO_MEMORY_FREQUENCY environment variable (settings snapshot > OS env)
|
|
679
|
+
const envFrequency = this.liveEnvSnapshot.WAVE_AUTO_MEMORY_FREQUENCY ??
|
|
634
680
|
process.env.WAVE_AUTO_MEMORY_FREQUENCY;
|
|
635
681
|
if (envFrequency) {
|
|
636
682
|
const parsed = parseInt(envFrequency, 10);
|
|
@@ -638,7 +684,7 @@ export class ConfigurationService {
|
|
|
638
684
|
return parsed;
|
|
639
685
|
}
|
|
640
686
|
}
|
|
641
|
-
//
|
|
687
|
+
// 4. Default (1)
|
|
642
688
|
return 1;
|
|
643
689
|
}
|
|
644
690
|
/**
|
|
@@ -657,7 +703,7 @@ export class ConfigurationService {
|
|
|
657
703
|
return this.options.maxTokens;
|
|
658
704
|
}
|
|
659
705
|
// Try env (settings.json snapshot) first, then process.env
|
|
660
|
-
const envMaxOutputTokens = this.
|
|
706
|
+
const envMaxOutputTokens = this.liveEnvSnapshot.WAVE_MAX_OUTPUT_TOKENS ??
|
|
661
707
|
process.env.WAVE_MAX_OUTPUT_TOKENS;
|
|
662
708
|
if (envMaxOutputTokens) {
|
|
663
709
|
const parsed = parseInt(envMaxOutputTokens, 10);
|
|
@@ -669,11 +715,20 @@ export class ConfigurationService {
|
|
|
669
715
|
return DEFAULT_WAVE_MAX_OUTPUT_TOKENS;
|
|
670
716
|
}
|
|
671
717
|
/**
|
|
672
|
-
* Set the active model in the session
|
|
718
|
+
* Set the active model in the session.
|
|
719
|
+
*
|
|
720
|
+
* Persisting to `~/.wave/settings.json` is best-effort: the in-memory model
|
|
721
|
+
* applies immediately, and a failed write (e.g. a locked file / rename EPERM
|
|
722
|
+
* on Windows) is logged instead of escaping as an unhandled rejection.
|
|
673
723
|
*/
|
|
674
|
-
setModel(model) {
|
|
724
|
+
async setModel(model) {
|
|
675
725
|
this.options.model = model;
|
|
676
|
-
|
|
726
|
+
try {
|
|
727
|
+
await this.persistModelToSettings(model);
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
logger.warn(`Failed to persist model to settings: ${error}`);
|
|
731
|
+
}
|
|
677
732
|
}
|
|
678
733
|
async persistModelToSettings(model) {
|
|
679
734
|
const configPath = getUserConfigPaths()[0]; // ~/.wave/settings.json
|
|
@@ -701,17 +756,17 @@ export class ConfigurationService {
|
|
|
701
756
|
const models = new Set();
|
|
702
757
|
// Add current model from options or environment (settings snapshot > OS env)
|
|
703
758
|
const currentModel = this.options.model ||
|
|
704
|
-
(this.
|
|
759
|
+
(this.liveEnvSnapshot.WAVE_MODEL ?? process.env.WAVE_MODEL);
|
|
705
760
|
if (currentModel) {
|
|
706
761
|
models.add(currentModel);
|
|
707
762
|
}
|
|
708
763
|
// Persisted model from settings (includes remote-merged)
|
|
709
|
-
if (this.
|
|
710
|
-
models.add(this.
|
|
764
|
+
if (this.liveConfiguration?.model) {
|
|
765
|
+
models.add(this.liveConfiguration.model);
|
|
711
766
|
}
|
|
712
767
|
// Add models from merged configuration
|
|
713
|
-
if (this.
|
|
714
|
-
Object.keys(this.
|
|
768
|
+
if (this.liveConfiguration?.models) {
|
|
769
|
+
Object.keys(this.liveConfiguration.models).forEach((model) => {
|
|
715
770
|
models.add(model);
|
|
716
771
|
});
|
|
717
772
|
}
|
|
@@ -721,7 +776,7 @@ export class ConfigurationService {
|
|
|
721
776
|
* Get the telemetry config from the loaded settings.
|
|
722
777
|
*/
|
|
723
778
|
resolveTelemetryConfig() {
|
|
724
|
-
return this.
|
|
779
|
+
return this.liveConfiguration?.monitoring?.telemetry;
|
|
725
780
|
}
|
|
726
781
|
/**
|
|
727
782
|
* Resolve all configuration file paths
|
|
@@ -841,11 +896,103 @@ export class ConfigurationService {
|
|
|
841
896
|
await atomicWriteFile(configPath, JSON.stringify(config, null, 2));
|
|
842
897
|
}
|
|
843
898
|
/**
|
|
844
|
-
*
|
|
899
|
+
* Resolve the settings.json path for a hook scope.
|
|
900
|
+
* user → ~/.wave/settings.json; project → <workdir>/.wave/settings.json
|
|
901
|
+
*/
|
|
902
|
+
resolveHookConfigPath(workdir, scope) {
|
|
903
|
+
const configPath = scope === "user"
|
|
904
|
+
? getUserConfigPaths()[0]
|
|
905
|
+
: getProjectConfigPaths(workdir)[1];
|
|
906
|
+
return { configPath, configDir: path.dirname(configPath) };
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Absolute path of the settings.json that holds hooks for `scope`
|
|
910
|
+
* (user → ~/.wave/settings.json, project → <workdir>/.wave/settings.json).
|
|
911
|
+
* Hosts surface it to the GUI so editors / file panels can open the file —
|
|
912
|
+
* they cannot expand a `~` prefix, so the path must already be absolute.
|
|
913
|
+
*/
|
|
914
|
+
getHookConfigPath(workdir, scope) {
|
|
915
|
+
return this.resolveHookConfigPath(workdir, scope).configPath;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Get hook configurations at a specific scope (user or project settings.json)
|
|
919
|
+
*/
|
|
920
|
+
async getHooksByScope(workdir, scope) {
|
|
921
|
+
if (scope !== "user" && !existsSync(workdir)) {
|
|
922
|
+
return {};
|
|
923
|
+
}
|
|
924
|
+
const { configPath } = this.resolveHookConfigPath(workdir, scope);
|
|
925
|
+
if (!existsSync(configPath)) {
|
|
926
|
+
return {};
|
|
927
|
+
}
|
|
928
|
+
try {
|
|
929
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
930
|
+
const config = JSON.parse(content);
|
|
931
|
+
return config.hooks || {};
|
|
932
|
+
}
|
|
933
|
+
catch {
|
|
934
|
+
return {};
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Delete a hook at a specific scope, identified by hookName of the form
|
|
939
|
+
* `Event:Matcher`. Removes the matching entry from the scope's settings.json
|
|
940
|
+
* hooks block.
|
|
941
|
+
*/
|
|
942
|
+
async deleteHook(workdir, scope, hookName) {
|
|
943
|
+
if (scope !== "user" && !existsSync(workdir)) {
|
|
944
|
+
throw new Error(`Working directory does not exist: ${workdir}`);
|
|
945
|
+
}
|
|
946
|
+
const { configPath, configDir } = this.resolveHookConfigPath(workdir, scope);
|
|
947
|
+
if (!existsSync(configDir)) {
|
|
948
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
949
|
+
}
|
|
950
|
+
let config = {};
|
|
951
|
+
if (existsSync(configPath)) {
|
|
952
|
+
try {
|
|
953
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
954
|
+
config = JSON.parse(content);
|
|
955
|
+
}
|
|
956
|
+
catch {
|
|
957
|
+
// Start with empty config if file is corrupted
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
const hooks = config.hooks || {};
|
|
961
|
+
const { event, matcher } = parseHookName(hookName);
|
|
962
|
+
if (!isValidHookEvent(event)) {
|
|
963
|
+
throw new Error(`Invalid hook event: ${event}`);
|
|
964
|
+
}
|
|
965
|
+
const eventConfigs = hooks[event];
|
|
966
|
+
if (eventConfigs) {
|
|
967
|
+
const remaining = eventConfigs.filter((cfg) => (cfg.matcher || "") !== (matcher || ""));
|
|
968
|
+
if (remaining.length > 0) {
|
|
969
|
+
hooks[event] = remaining;
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
delete hooks[event];
|
|
973
|
+
}
|
|
974
|
+
config.hooks = hooks;
|
|
975
|
+
await atomicWriteFile(configPath, JSON.stringify(config, null, 2));
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Get merged marketplaces from all scopes, with admin-managed (remote) entries
|
|
980
|
+
* taking precedence per key (spec enterprise server-managed-config「托管配置
|
|
981
|
+
* 下发插件市场与启用列表」场景 5).
|
|
845
982
|
*/
|
|
846
983
|
getMergedMarketplaces(workdir) {
|
|
847
984
|
const mergedConfig = loadMergedWaveConfig(workdir);
|
|
848
|
-
return
|
|
985
|
+
return {
|
|
986
|
+
...(mergedConfig?.marketplaces || {}),
|
|
987
|
+
...(this.getManagedMarketplaces() || {}),
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Marketplaces declared by the remote (managed) settings. `null` means the
|
|
992
|
+
* managed layer declares none, leaving the local configuration chain in charge.
|
|
993
|
+
*/
|
|
994
|
+
getManagedMarketplaces() {
|
|
995
|
+
return getRemoteSettingsSync()?.marketplaces ?? null;
|
|
849
996
|
}
|
|
850
997
|
/**
|
|
851
998
|
* Get marketplaces at a specific scope
|
|
@@ -966,11 +1113,25 @@ export class ConfigurationService {
|
|
|
966
1113
|
}
|
|
967
1114
|
}
|
|
968
1115
|
/**
|
|
969
|
-
* Get merged enabled plugins from all scopes
|
|
1116
|
+
* Get merged enabled plugins from all scopes, with admin-managed (remote)
|
|
1117
|
+
* entries taking precedence per key (spec enterprise server-managed-config
|
|
1118
|
+
* 「托管配置下发插件市场与启用列表」场景 5). A managed `false` therefore
|
|
1119
|
+
* force-disables a plugin no matter what the local files say.
|
|
970
1120
|
*/
|
|
971
1121
|
getMergedEnabledPlugins(workdir) {
|
|
972
1122
|
const mergedConfig = loadMergedWaveConfig(workdir);
|
|
973
|
-
return
|
|
1123
|
+
return {
|
|
1124
|
+
...(mergedConfig?.enabledPlugins || {}),
|
|
1125
|
+
...(this.getManagedEnabledPlugins() || {}),
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Enabled-plugin entries declared by the remote (managed) settings. `null`
|
|
1130
|
+
* means the managed layer declares none. Doubles as the authoritative
|
|
1131
|
+
* "is this plugin managed by the organization" lookup (spec plugin A-024).
|
|
1132
|
+
*/
|
|
1133
|
+
getManagedEnabledPlugins() {
|
|
1134
|
+
return getRemoteSettingsSync()?.enabledPlugins ?? null;
|
|
974
1135
|
}
|
|
975
1136
|
/**
|
|
976
1137
|
* Load Wave configuration from a JSON file
|
|
@@ -1100,6 +1261,7 @@ export function loadWaveConfigFromFile(filePath) {
|
|
|
1100
1261
|
marketplaces: config.marketplaces || undefined,
|
|
1101
1262
|
worktree: config.worktree || undefined,
|
|
1102
1263
|
enableArtifact: config.enableArtifact !== undefined ? config.enableArtifact : undefined,
|
|
1264
|
+
enableExec: config.enableExec !== undefined ? config.enableExec : undefined,
|
|
1103
1265
|
};
|
|
1104
1266
|
}
|
|
1105
1267
|
catch (error) {
|
|
@@ -1195,10 +1357,9 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1195
1357
|
]),
|
|
1196
1358
|
];
|
|
1197
1359
|
}
|
|
1198
|
-
// Merge
|
|
1199
|
-
if (config.permissions.
|
|
1200
|
-
mergedConfig.permissions.
|
|
1201
|
-
config.permissions.permissionMode;
|
|
1360
|
+
// Merge defaultMode (last one wins)
|
|
1361
|
+
if (config.permissions.defaultMode !== undefined) {
|
|
1362
|
+
mergedConfig.permissions.defaultMode = config.permissions.defaultMode;
|
|
1202
1363
|
}
|
|
1203
1364
|
// Merge additionalDirectories
|
|
1204
1365
|
if (config.permissions.additionalDirectories) {
|
|
@@ -1252,6 +1413,10 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1252
1413
|
if (config.enableArtifact !== undefined) {
|
|
1253
1414
|
mergedConfig.enableArtifact = config.enableArtifact;
|
|
1254
1415
|
}
|
|
1416
|
+
// Merge enableExec (last one wins)
|
|
1417
|
+
if (config.enableExec !== undefined) {
|
|
1418
|
+
mergedConfig.enableExec = config.enableExec;
|
|
1419
|
+
}
|
|
1255
1420
|
// Merge models
|
|
1256
1421
|
if (config.models) {
|
|
1257
1422
|
if (!mergedConfig.models)
|
|
@@ -1282,6 +1447,7 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1282
1447
|
language: mergedConfig.language,
|
|
1283
1448
|
model: mergedConfig.model,
|
|
1284
1449
|
autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
|
|
1450
|
+
autoMemoryFrequency: mergedConfig.autoMemoryFrequency,
|
|
1285
1451
|
cleanupPeriodDays: mergedConfig.cleanupPeriodDays,
|
|
1286
1452
|
marketplaces: mergedConfig.marketplaces &&
|
|
1287
1453
|
Object.keys(mergedConfig.marketplaces).length > 0
|
|
@@ -1292,5 +1458,21 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1292
1458
|
: undefined,
|
|
1293
1459
|
worktree: mergedConfig.worktree,
|
|
1294
1460
|
enableArtifact: mergedConfig.enableArtifact,
|
|
1461
|
+
enableExec: mergedConfig.enableExec,
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Parse a hookName of the form `Event:Matcher` (e.g. `PreToolUse:Write`)
|
|
1466
|
+
* into its event and matcher parts. Hooks without a matcher use the event
|
|
1467
|
+
* name alone (e.g. `SessionStart`).
|
|
1468
|
+
*/
|
|
1469
|
+
export function parseHookName(hookName) {
|
|
1470
|
+
const separator = hookName.indexOf(":");
|
|
1471
|
+
if (separator === -1) {
|
|
1472
|
+
return { event: hookName, matcher: "" };
|
|
1473
|
+
}
|
|
1474
|
+
return {
|
|
1475
|
+
event: hookName.slice(0, separator),
|
|
1476
|
+
matcher: hookName.slice(separator + 1),
|
|
1295
1477
|
};
|
|
1296
1478
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fast-model content processing.
|
|
3
|
+
*
|
|
4
|
+
* WebFetch runs every fetched page through a small, fast model, and the
|
|
5
|
+
* reader-view artifact read summarizes someone else's page the same way.
|
|
6
|
+
* Both go through this single implementation (prompt → answer).
|
|
7
|
+
*/
|
|
8
|
+
import type { ToolContext, ToolResult } from "../tools/types.js";
|
|
9
|
+
/** Human-readable byte size (e.g. "512B", "2.0KB", "1.5MB"). */
|
|
10
|
+
export declare function formatSize(bytes: number): string;
|
|
11
|
+
/**
|
|
12
|
+
* Run the fast model over `content` with `prompt` and return its answer as the
|
|
13
|
+
* tool result. The main model only ever sees this answer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function processContentWithAI(url: string, prompt: string, content: string, statusCode: number, statusText: string, context: ToolContext, contentSize?: number): Promise<ToolResult>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fast-model content processing.
|
|
3
|
+
*
|
|
4
|
+
* WebFetch runs every fetched page through a small, fast model, and the
|
|
5
|
+
* reader-view artifact read summarizes someone else's page the same way.
|
|
6
|
+
* Both go through this single implementation (prompt → answer).
|
|
7
|
+
*/
|
|
8
|
+
/** Human-readable byte size (e.g. "512B", "2.0KB", "1.5MB"). */
|
|
9
|
+
export function formatSize(bytes) {
|
|
10
|
+
if (bytes < 1024)
|
|
11
|
+
return `${bytes}B`;
|
|
12
|
+
if (bytes < 1024 * 1024)
|
|
13
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
14
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run the fast model over `content` with `prompt` and return its answer as the
|
|
18
|
+
* tool result. The main model only ever sees this answer.
|
|
19
|
+
*/
|
|
20
|
+
export async function processContentWithAI(url, prompt, content, statusCode, statusText, context, contentSize) {
|
|
21
|
+
if (!context.aiManager || !context.aiService) {
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
content: content,
|
|
25
|
+
error: "AI Manager or AI Service not available for processing content",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const modelConfig = context.aiManager.getModelConfig();
|
|
29
|
+
const fastModel = modelConfig.fastModel;
|
|
30
|
+
const aiResponse = await context.aiService.processWebContent({
|
|
31
|
+
gatewayConfig: context.aiManager.getGatewayConfig(),
|
|
32
|
+
modelConfig: modelConfig,
|
|
33
|
+
content: content,
|
|
34
|
+
prompt: prompt,
|
|
35
|
+
model: fastModel,
|
|
36
|
+
abortSignal: context.abortSignal,
|
|
37
|
+
});
|
|
38
|
+
const sizeStr = contentSize !== undefined ? formatSize(contentSize) : "unknown size";
|
|
39
|
+
const statusStr = `${statusCode} ${statusText}`.trim();
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
content: aiResponse.content || "",
|
|
43
|
+
shortResult: `Received ${sizeStr} (${statusStr}) from ${url}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Code default for Exec availability. */
|
|
2
|
+
export declare const EXEC_DEFAULT_ENABLED = true;
|
|
3
|
+
/**
|
|
4
|
+
* Whether the Exec tool may be registered for the given workdir.
|
|
5
|
+
* Resolution order: remote managed settings (`enableExec` from
|
|
6
|
+
* `GET /api/wave/settings`, admin override) → explicit `enableExec` in local
|
|
7
|
+
* merged settings → code default.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isExecEnabled(workdir?: string): boolean;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exec feature availability.
|
|
3
|
+
*
|
|
4
|
+
* Exec collapses the MCP tool pool into a single scriptable tool, which changes
|
|
5
|
+
* what the model sees in `tools[]`. It is on by default (the collapse applies
|
|
6
|
+
* whenever the pool is non-empty — there is no minimum tool count);
|
|
7
|
+
* `enableExec: false` is the escape hatch for a session that wants the flat MCP
|
|
8
|
+
* declarations back without downgrading.
|
|
9
|
+
*/
|
|
10
|
+
import { loadMergedWaveConfig } from "./configurationService.js";
|
|
11
|
+
import { getRemoteSettingsSync } from "./remoteSettingsService.js";
|
|
12
|
+
/** Code default for Exec availability. */
|
|
13
|
+
export const EXEC_DEFAULT_ENABLED = true;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the Exec tool may be registered for the given workdir.
|
|
16
|
+
* Resolution order: remote managed settings (`enableExec` from
|
|
17
|
+
* `GET /api/wave/settings`, admin override) → explicit `enableExec` in local
|
|
18
|
+
* merged settings → code default.
|
|
19
|
+
*/
|
|
20
|
+
export function isExecEnabled(workdir) {
|
|
21
|
+
const remote = getRemoteSettingsSync();
|
|
22
|
+
if (remote?.enableExec !== undefined) {
|
|
23
|
+
return remote.enableExec;
|
|
24
|
+
}
|
|
25
|
+
if (workdir) {
|
|
26
|
+
const config = loadMergedWaveConfig(workdir);
|
|
27
|
+
if (config?.enableExec !== undefined) {
|
|
28
|
+
return config.enableExec;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return EXEC_DEFAULT_ENABLED;
|
|
32
|
+
}
|