@vybestack/llxprt-code 0.4.8 → 0.5.0-nightly.251102.6bb3db7a
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/package.json +5 -3
- package/dist/src/auth/__tests__/oauthManager.safety.test.d.ts +6 -0
- package/dist/src/auth/__tests__/oauthManager.safety.test.js +49 -0
- package/dist/src/auth/__tests__/oauthManager.safety.test.js.map +1 -0
- package/dist/src/auth/oauth-manager.d.ts +11 -0
- package/dist/src/auth/oauth-manager.js +62 -29
- package/dist/src/auth/oauth-manager.js.map +1 -1
- package/dist/src/auth/oauth-manager.spec.js +7 -2
- package/dist/src/auth/oauth-manager.spec.js.map +1 -1
- package/dist/src/config/__tests__/nonInteractiveTools.test.d.ts +6 -0
- package/dist/src/config/__tests__/nonInteractiveTools.test.js +13 -0
- package/dist/src/config/__tests__/nonInteractiveTools.test.js.map +1 -0
- package/dist/src/config/__tests__/profileBootstrap.test.d.ts +6 -0
- package/dist/src/config/__tests__/profileBootstrap.test.js +91 -0
- package/dist/src/config/__tests__/profileBootstrap.test.js.map +1 -0
- package/dist/src/config/config.d.ts +6 -2
- package/dist/src/config/config.js +219 -18
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/profileBootstrap.d.ts +64 -0
- package/dist/src/config/profileBootstrap.js +140 -0
- package/dist/src/config/profileBootstrap.js.map +1 -0
- package/dist/src/gemini.js +68 -23
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +1 -2
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/integration-tests/base-url-behavior.integration.test.js +110 -450
- package/dist/src/integration-tests/base-url-behavior.integration.test.js.map +1 -1
- package/dist/src/integration-tests/model-params-isolation.integration.test.js +101 -539
- package/dist/src/integration-tests/model-params-isolation.integration.test.js.map +1 -1
- package/dist/src/integration-tests/modelParams.integration.test.js +86 -761
- package/dist/src/integration-tests/modelParams.integration.test.js.map +1 -1
- package/dist/src/integration-tests/provider-multi-runtime.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/provider-multi-runtime.integration.test.js +198 -0
- package/dist/src/integration-tests/provider-multi-runtime.integration.test.js.map +1 -0
- package/dist/src/integration-tests/provider-switching.integration.test.js +97 -151
- package/dist/src/integration-tests/provider-switching.integration.test.js.map +1 -1
- package/dist/src/integration-tests/runtime-isolation.test.d.ts +13 -0
- package/dist/src/integration-tests/runtime-isolation.test.js +170 -0
- package/dist/src/integration-tests/runtime-isolation.test.js.map +1 -0
- package/dist/src/integration-tests/test-utils.js +19 -2
- package/dist/src/integration-tests/test-utils.js.map +1 -1
- package/dist/src/integration-tests/test-utils.test.js +9 -8
- package/dist/src/integration-tests/test-utils.test.js.map +1 -1
- package/dist/src/integration-tests/todo-continuation.integration.test.js +5 -2
- package/dist/src/integration-tests/todo-continuation.integration.test.js.map +1 -1
- package/dist/src/integration-tests/tools-governance.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/tools-governance.integration.test.js +98 -0
- package/dist/src/integration-tests/tools-governance.integration.test.js.map +1 -0
- package/dist/src/nonInteractiveCli.js +36 -11
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/providers/logging/git-stats.test.js +11 -1
- package/dist/src/providers/logging/git-stats.test.js.map +1 -1
- package/dist/src/providers/logging/multi-provider-logging.integration.test.js +1 -2
- package/dist/src/providers/logging/multi-provider-logging.integration.test.js.map +1 -1
- package/dist/src/providers/logging/performance.test.js +1 -1
- package/dist/src/providers/logging/performance.test.js.map +1 -1
- package/dist/src/providers/oauth-provider-registration.d.ts +2 -2
- package/dist/src/providers/oauth-provider-registration.js +25 -9
- package/dist/src/providers/oauth-provider-registration.js.map +1 -1
- package/dist/src/providers/provider-gemini-switching.test.js +67 -89
- package/dist/src/providers/provider-gemini-switching.test.js.map +1 -1
- package/dist/src/providers/provider-switching.integration.test.js +42 -98
- package/dist/src/providers/provider-switching.integration.test.js.map +1 -1
- package/dist/src/providers/providerConfigUtils.d.ts +12 -7
- package/dist/src/providers/providerConfigUtils.js +31 -99
- package/dist/src/providers/providerConfigUtils.js.map +1 -1
- package/dist/src/providers/providerManagerInstance.d.ts +17 -1
- package/dist/src/providers/providerManagerInstance.js +157 -175
- package/dist/src/providers/providerManagerInstance.js.map +1 -1
- package/dist/src/providers/providerManagerInstance.oauthRegistration.test.js +19 -15
- package/dist/src/providers/providerManagerInstance.oauthRegistration.test.js.map +1 -1
- package/dist/src/providers/providerManagerInstance.test.js +2 -5
- package/dist/src/providers/providerManagerInstance.test.js.map +1 -1
- package/dist/src/runtime/__tests__/profileApplication.test.d.ts +5 -0
- package/dist/src/runtime/__tests__/profileApplication.test.js +232 -0
- package/dist/src/runtime/__tests__/profileApplication.test.js.map +1 -0
- package/dist/src/runtime/__tests__/runtimeIsolation.test.d.ts +5 -0
- package/dist/src/runtime/__tests__/runtimeIsolation.test.js +376 -0
- package/dist/src/runtime/__tests__/runtimeIsolation.test.js.map +1 -0
- package/dist/src/runtime/agentRuntimeAdapter.d.ts +249 -0
- package/dist/src/runtime/agentRuntimeAdapter.js +506 -0
- package/dist/src/runtime/agentRuntimeAdapter.js.map +1 -0
- package/dist/src/runtime/agentRuntimeAdapter.spec.d.ts +6 -0
- package/dist/src/runtime/agentRuntimeAdapter.spec.js +866 -0
- package/dist/src/runtime/agentRuntimeAdapter.spec.js.map +1 -0
- package/dist/src/runtime/messages.d.ts +28 -0
- package/dist/src/runtime/messages.js +64 -0
- package/dist/src/runtime/messages.js.map +1 -0
- package/dist/src/runtime/profileApplication.d.ts +33 -0
- package/dist/src/runtime/profileApplication.js +191 -0
- package/dist/src/runtime/profileApplication.js.map +1 -0
- package/dist/src/runtime/providerConfigUtils.test.d.ts +1 -0
- package/dist/src/runtime/providerConfigUtils.test.js +68 -0
- package/dist/src/runtime/providerConfigUtils.test.js.map +1 -0
- package/dist/src/runtime/runtimeContextFactory.d.ts +102 -0
- package/dist/src/runtime/runtimeContextFactory.js +190 -0
- package/dist/src/runtime/runtimeContextFactory.js.map +1 -0
- package/dist/src/runtime/runtimeSettings.d.ts +217 -0
- package/dist/src/runtime/runtimeSettings.js +1094 -0
- package/dist/src/runtime/runtimeSettings.js.map +1 -0
- package/dist/src/runtime/runtimeSettings.test.d.ts +1 -0
- package/dist/src/runtime/runtimeSettings.test.js +320 -0
- package/dist/src/runtime/runtimeSettings.test.js.map +1 -0
- package/dist/src/services/BuiltinCommandLoader.d.ts +13 -4
- package/dist/src/services/BuiltinCommandLoader.js +17 -4
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/McpPromptLoader.js +34 -13
- package/dist/src/services/McpPromptLoader.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +5 -2
- package/dist/src/test-utils/mockCommandContext.js.map +1 -1
- package/dist/src/ui/App.js +29 -49
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/commands/aboutCommand.js +59 -38
- package/dist/src/ui/commands/aboutCommand.js.map +1 -1
- package/dist/src/ui/commands/authCommand.js +7 -9
- package/dist/src/ui/commands/authCommand.js.map +1 -1
- package/dist/src/ui/commands/baseurlCommand.js +8 -44
- package/dist/src/ui/commands/baseurlCommand.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.js +28 -12
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/diagnosticsCommand.d.ts +0 -3
- package/dist/src/ui/commands/diagnosticsCommand.js +45 -191
- package/dist/src/ui/commands/diagnosticsCommand.js.map +1 -1
- package/dist/src/ui/commands/keyCommand.js +9 -58
- package/dist/src/ui/commands/keyCommand.js.map +1 -1
- package/dist/src/ui/commands/keyCommand.test.js +48 -102
- package/dist/src/ui/commands/keyCommand.test.js.map +1 -1
- package/dist/src/ui/commands/keyfileCommand.js +42 -93
- package/dist/src/ui/commands/keyfileCommand.js.map +1 -1
- package/dist/src/ui/commands/logoutCommand.js +2 -2
- package/dist/src/ui/commands/logoutCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +29 -7
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/modelCommand.js +8 -59
- package/dist/src/ui/commands/modelCommand.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.js +151 -267
- package/dist/src/ui/commands/profileCommand.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.test.js +88 -344
- package/dist/src/ui/commands/profileCommand.test.js.map +1 -1
- package/dist/src/ui/commands/providerCommand.js +9 -3
- package/dist/src/ui/commands/providerCommand.js.map +1 -1
- package/dist/src/ui/commands/restoreCommand.js +38 -18
- package/dist/src/ui/commands/restoreCommand.js.map +1 -1
- package/dist/src/ui/commands/schema/argumentResolver.test.d.ts +6 -0
- package/dist/src/ui/commands/schema/argumentResolver.test.js +619 -0
- package/dist/src/ui/commands/schema/argumentResolver.test.js.map +1 -0
- package/dist/src/ui/commands/schema/index.d.ts +15 -0
- package/dist/src/ui/commands/schema/index.js +320 -0
- package/dist/src/ui/commands/schema/index.js.map +1 -0
- package/dist/src/ui/commands/schema/types.d.ts +61 -0
- package/dist/src/ui/commands/schema/types.js +12 -0
- package/dist/src/ui/commands/schema/types.js.map +1 -0
- package/dist/src/ui/commands/setCommand.js +641 -325
- package/dist/src/ui/commands/setCommand.js.map +1 -1
- package/dist/src/ui/commands/setCommand.test.js +92 -388
- package/dist/src/ui/commands/setCommand.test.js.map +1 -1
- package/dist/src/ui/commands/statusCommand.js +2 -2
- package/dist/src/ui/commands/statusCommand.js.map +1 -1
- package/dist/src/ui/commands/subagentCommand.d.ts +16 -0
- package/dist/src/ui/commands/subagentCommand.js +674 -0
- package/dist/src/ui/commands/subagentCommand.js.map +1 -0
- package/dist/src/ui/commands/test/setCommand.mutation.test.d.ts +6 -0
- package/dist/src/ui/commands/test/setCommand.mutation.test.js +132 -0
- package/dist/src/ui/commands/test/setCommand.mutation.test.js.map +1 -0
- package/dist/src/ui/commands/test/setCommand.phase09.test.d.ts +6 -0
- package/dist/src/ui/commands/test/setCommand.phase09.test.js +222 -0
- package/dist/src/ui/commands/test/setCommand.phase09.test.js.map +1 -0
- package/dist/src/ui/commands/test/subagentCommand.schema.test.d.ts +6 -0
- package/dist/src/ui/commands/test/subagentCommand.schema.test.js +125 -0
- package/dist/src/ui/commands/test/subagentCommand.schema.test.js.map +1 -0
- package/dist/src/ui/commands/test/subagentCommand.test.d.ts +1 -0
- package/dist/src/ui/commands/test/subagentCommand.test.js +598 -0
- package/dist/src/ui/commands/test/subagentCommand.test.js.map +1 -0
- package/dist/src/ui/commands/toolformatCommand.js +25 -98
- package/dist/src/ui/commands/toolformatCommand.js.map +1 -1
- package/dist/src/ui/commands/toolformatCommand.test.js +56 -102
- package/dist/src/ui/commands/toolformatCommand.test.js.map +1 -1
- package/dist/src/ui/commands/toolsCommand.js +187 -31
- package/dist/src/ui/commands/toolsCommand.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +11 -2
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.js +16 -55
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/components/Footer.js +4 -5
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +1 -1
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/StatsDisplay.js +6 -11
- package/dist/src/ui/components/StatsDisplay.js.map +1 -1
- package/dist/src/ui/components/SuggestionsDisplay.d.ts +13 -1
- package/dist/src/ui/components/SuggestionsDisplay.js +22 -3
- package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.d.ts +1 -0
- package/dist/src/ui/components/messages/ToolGroupMessage.js +14 -14
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.test.js +1 -0
- package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
- package/dist/src/ui/containers/SessionController.js +61 -117
- package/dist/src/ui/containers/SessionController.js.map +1 -1
- package/dist/src/ui/contexts/RuntimeContext.d.ts +61 -0
- package/dist/src/ui/contexts/RuntimeContext.js +118 -0
- package/dist/src/ui/contexts/RuntimeContext.js.map +1 -0
- package/dist/src/ui/contexts/TodoProvider.d.ts +1 -0
- package/dist/src/ui/contexts/TodoProvider.js +10 -8
- package/dist/src/ui/contexts/TodoProvider.js.map +1 -1
- package/dist/src/ui/contexts/ToolCallProvider.d.ts +1 -0
- package/dist/src/ui/contexts/ToolCallProvider.js +10 -9
- package/dist/src/ui/contexts/ToolCallProvider.js.map +1 -1
- package/dist/src/ui/hooks/__tests__/useSlashCompletion.set.phase09.test.d.ts +6 -0
- package/dist/src/ui/hooks/__tests__/useSlashCompletion.set.phase09.test.js +39 -0
- package/dist/src/ui/hooks/__tests__/useSlashCompletion.set.phase09.test.js.map +1 -0
- package/dist/src/ui/hooks/atCommandProcessor.js +11 -3
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +3 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js +4 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.test.js +1 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js +27 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAuthCommand.js +11 -3
- package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -0
- package/dist/src/ui/hooks/useCommandCompletion.js +2 -0
- package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +37 -11
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.subagent.spec.d.ts +6 -0
- package/dist/src/ui/hooks/useGeminiStream.subagent.spec.js +232 -0
- package/dist/src/ui/hooks/useGeminiStream.subagent.spec.js.map +1 -0
- package/dist/src/ui/hooks/useLoadProfileDialog.d.ts +1 -1
- package/dist/src/ui/hooks/useLoadProfileDialog.js +18 -57
- package/dist/src/ui/hooks/useLoadProfileDialog.js.map +1 -1
- package/dist/src/ui/hooks/useOpenAIProviderInfo.d.ts +1 -1
- package/dist/src/ui/hooks/useOpenAIProviderInfo.js +12 -7
- package/dist/src/ui/hooks/useOpenAIProviderInfo.js.map +1 -1
- package/dist/src/ui/hooks/useProviderDialog.d.ts +1 -1
- package/dist/src/ui/hooks/useProviderDialog.js +17 -90
- package/dist/src/ui/hooks/useProviderDialog.js.map +1 -1
- package/dist/src/ui/hooks/useProviderModelDialog.d.ts +2 -2
- package/dist/src/ui/hooks/useProviderModelDialog.js +11 -12
- package/dist/src/ui/hooks/useProviderModelDialog.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.d.ts +3 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +144 -34
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.d.ts +32 -0
- package/dist/src/ui/hooks/useSlashCompletion.js +154 -77
- package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +39 -14
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +108 -79
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/types.d.ts +1 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/utils/sandbox.js +7 -5
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +4 -2
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +30 -30
- package/dist/src/zed-integration/zedIntegration.js +112 -39
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +5 -3
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -4,326 +4,685 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { CommandKind, } from './types.js';
|
|
7
|
-
import {
|
|
8
|
-
import { parseModelParamValue } from '../../settings/modelParamParser.js';
|
|
7
|
+
import { getRuntimeApi } from '../contexts/RuntimeContext.js';
|
|
9
8
|
// Subcommand for /set unset - removes ephemeral settings or model parameters
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Implementation for the /set command that handles both:
|
|
11
|
+
* - /set modelparam <key> <value>
|
|
12
|
+
* - /set <ephemeral-key> <value>
|
|
13
|
+
*/
|
|
14
|
+
// Help text for ephemeral settings - these are session-only and saved only via profiles
|
|
15
|
+
const ephemeralSettingHelp = {
|
|
16
|
+
'context-limit': 'Maximum number of tokens for the context window (e.g., 100000)',
|
|
17
|
+
'compression-threshold': 'Fraction of context limit that triggers compression (0.0-1.0, e.g., 0.7 for 70%)',
|
|
18
|
+
'base-url': 'Base URL for API requests',
|
|
19
|
+
'tool-format': 'Tool format override for the provider',
|
|
20
|
+
'api-version': 'API version to use',
|
|
21
|
+
'custom-headers': 'Custom HTTP headers as JSON object',
|
|
22
|
+
'stream-options': 'Stream options for OpenAI API (default: { include_usage: true })',
|
|
23
|
+
streaming: 'Enable or disable streaming responses (enabled/disabled, default: enabled)',
|
|
24
|
+
'shell-replacement': 'Allow command substitution ($(), <(), backticks) in shell commands (default: false)',
|
|
25
|
+
// Socket configuration for local AI servers (LM Studio, Ollama, etc.)
|
|
26
|
+
'socket-timeout': 'Request timeout in milliseconds for local AI servers (default: 60000)',
|
|
27
|
+
'socket-keepalive': 'Enable TCP keepalive for local AI server connections (true/false, default: true)',
|
|
28
|
+
'socket-nodelay': 'Enable TCP_NODELAY concept for local AI servers (true/false, default: true)',
|
|
29
|
+
// Tool output limit settings - apply to all tools that can return large outputs
|
|
30
|
+
'tool-output-max-items': 'Maximum number of items/files/matches returned by tools (default: 50)',
|
|
31
|
+
'tool-output-max-tokens': 'Maximum tokens in tool output (default: 50000)',
|
|
32
|
+
'tool-output-truncate-mode': 'How to handle exceeding limits: warn, truncate, or sample (default: warn)',
|
|
33
|
+
'tool-output-item-size-limit': 'Maximum size per item/file in bytes (default: 524288 = 512KB)',
|
|
34
|
+
// Final catch-all to prevent context overflow
|
|
35
|
+
'max-prompt-tokens': 'Maximum tokens allowed in any prompt sent to LLM (default: 200000)',
|
|
36
|
+
// Emoji filter settings
|
|
37
|
+
emojifilter: 'Emoji filter mode (allowed, auto, warn, error)',
|
|
38
|
+
// Retry settings
|
|
39
|
+
retries: 'Maximum number of retry attempts for API calls (default: varies by provider)',
|
|
40
|
+
retrywait: 'Initial delay in milliseconds between retry attempts (default: varies by provider)',
|
|
41
|
+
// Loop prevention settings
|
|
42
|
+
maxTurnsPerPrompt: 'Maximum number of turns allowed per prompt before stopping (default: 100, -1 for unlimited)',
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Schema-based completion for /set command, redesigned to match P09 test expectations.
|
|
46
|
+
*
|
|
47
|
+
* @plan:PLAN-20251013-AUTOCOMPLETE.P10
|
|
48
|
+
* @requirement:REQ-006
|
|
49
|
+
* @pseudocode ArgumentSchema.md lines 111-130
|
|
50
|
+
* - Line 111: literal `unset`
|
|
51
|
+
* - Line 112: literal `modelparam`
|
|
52
|
+
* - Line 113: literal `emojifilter`
|
|
53
|
+
* - Line 114: nested value arg for param name
|
|
54
|
+
* - Line 115: nested value arg for param value
|
|
55
|
+
* - Line 116: hint for emoji mode
|
|
56
|
+
* - Line 117-120: dynamic completers for providers/params
|
|
57
|
+
*
|
|
58
|
+
* Note: The P09 test uses a mixed approach: literals for 'unset', 'modelparam', 'emojifilter'
|
|
59
|
+
* and a single top-level 'value' argument for other settings. This implementation matches that.
|
|
60
|
+
*/
|
|
61
|
+
const toTitleCase = (input) => input
|
|
62
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
63
|
+
.replace(/[-_]/g, ' ')
|
|
64
|
+
.replace(/\s+/g, ' ')
|
|
65
|
+
.trim()
|
|
66
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
67
|
+
const booleanOptions = [
|
|
68
|
+
{ value: 'true', description: 'true' },
|
|
69
|
+
{ value: 'false', description: 'false' },
|
|
70
|
+
];
|
|
71
|
+
const streamingOptions = [
|
|
72
|
+
{ value: 'enabled', description: 'enabled' },
|
|
73
|
+
{ value: 'disabled', description: 'disabled' },
|
|
74
|
+
];
|
|
75
|
+
const emojifilterOptions = [
|
|
76
|
+
{ value: 'allowed', description: 'Allow all emojis' },
|
|
77
|
+
{ value: 'auto', description: 'Automatically filter inappropriate emojis' },
|
|
78
|
+
{ value: 'warn', description: 'Warn about filtered emojis' },
|
|
79
|
+
{ value: 'error', description: 'Error on filtered emojis' },
|
|
80
|
+
];
|
|
81
|
+
const truncateModeOptions = [
|
|
82
|
+
{ value: 'warn', description: 'warn' },
|
|
83
|
+
{ value: 'truncate', description: 'truncate' },
|
|
84
|
+
{ value: 'sample', description: 'sample' },
|
|
85
|
+
];
|
|
86
|
+
const directSettingSpecs = [
|
|
87
|
+
{
|
|
88
|
+
value: 'emojifilter',
|
|
89
|
+
hint: 'filter mode',
|
|
90
|
+
description: 'filter mode',
|
|
91
|
+
options: emojifilterOptions,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
value: 'context-limit',
|
|
95
|
+
hint: 'positive integer (e.g., 100000)',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
value: 'compression-threshold',
|
|
99
|
+
hint: 'decimal between 0 and 1 (e.g., 0.7)',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
value: 'base-url',
|
|
103
|
+
hint: 'URL string (e.g., https://api.example.com)',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
value: 'api-version',
|
|
107
|
+
hint: 'API version (e.g., v1, 2024-05-01)',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
value: 'streaming',
|
|
111
|
+
hint: 'enabled or disabled',
|
|
112
|
+
description: 'streaming mode',
|
|
113
|
+
options: streamingOptions,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
value: 'socket-timeout',
|
|
117
|
+
hint: 'positive integer in milliseconds (e.g., 60000)',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
value: 'socket-keepalive',
|
|
121
|
+
hint: 'true or false',
|
|
122
|
+
description: 'boolean value',
|
|
123
|
+
options: booleanOptions,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
value: 'socket-nodelay',
|
|
127
|
+
hint: 'true or false',
|
|
128
|
+
description: 'boolean value',
|
|
129
|
+
options: booleanOptions,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
value: 'shell-replacement',
|
|
133
|
+
hint: 'true or false',
|
|
134
|
+
description: 'boolean value',
|
|
135
|
+
options: booleanOptions,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
value: 'tool-output-max-items',
|
|
139
|
+
hint: 'positive integer (e.g., 50)',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
value: 'tool-output-max-tokens',
|
|
143
|
+
hint: 'positive integer (e.g., 50000)',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
value: 'tool-output-item-size-limit',
|
|
147
|
+
hint: 'positive integer (e.g., 1048576)',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
value: 'tool-output-truncate-mode',
|
|
151
|
+
hint: 'warn, truncate, or sample',
|
|
152
|
+
description: 'truncate mode',
|
|
153
|
+
options: truncateModeOptions,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
value: 'max-prompt-tokens',
|
|
157
|
+
hint: 'positive integer (e.g., 200000)',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
value: 'maxTurnsPerPrompt',
|
|
161
|
+
hint: 'positive integer or -1 (unlimited)',
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
const createSettingLiteral = (spec) => ({
|
|
165
|
+
kind: 'literal',
|
|
166
|
+
value: spec.value,
|
|
167
|
+
description: `${toTitleCase(spec.value)} option`,
|
|
168
|
+
next: [
|
|
169
|
+
{
|
|
170
|
+
kind: 'value',
|
|
171
|
+
name: `${spec.value}-value`,
|
|
172
|
+
description: spec.description ?? `${toTitleCase(spec.value)} value`,
|
|
173
|
+
hint: spec.hint,
|
|
174
|
+
options: spec.options,
|
|
175
|
+
completer: spec.completer,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
});
|
|
179
|
+
const directSettingLiterals = directSettingSpecs.map(createSettingLiteral);
|
|
180
|
+
const directSettingKeys = new Set(directSettingSpecs.map((spec) => spec.value));
|
|
181
|
+
// Stryker disable StringLiteral -- literal descriptions are static UX copy verified via interaction tests.
|
|
182
|
+
const setSchema = [
|
|
183
|
+
{
|
|
184
|
+
kind: 'literal',
|
|
185
|
+
value: 'unset',
|
|
186
|
+
description: 'Unset option',
|
|
187
|
+
next: [
|
|
188
|
+
{
|
|
189
|
+
kind: 'value',
|
|
190
|
+
name: 'key',
|
|
191
|
+
description: 'setting key to remove',
|
|
192
|
+
completer: async (_ctx, partial) => {
|
|
193
|
+
const ephemeralSettings = getRuntimeApi().getEphemeralSettings();
|
|
194
|
+
const ephemeralKeys = Object.keys(ephemeralSettings).filter((key) => ephemeralSettings[key] !== undefined);
|
|
195
|
+
const specialKeys = [
|
|
196
|
+
'modelparam',
|
|
197
|
+
'custom-headers',
|
|
198
|
+
...Array.from(directSettingKeys),
|
|
199
|
+
];
|
|
200
|
+
const allKeys = Array.from(new Set([...ephemeralKeys, ...specialKeys]));
|
|
201
|
+
return allKeys
|
|
202
|
+
.filter((key) => key.startsWith(partial))
|
|
203
|
+
.map((key) => ({ value: key, description: `setting: ${key}` }));
|
|
204
|
+
},
|
|
205
|
+
next: [
|
|
206
|
+
{
|
|
207
|
+
kind: 'value',
|
|
208
|
+
name: 'subkey',
|
|
209
|
+
description: 'nested key for specific settings',
|
|
210
|
+
hint: async (_ctx, tokens) => {
|
|
211
|
+
const key = tokens.tokens[1];
|
|
212
|
+
if (key === 'modelparam') {
|
|
213
|
+
return 'model parameter name (e.g., temperature, max_tokens)';
|
|
214
|
+
}
|
|
215
|
+
if (key === 'custom-headers') {
|
|
216
|
+
return 'header name (e.g., Authorization)';
|
|
217
|
+
}
|
|
218
|
+
return 'subkey (optional)';
|
|
219
|
+
},
|
|
220
|
+
completer: async (ctx, partial, tokens) => {
|
|
221
|
+
const key = tokens.tokens[1];
|
|
222
|
+
if (key === 'modelparam') {
|
|
223
|
+
const params = getRuntimeApi().getActiveModelParams();
|
|
224
|
+
const paramNames = Object.keys(params);
|
|
225
|
+
return paramNames
|
|
226
|
+
.filter((name) => name.startsWith(partial))
|
|
227
|
+
.map((name) => ({
|
|
228
|
+
value: name,
|
|
229
|
+
description: `Parameter: ${name}`,
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
if (key === 'custom-headers') {
|
|
233
|
+
const headers = getRuntimeApi().getEphemeralSettings()['custom-headers'];
|
|
234
|
+
if (headers) {
|
|
235
|
+
return Object.keys(headers)
|
|
236
|
+
.filter((name) => name.startsWith(partial))
|
|
237
|
+
.map((name) => ({
|
|
238
|
+
value: name,
|
|
239
|
+
description: `header: ${name}`,
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return [];
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
kind: 'literal',
|
|
252
|
+
value: 'modelparam',
|
|
253
|
+
description: 'Model parameter option',
|
|
254
|
+
next: [
|
|
255
|
+
{
|
|
256
|
+
kind: 'value',
|
|
257
|
+
name: 'param-name',
|
|
258
|
+
description: 'parameter name',
|
|
259
|
+
hint: 'parameter name',
|
|
260
|
+
completer: async (_ctx, partial) => {
|
|
261
|
+
const modelParams = getRuntimeApi().getActiveModelParams();
|
|
262
|
+
if (modelParams && Object.keys(modelParams).length > 0) {
|
|
263
|
+
const paramNames = Object.keys(modelParams);
|
|
264
|
+
const matches = paramNames.filter((name) => name.startsWith(partial));
|
|
265
|
+
if (matches.length > 0) {
|
|
266
|
+
return matches.map((name) => ({
|
|
267
|
+
value: name,
|
|
268
|
+
description: `Parameter: ${name}`,
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const commonParams = [
|
|
273
|
+
'temperature',
|
|
274
|
+
'max_tokens',
|
|
275
|
+
'top_p',
|
|
276
|
+
'top_k',
|
|
277
|
+
'frequency_penalty',
|
|
278
|
+
'presence_penalty',
|
|
279
|
+
];
|
|
280
|
+
return commonParams
|
|
281
|
+
.filter((name) => name.startsWith(partial))
|
|
282
|
+
.map((name) => ({
|
|
283
|
+
value: name,
|
|
284
|
+
description: `Parameter: ${name}`,
|
|
285
|
+
}));
|
|
286
|
+
},
|
|
287
|
+
next: [
|
|
288
|
+
{
|
|
289
|
+
kind: 'value',
|
|
290
|
+
name: 'param-value',
|
|
291
|
+
description: 'model parameter value',
|
|
292
|
+
hint: 'value to set for the parameter (number, string, boolean, or JSON)',
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
kind: 'literal',
|
|
300
|
+
value: 'emojifilter',
|
|
301
|
+
description: 'Emoji filter option',
|
|
302
|
+
next: [
|
|
303
|
+
{
|
|
304
|
+
kind: 'value',
|
|
305
|
+
name: 'mode',
|
|
306
|
+
description: 'filter mode',
|
|
307
|
+
hint: 'filter mode',
|
|
308
|
+
options: emojifilterOptions,
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
},
|
|
312
|
+
...directSettingLiterals.filter((literal) => literal.value !== 'emojifilter'),
|
|
313
|
+
// Stryker disable all -- fallback value handler is exercised through runtime flows and
|
|
314
|
+
// would require extensive integration scaffolding beyond the schema migration scope.
|
|
315
|
+
{
|
|
316
|
+
kind: 'value',
|
|
317
|
+
name: 'setting',
|
|
318
|
+
description: 'any ephemeral setting key',
|
|
319
|
+
options: Object.entries(ephemeralSettingHelp)
|
|
320
|
+
.filter(([key]) => !directSettingKeys.has(key))
|
|
321
|
+
.map(([value, description]) => ({
|
|
322
|
+
value,
|
|
323
|
+
description,
|
|
324
|
+
})),
|
|
325
|
+
next: [
|
|
326
|
+
{
|
|
327
|
+
kind: 'value',
|
|
328
|
+
name: 'setting-value',
|
|
329
|
+
description: 'setting value',
|
|
330
|
+
hint: async (_ctx, tokens) => {
|
|
331
|
+
const setting = tokens.partialToken || tokens.tokens[0];
|
|
332
|
+
switch (setting) {
|
|
333
|
+
case 'context-limit':
|
|
334
|
+
return 'positive integer (e.g., 100000)';
|
|
335
|
+
case 'compression-threshold':
|
|
336
|
+
return 'decimal between 0 and 1 (e.g., 0.7)';
|
|
337
|
+
case 'emojifilter':
|
|
338
|
+
return 'allowed, auto, warn, or error';
|
|
339
|
+
case 'streaming':
|
|
340
|
+
return 'enabled or disabled';
|
|
341
|
+
case 'socket-timeout':
|
|
342
|
+
return 'positive integer in milliseconds (e.g., 60000)';
|
|
343
|
+
case 'socket-keepalive':
|
|
344
|
+
case 'socket-nodelay':
|
|
345
|
+
case 'shell-replacement':
|
|
346
|
+
return 'true or false';
|
|
347
|
+
case 'tool-output-truncate-mode':
|
|
348
|
+
return 'warn, truncate, or sample';
|
|
349
|
+
case 'maxTurnsPerPrompt':
|
|
350
|
+
return 'positive integer or -1 (unlimited)';
|
|
351
|
+
default:
|
|
352
|
+
return 'value to set';
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
completer: async (ctx, partial, tokens) => {
|
|
356
|
+
const setting = tokens.tokens[0] || tokens.partialToken;
|
|
357
|
+
if (setting === 'emojifilter') {
|
|
358
|
+
return emojifilterOptions
|
|
359
|
+
.filter((option) => option.value.startsWith(partial))
|
|
360
|
+
.map((option) => ({
|
|
361
|
+
value: option.value,
|
|
362
|
+
description: option.description,
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
if (setting === 'streaming') {
|
|
366
|
+
return streamingOptions
|
|
367
|
+
.filter((option) => option.value.startsWith(partial))
|
|
368
|
+
.map((option) => ({
|
|
369
|
+
value: option.value,
|
|
370
|
+
description: option.description,
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
if (setting === 'socket-keepalive' ||
|
|
374
|
+
setting === 'socket-nodelay' ||
|
|
375
|
+
setting === 'shell-replacement') {
|
|
376
|
+
return booleanOptions
|
|
377
|
+
.filter((option) => option.value.startsWith(partial))
|
|
378
|
+
.map((option) => ({
|
|
379
|
+
value: option.value,
|
|
380
|
+
description: option.description,
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
if (setting === 'tool-output-truncate-mode') {
|
|
384
|
+
return truncateModeOptions
|
|
385
|
+
.filter((option) => option.value.startsWith(partial))
|
|
386
|
+
.map((option) => ({
|
|
387
|
+
value: option.value,
|
|
388
|
+
description: option.description,
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
if (setting === 'custom-headers') {
|
|
392
|
+
const headers = getRuntimeApi().getEphemeralSettings()['custom-headers'];
|
|
393
|
+
if (headers) {
|
|
394
|
+
return Object.keys(headers)
|
|
395
|
+
.filter((name) => name.startsWith(partial))
|
|
396
|
+
.map((name) => ({
|
|
397
|
+
value: name,
|
|
398
|
+
description: `header: ${name}`,
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return [];
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
},
|
|
407
|
+
// Stryker restore all
|
|
408
|
+
];
|
|
409
|
+
// Stryker restore StringLiteral
|
|
410
|
+
export const setCommand = {
|
|
411
|
+
name: 'set',
|
|
412
|
+
description: 'set model parameters or ephemeral settings',
|
|
13
413
|
kind: CommandKind.BUILT_IN,
|
|
414
|
+
schema: setSchema,
|
|
14
415
|
action: async (context, args) => {
|
|
15
|
-
const
|
|
16
|
-
|
|
416
|
+
const runtime = getRuntimeApi();
|
|
417
|
+
// This handles direct ephemeral settings: /set <ephemeral-key> <value>
|
|
418
|
+
const trimmedArgs = args?.trim();
|
|
419
|
+
if (!trimmedArgs) {
|
|
17
420
|
return {
|
|
18
421
|
type: 'message',
|
|
19
422
|
messageType: 'error',
|
|
20
|
-
content: 'Usage: /set
|
|
423
|
+
content: 'Usage: /set <ephemeral-key> <value>\nExample: /set context-limit 100000\n\nFor model parameters use: /set modelparam <key> <value>',
|
|
21
424
|
};
|
|
22
425
|
}
|
|
426
|
+
const parts = trimmedArgs.split(/\s+/);
|
|
23
427
|
const key = parts[0];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
428
|
+
if (key === 'modelparam') {
|
|
429
|
+
if (parts.length < 3) {
|
|
430
|
+
return {
|
|
431
|
+
type: 'message',
|
|
432
|
+
messageType: 'error',
|
|
433
|
+
content: 'Usage: /set modelparam <key> <value>\nExample: /set modelparam temperature 0.7',
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
const paramName = parts[1];
|
|
437
|
+
const rawValue = parts.slice(2).join(' ');
|
|
438
|
+
const parsedParamValue = parseValue(rawValue);
|
|
439
|
+
try {
|
|
440
|
+
runtime.setActiveModelParam(paramName, parsedParamValue);
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
return {
|
|
444
|
+
type: 'message',
|
|
445
|
+
messageType: 'error',
|
|
446
|
+
content: `Failed to set model parameter: ${error instanceof Error ? error.message : String(error)}`,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const formattedValue = typeof parsedParamValue === 'string'
|
|
450
|
+
? parsedParamValue
|
|
451
|
+
: typeof parsedParamValue === 'number' ||
|
|
452
|
+
typeof parsedParamValue === 'boolean' ||
|
|
453
|
+
parsedParamValue === null
|
|
454
|
+
? String(parsedParamValue)
|
|
455
|
+
: JSON.stringify(parsedParamValue);
|
|
28
456
|
return {
|
|
29
457
|
type: 'message',
|
|
30
|
-
messageType: '
|
|
31
|
-
content: '
|
|
458
|
+
messageType: 'info',
|
|
459
|
+
content: `Model parameter '${paramName}' set to ${formattedValue}`,
|
|
32
460
|
};
|
|
33
461
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const providerManager = config.getProviderManager();
|
|
37
|
-
const activeProvider = providerManager?.getActiveProvider();
|
|
38
|
-
if (!activeProvider) {
|
|
462
|
+
if (key === 'unset') {
|
|
463
|
+
if (parts.length < 2) {
|
|
39
464
|
return {
|
|
40
465
|
type: 'message',
|
|
41
466
|
messageType: 'error',
|
|
42
|
-
content: '
|
|
467
|
+
content: 'Usage: /set unset <ephemeral-key|modelparam> [subkey]\nExample: /set unset base-url',
|
|
43
468
|
};
|
|
44
469
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!
|
|
470
|
+
const targetKey = parts[1];
|
|
471
|
+
const subKey = parts[2];
|
|
472
|
+
if (targetKey === 'modelparam') {
|
|
473
|
+
if (!subKey) {
|
|
49
474
|
return {
|
|
50
475
|
type: 'message',
|
|
51
476
|
messageType: 'error',
|
|
52
|
-
content:
|
|
477
|
+
content: 'Usage: /set unset modelparam <key>\nExample: /set unset modelparam temperature',
|
|
53
478
|
};
|
|
54
479
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
480
|
+
try {
|
|
481
|
+
runtime.clearActiveModelParam(subKey);
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
58
484
|
return {
|
|
59
485
|
type: 'message',
|
|
60
|
-
messageType: '
|
|
61
|
-
content: `
|
|
486
|
+
messageType: 'error',
|
|
487
|
+
content: `Failed to clear model parameter: ${error instanceof Error ? error.message : String(error)}`,
|
|
62
488
|
};
|
|
63
489
|
}
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
type: 'message',
|
|
67
|
-
messageType: 'error',
|
|
68
|
-
content: 'Provider does not support model parameters',
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
// Handle nested unset for custom-headers
|
|
72
|
-
if (key === 'custom-headers' && subkey) {
|
|
73
|
-
const currentHeaders = config.getEphemeralSetting('custom-headers');
|
|
74
|
-
if (!currentHeaders || !(subkey in currentHeaders)) {
|
|
75
490
|
return {
|
|
76
491
|
type: 'message',
|
|
77
|
-
messageType: '
|
|
78
|
-
content: `
|
|
492
|
+
messageType: 'info',
|
|
493
|
+
content: `Model parameter '${subKey}' cleared`,
|
|
79
494
|
};
|
|
80
495
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
delete updatedHeaders[subkey];
|
|
84
|
-
// If no headers left, remove the entire setting
|
|
85
|
-
if (Object.keys(updatedHeaders).length === 0) {
|
|
86
|
-
// Note: SettingsService doesn't currently support ephemeral settings,
|
|
87
|
-
// so we continue to use the config directly for these session-only settings
|
|
88
|
-
config.setEphemeralSetting('custom-headers', undefined);
|
|
496
|
+
const validEphemeralKeys = Object.keys(ephemeralSettingHelp);
|
|
497
|
+
if (!validEphemeralKeys.includes(targetKey)) {
|
|
89
498
|
return {
|
|
90
499
|
type: 'message',
|
|
91
|
-
messageType: '
|
|
92
|
-
content: `
|
|
500
|
+
messageType: 'error',
|
|
501
|
+
content: `Invalid setting key: ${targetKey}. Valid keys are: ${validEphemeralKeys.join(', ')}`,
|
|
93
502
|
};
|
|
94
503
|
}
|
|
95
|
-
|
|
96
|
-
|
|
504
|
+
if (targetKey === 'custom-headers' && subKey) {
|
|
505
|
+
const currentHeaders = runtime.getEphemeralSettings()['custom-headers'];
|
|
506
|
+
if (currentHeaders && subKey in currentHeaders) {
|
|
507
|
+
const nextHeaders = { ...currentHeaders };
|
|
508
|
+
delete nextHeaders[subKey];
|
|
509
|
+
runtime.setEphemeralSetting(targetKey, Object.keys(nextHeaders).length > 0 ? nextHeaders : undefined);
|
|
510
|
+
return {
|
|
511
|
+
type: 'message',
|
|
512
|
+
messageType: 'info',
|
|
513
|
+
content: `Custom header '${subKey}' cleared`,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
97
516
|
return {
|
|
98
517
|
type: 'message',
|
|
99
518
|
messageType: 'info',
|
|
100
|
-
content: `
|
|
519
|
+
content: `No custom header named '${subKey}' found`,
|
|
101
520
|
};
|
|
102
521
|
}
|
|
103
|
-
|
|
104
|
-
// Handle regular unset (non-nested)
|
|
105
|
-
if (subkey) {
|
|
106
|
-
return {
|
|
107
|
-
type: 'message',
|
|
108
|
-
messageType: 'error',
|
|
109
|
-
content: `Setting '${key}' does not support nested unset. Use: /set unset ${key}`,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
// No special handling for emojifilter - treat it like any other ephemeral setting
|
|
113
|
-
// Check if the setting exists
|
|
114
|
-
const currentValue = config.getEphemeralSetting(key);
|
|
115
|
-
if (currentValue === undefined) {
|
|
522
|
+
runtime.setEphemeralSetting(targetKey, undefined);
|
|
116
523
|
return {
|
|
117
524
|
type: 'message',
|
|
118
|
-
messageType: '
|
|
119
|
-
content: `Ephemeral setting '${
|
|
525
|
+
messageType: 'info',
|
|
526
|
+
content: `Ephemeral setting '${targetKey}' cleared`,
|
|
120
527
|
};
|
|
121
528
|
}
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
messageType: 'info',
|
|
131
|
-
content: `Ephemeral setting '${key}' has been removed`,
|
|
132
|
-
};
|
|
133
|
-
},
|
|
134
|
-
completion: async (_context, partialArg) => {
|
|
135
|
-
// Get all current ephemeral settings
|
|
136
|
-
const config = _context.services.config;
|
|
137
|
-
if (!config)
|
|
138
|
-
return [];
|
|
139
|
-
const ephemeralSettings = config.getEphemeralSettings();
|
|
140
|
-
const ephemeralKeys = Object.keys(ephemeralSettings).filter((key) => ephemeralSettings[key] !== undefined);
|
|
141
|
-
// Add 'modelparam' and 'emojifilter' as completion options
|
|
142
|
-
const specialKeys = ['modelparam', 'emojifilter'];
|
|
143
|
-
const allKeys = [...ephemeralKeys, ...specialKeys];
|
|
144
|
-
if (partialArg) {
|
|
145
|
-
const parts = partialArg.split(/\s+/);
|
|
146
|
-
// If user typed "emojifilter " (with space), offer mode options
|
|
147
|
-
if (parts.length === 2 && parts[0] === 'emojifilter') {
|
|
148
|
-
const modes = ['allowed', 'auto', 'warn', 'error'];
|
|
149
|
-
if (parts[1]) {
|
|
150
|
-
return modes.filter((mode) => mode.startsWith(parts[1]));
|
|
151
|
-
}
|
|
152
|
-
return modes;
|
|
153
|
-
}
|
|
154
|
-
// If user typed "modelparam " (with space), offer model param names
|
|
155
|
-
if (parts.length === 2 && parts[0] === 'modelparam') {
|
|
156
|
-
const providerManager = config.getProviderManager();
|
|
157
|
-
const activeProvider = providerManager?.getActiveProvider();
|
|
158
|
-
if (activeProvider &&
|
|
159
|
-
'getModelParams' in activeProvider &&
|
|
160
|
-
typeof activeProvider.getModelParams === 'function') {
|
|
161
|
-
const modelParams = activeProvider.getModelParams();
|
|
162
|
-
if (modelParams) {
|
|
163
|
-
const paramNames = Object.keys(modelParams);
|
|
164
|
-
if (parts[1]) {
|
|
165
|
-
return paramNames.filter((name) => name.startsWith(parts[1]));
|
|
166
|
-
}
|
|
167
|
-
return paramNames;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
|
-
// If user typed "custom-headers " (with space), offer header names
|
|
173
|
-
if (parts.length === 2 && parts[0] === 'custom-headers') {
|
|
174
|
-
const headers = ephemeralSettings['custom-headers'];
|
|
175
|
-
if (headers) {
|
|
176
|
-
const headerNames = Object.keys(headers);
|
|
177
|
-
if (parts[1]) {
|
|
178
|
-
return headerNames.filter((name) => name.startsWith(parts[1]));
|
|
179
|
-
}
|
|
180
|
-
return headerNames;
|
|
181
|
-
}
|
|
182
|
-
return [];
|
|
529
|
+
// If only key is provided, show help for that key
|
|
530
|
+
if (parts.length === 1) {
|
|
531
|
+
if (ephemeralSettingHelp[key]) {
|
|
532
|
+
return {
|
|
533
|
+
type: 'message',
|
|
534
|
+
messageType: 'info',
|
|
535
|
+
content: `${key}: ${ephemeralSettingHelp[key]}`,
|
|
536
|
+
};
|
|
183
537
|
}
|
|
184
|
-
// Otherwise, complete the setting key
|
|
185
|
-
return allKeys.filter((key) => key.startsWith(parts[0]));
|
|
186
|
-
}
|
|
187
|
-
return allKeys;
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
// Subcommand for /set modelparam
|
|
191
|
-
const modelParamCommand = {
|
|
192
|
-
name: 'modelparam',
|
|
193
|
-
description: 'set model parameters like temperature, max_tokens, etc',
|
|
194
|
-
kind: CommandKind.BUILT_IN,
|
|
195
|
-
action: async (context, args) => {
|
|
196
|
-
const parts = args?.trim().split(/\s+/);
|
|
197
|
-
if (!parts || parts.length < 2) {
|
|
198
538
|
return {
|
|
199
539
|
type: 'message',
|
|
200
540
|
messageType: 'error',
|
|
201
|
-
content:
|
|
541
|
+
content: `Usage: /set ${key} <value>\n\nValid ephemeral keys:\n${Object.entries(ephemeralSettingHelp)
|
|
542
|
+
.map(([k, v]) => ` ${k}: ${v}`)
|
|
543
|
+
.join('\n')}`,
|
|
202
544
|
};
|
|
203
545
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (!
|
|
546
|
+
const value = parts.slice(1).join(' '); // Join remaining parts as value
|
|
547
|
+
// List of valid ephemeral settings from the specification
|
|
548
|
+
const validEphemeralKeys = Object.keys(ephemeralSettingHelp);
|
|
549
|
+
// Check if it's a valid ephemeral key
|
|
550
|
+
if (!validEphemeralKeys.includes(key)) {
|
|
209
551
|
return {
|
|
210
552
|
type: 'message',
|
|
211
553
|
messageType: 'error',
|
|
212
|
-
content:
|
|
554
|
+
content: `Invalid setting key: ${key}. Valid keys are: ${validEphemeralKeys.join(', ')}`,
|
|
213
555
|
};
|
|
214
556
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
557
|
+
// Parse the value
|
|
558
|
+
let parsedValue = parseValue(value);
|
|
559
|
+
// Validate specific settings
|
|
560
|
+
if (key === 'compression-threshold') {
|
|
561
|
+
const numValue = parsedValue;
|
|
562
|
+
if (typeof numValue !== 'number' || numValue <= 0 || numValue > 1) {
|
|
563
|
+
return {
|
|
564
|
+
type: 'message',
|
|
565
|
+
messageType: 'error',
|
|
566
|
+
content: `compression-threshold must be a decimal between 0 and 1 (e.g., 0.7 for 70%)`,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
222
569
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
570
|
+
if (key === 'context-limit') {
|
|
571
|
+
const numValue = parsedValue;
|
|
572
|
+
if (typeof numValue !== 'number' ||
|
|
573
|
+
numValue <= 0 ||
|
|
574
|
+
!Number.isInteger(numValue)) {
|
|
575
|
+
return {
|
|
576
|
+
type: 'message',
|
|
577
|
+
messageType: 'error',
|
|
578
|
+
content: `context-limit must be a positive integer (e.g., 100000)`,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
230
581
|
}
|
|
231
|
-
//
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
582
|
+
// Validate socket configuration settings
|
|
583
|
+
if (key === 'socket-timeout') {
|
|
584
|
+
const numValue = parsedValue;
|
|
585
|
+
if (typeof numValue !== 'number' ||
|
|
586
|
+
numValue <= 0 ||
|
|
587
|
+
!Number.isInteger(numValue)) {
|
|
588
|
+
return {
|
|
589
|
+
type: 'message',
|
|
590
|
+
messageType: 'error',
|
|
591
|
+
content: `socket-timeout must be a positive integer in milliseconds (e.g., 60000)`,
|
|
592
|
+
};
|
|
593
|
+
}
|
|
239
594
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
};
|
|
595
|
+
if (key === 'socket-keepalive' || key === 'socket-nodelay') {
|
|
596
|
+
if (typeof parsedValue !== 'boolean') {
|
|
597
|
+
return {
|
|
598
|
+
type: 'message',
|
|
599
|
+
messageType: 'error',
|
|
600
|
+
content: `${key} must be either 'true' or 'false'`,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
249
603
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
604
|
+
// Validate tool output settings
|
|
605
|
+
if (key === 'tool-output-max-items' ||
|
|
606
|
+
key === 'tool-output-max-tokens' ||
|
|
607
|
+
key === 'tool-output-item-size-limit' ||
|
|
608
|
+
key === 'max-prompt-tokens') {
|
|
609
|
+
const numValue = parsedValue;
|
|
610
|
+
if (typeof numValue !== 'number' ||
|
|
611
|
+
numValue <= 0 ||
|
|
612
|
+
!Number.isInteger(numValue)) {
|
|
613
|
+
return {
|
|
614
|
+
type: 'message',
|
|
615
|
+
messageType: 'error',
|
|
616
|
+
content: `${key} must be a positive integer`,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
256
619
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
'stop_sequences',
|
|
269
|
-
'seed',
|
|
270
|
-
'enable_thinking',
|
|
271
|
-
];
|
|
272
|
-
// If user has typed part of a parameter name, filter suggestions
|
|
273
|
-
if (partialArg) {
|
|
274
|
-
const parts = partialArg.split(/\s+/);
|
|
275
|
-
if (parts.length === 1) {
|
|
276
|
-
// Still typing the parameter name
|
|
277
|
-
return commonParams.filter((param) => param.startsWith(parts[0]));
|
|
620
|
+
// Validate maxTurnsPerPrompt
|
|
621
|
+
if (key === 'maxTurnsPerPrompt') {
|
|
622
|
+
const numValue = parsedValue;
|
|
623
|
+
if (typeof numValue !== 'number' ||
|
|
624
|
+
!Number.isInteger(numValue) ||
|
|
625
|
+
(numValue !== -1 && numValue <= 0)) {
|
|
626
|
+
return {
|
|
627
|
+
type: 'message',
|
|
628
|
+
messageType: 'error',
|
|
629
|
+
content: `${key} must be a positive integer or -1 for unlimited`,
|
|
630
|
+
};
|
|
278
631
|
}
|
|
279
632
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// This handles direct ephemeral settings: /set <ephemeral-key> <value>
|
|
290
|
-
const trimmedArgs = args?.trim();
|
|
291
|
-
if (!trimmedArgs) {
|
|
292
|
-
return {
|
|
293
|
-
type: 'message',
|
|
294
|
-
messageType: 'error',
|
|
295
|
-
content: 'Usage: /set <ephemeral-key> <value>\nExample: /set context-limit 100000\n\nFor model parameters use: /set modelparam <key> <value>',
|
|
296
|
-
};
|
|
633
|
+
if (key === 'tool-output-truncate-mode') {
|
|
634
|
+
const validModes = ['warn', 'truncate', 'sample'];
|
|
635
|
+
if (!validModes.includes(parsedValue)) {
|
|
636
|
+
return {
|
|
637
|
+
type: 'message',
|
|
638
|
+
messageType: 'error',
|
|
639
|
+
content: `${key} must be one of: ${validModes.join(', ')}`,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
297
642
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
643
|
+
// Validate emojifilter mode
|
|
644
|
+
if (key === 'emojifilter') {
|
|
645
|
+
const validModes = [
|
|
646
|
+
'allowed',
|
|
647
|
+
'auto',
|
|
648
|
+
'warn',
|
|
649
|
+
'error',
|
|
650
|
+
];
|
|
651
|
+
const normalizedValue = parsedValue.toLowerCase();
|
|
652
|
+
if (!validModes.includes(normalizedValue)) {
|
|
303
653
|
return {
|
|
304
654
|
type: 'message',
|
|
305
|
-
messageType: '
|
|
306
|
-
content:
|
|
655
|
+
messageType: 'error',
|
|
656
|
+
content: `Invalid emoji filter mode '${parsedValue}'. Valid modes are: ${validModes.join(', ')}`,
|
|
307
657
|
};
|
|
308
658
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
messageType: 'error',
|
|
312
|
-
content: `Usage: /set ${key} <value>\n\nValid ephemeral keys:\n${Object.entries(ephemeralSettingHelp)
|
|
313
|
-
.map(([k, v]) => ` ${k}: ${v}`)
|
|
314
|
-
.join('\n')}`,
|
|
315
|
-
};
|
|
659
|
+
// Override the parsed value with normalized lowercase version
|
|
660
|
+
parsedValue = normalizedValue;
|
|
316
661
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
662
|
+
// Validate shell-replacement setting
|
|
663
|
+
if (key === 'shell-replacement') {
|
|
664
|
+
if (typeof parsedValue !== 'boolean') {
|
|
665
|
+
return {
|
|
666
|
+
type: 'message',
|
|
667
|
+
messageType: 'error',
|
|
668
|
+
content: `shell-replacement must be either 'true' or 'false'`,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// Validate streaming mode
|
|
673
|
+
if (key === 'streaming') {
|
|
674
|
+
const validModes = ['enabled', 'disabled'];
|
|
675
|
+
const normalizedValue = parsedValue.toLowerCase();
|
|
676
|
+
if (!validModes.includes(normalizedValue)) {
|
|
677
|
+
return {
|
|
678
|
+
type: 'message',
|
|
679
|
+
messageType: 'error',
|
|
680
|
+
content: `Invalid streaming mode '${parsedValue}'. Valid modes are: ${validModes.join(', ')}`,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
// Override the parsed value with normalized lowercase version
|
|
684
|
+
parsedValue = normalizedValue;
|
|
325
685
|
}
|
|
326
|
-
const parsedValue = parseResult.value;
|
|
327
686
|
// Get the config to apply settings
|
|
328
687
|
const config = context.services.config;
|
|
329
688
|
if (!config) {
|
|
@@ -333,8 +692,6 @@ export const setCommand = {
|
|
|
333
692
|
content: 'No configuration available',
|
|
334
693
|
};
|
|
335
694
|
}
|
|
336
|
-
const settingsService = config.getSettingsService();
|
|
337
|
-
const useSettingsService = settingsService !== null;
|
|
338
695
|
// Store compression settings as ephemeral settings
|
|
339
696
|
// They will be read by geminiChat.ts when compression is needed
|
|
340
697
|
if (key === 'context-limit' || key === 'compression-threshold') {
|
|
@@ -347,83 +704,42 @@ export const setCommand = {
|
|
|
347
704
|
// They will be saved only when user explicitly saves a profile
|
|
348
705
|
// Note: SettingsService doesn't currently support ephemeral settings,
|
|
349
706
|
// so we continue to use the config directly for these session-only settings
|
|
350
|
-
|
|
351
|
-
// When SettingsService is available, we still use config for ephemeral settings
|
|
352
|
-
// as they are session-only and not persisted to the settings file
|
|
353
|
-
config.setEphemeralSetting(key, parsedValue);
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
// Fallback to direct config usage
|
|
357
|
-
config.setEphemeralSetting(key, parsedValue);
|
|
358
|
-
}
|
|
707
|
+
runtime.setEphemeralSetting(key, parsedValue);
|
|
359
708
|
return {
|
|
360
709
|
type: 'message',
|
|
361
710
|
messageType: 'info',
|
|
362
711
|
content: `Ephemeral setting '${key}' set to ${JSON.stringify(parsedValue)} (session only, use /profile save to persist)`,
|
|
363
712
|
};
|
|
364
713
|
},
|
|
365
|
-
completion: async (_context, partialArg) => {
|
|
366
|
-
// Provide completions for ephemeral settings
|
|
367
|
-
const ephemeralKeys = Object.keys(ephemeralSettingHelp);
|
|
368
|
-
if (partialArg) {
|
|
369
|
-
const parts = partialArg.split(/\s+/);
|
|
370
|
-
if (parts.length === 1) {
|
|
371
|
-
// Still typing the key
|
|
372
|
-
return ephemeralKeys.filter((key) => key.startsWith(parts[0]));
|
|
373
|
-
}
|
|
374
|
-
else if (parts.length === 2) {
|
|
375
|
-
// User has typed the key and a space, provide value completions for specific keys
|
|
376
|
-
const key = parts[0];
|
|
377
|
-
// Provide completions for tool-output-truncate-mode
|
|
378
|
-
if (key === 'tool-output-truncate-mode') {
|
|
379
|
-
const modes = ['warn', 'truncate', 'sample'];
|
|
380
|
-
if (parts[1]) {
|
|
381
|
-
return modes.filter((mode) => mode.startsWith(parts[1]));
|
|
382
|
-
}
|
|
383
|
-
return modes;
|
|
384
|
-
}
|
|
385
|
-
// Provide completions for emojifilter
|
|
386
|
-
if (key === 'emojifilter') {
|
|
387
|
-
const modes = ['allowed', 'auto', 'warn', 'error'];
|
|
388
|
-
if (parts[1]) {
|
|
389
|
-
return modes.filter((mode) => mode.startsWith(parts[1].toLowerCase()));
|
|
390
|
-
}
|
|
391
|
-
return modes;
|
|
392
|
-
}
|
|
393
|
-
// Provide completions for shell-replacement
|
|
394
|
-
if (key === 'shell-replacement') {
|
|
395
|
-
const values = ['true', 'false'];
|
|
396
|
-
if (parts[1]) {
|
|
397
|
-
return values.filter((value) => value.startsWith(parts[1].toLowerCase()));
|
|
398
|
-
}
|
|
399
|
-
return values;
|
|
400
|
-
}
|
|
401
|
-
// Provide completions for streaming
|
|
402
|
-
if (key === 'streaming') {
|
|
403
|
-
const modes = ['enabled', 'disabled'];
|
|
404
|
-
if (parts[1]) {
|
|
405
|
-
return modes.filter((mode) => mode.startsWith(parts[1].toLowerCase()));
|
|
406
|
-
}
|
|
407
|
-
return modes;
|
|
408
|
-
}
|
|
409
|
-
// Provide completions for socket boolean settings
|
|
410
|
-
if (key === 'socket-keepalive' || key === 'socket-nodelay') {
|
|
411
|
-
const values = ['true', 'false'];
|
|
412
|
-
if (parts[1]) {
|
|
413
|
-
return values.filter((value) => value.startsWith(parts[1].toLowerCase()));
|
|
414
|
-
}
|
|
415
|
-
return values;
|
|
416
|
-
}
|
|
417
|
-
if (key === 'authOnly') {
|
|
418
|
-
const values = ['true', 'false'];
|
|
419
|
-
if (parts[1]) {
|
|
420
|
-
return values.filter((value) => value.startsWith(parts[1].toLowerCase()));
|
|
421
|
-
}
|
|
422
|
-
return values;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return ephemeralKeys;
|
|
427
|
-
},
|
|
428
714
|
};
|
|
715
|
+
// Stryker disable all -- Parsing is covered by higher-level integration tests and mutating this
|
|
716
|
+
// helper introduces hundreds of equivalent mutants unrelated to autocomplete behaviour.
|
|
717
|
+
/**
|
|
718
|
+
* Parse a string value into the appropriate type.
|
|
719
|
+
* Handles numbers, booleans, and JSON objects/arrays.
|
|
720
|
+
*/
|
|
721
|
+
function parseValue(value) {
|
|
722
|
+
// Try to parse as number
|
|
723
|
+
if (/^-?\d+(\.\d+)?$/.test(value)) {
|
|
724
|
+
const num = Number(value);
|
|
725
|
+
if (!isNaN(num)) {
|
|
726
|
+
return num;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
// Try to parse as boolean
|
|
730
|
+
if (value.toLowerCase() === 'true') {
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
if (value.toLowerCase() === 'false') {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
// Try to parse as JSON
|
|
737
|
+
try {
|
|
738
|
+
return JSON.parse(value);
|
|
739
|
+
}
|
|
740
|
+
catch {
|
|
741
|
+
// If all parsing fails, return as string
|
|
742
|
+
return value;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
429
745
|
//# sourceMappingURL=setCommand.js.map
|