byterover-cli 1.4.0 → 1.5.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/README.md +61 -1
- package/dist/core/domain/cipher/process/types.d.ts +1 -1
- package/dist/core/domain/entities/provider-config.d.ts +92 -0
- package/dist/core/domain/entities/provider-config.js +181 -0
- package/dist/core/domain/entities/provider-registry.d.ts +55 -0
- package/dist/core/domain/entities/provider-registry.js +74 -0
- package/dist/core/interfaces/cipher/i-content-generator.d.ts +30 -0
- package/dist/core/interfaces/cipher/i-content-generator.js +12 -1
- package/dist/core/interfaces/cipher/message-factory.d.ts +4 -1
- package/dist/core/interfaces/cipher/message-factory.js +5 -0
- package/dist/core/interfaces/cipher/message-types.d.ts +19 -1
- package/dist/core/interfaces/i-provider-config-store.d.ts +88 -0
- package/dist/core/interfaces/i-provider-config-store.js +1 -0
- package/dist/core/interfaces/i-provider-keychain-store.d.ts +33 -0
- package/dist/core/interfaces/i-provider-keychain-store.js +1 -0
- package/dist/infra/cipher/file-system/file-system-service.js +5 -5
- package/dist/infra/cipher/http/internal-llm-http-service.d.ts +40 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +152 -2
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +8 -1
- package/dist/infra/cipher/llm/generators/byterover-content-generator.d.ts +2 -3
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +20 -11
- package/dist/infra/cipher/llm/generators/openrouter-content-generator.d.ts +1 -0
- package/dist/infra/cipher/llm/generators/openrouter-content-generator.js +26 -0
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +13 -0
- package/dist/infra/cipher/llm/internal-llm-service.js +75 -4
- package/dist/infra/cipher/llm/model-capabilities.d.ts +74 -0
- package/dist/infra/cipher/llm/model-capabilities.js +157 -0
- package/dist/infra/cipher/llm/openrouter-llm-service.d.ts +35 -1
- package/dist/infra/cipher/llm/openrouter-llm-service.js +216 -28
- package/dist/infra/cipher/llm/stream-processor.d.ts +22 -2
- package/dist/infra/cipher/llm/stream-processor.js +78 -4
- package/dist/infra/cipher/llm/thought-parser.d.ts +1 -1
- package/dist/infra/cipher/llm/thought-parser.js +5 -5
- package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.d.ts +49 -0
- package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.js +272 -0
- package/dist/infra/cipher/llm/transformers/reasoning-extractor.d.ts +71 -0
- package/dist/infra/cipher/llm/transformers/reasoning-extractor.js +253 -0
- package/dist/infra/cipher/process/process-service.js +1 -1
- package/dist/infra/cipher/session/chat-session.d.ts +2 -0
- package/dist/infra/cipher/session/chat-session.js +13 -2
- package/dist/infra/cipher/storage/message-storage-service.js +4 -0
- package/dist/infra/cipher/tools/implementations/bash-exec-tool.js +3 -3
- package/dist/infra/cipher/tools/implementations/task-tool.js +1 -1
- package/dist/infra/http/openrouter-api-client.d.ts +148 -0
- package/dist/infra/http/openrouter-api-client.js +161 -0
- package/dist/infra/mcp/tools/task-result-waiter.js +9 -1
- package/dist/infra/process/agent-worker.js +178 -70
- package/dist/infra/process/transport-handlers.d.ts +25 -4
- package/dist/infra/process/transport-handlers.js +57 -10
- package/dist/infra/repl/commands/connectors-command.js +2 -2
- package/dist/infra/repl/commands/index.js +5 -0
- package/dist/infra/repl/commands/model-command.d.ts +13 -0
- package/dist/infra/repl/commands/model-command.js +212 -0
- package/dist/infra/repl/commands/provider-command.d.ts +13 -0
- package/dist/infra/repl/commands/provider-command.js +181 -0
- package/dist/infra/repl/transport-client-helper.js +6 -2
- package/dist/infra/storage/file-provider-config-store.d.ts +83 -0
- package/dist/infra/storage/file-provider-config-store.js +157 -0
- package/dist/infra/storage/provider-keychain-store.d.ts +37 -0
- package/dist/infra/storage/provider-keychain-store.js +75 -0
- package/dist/infra/transport/socket-io-transport-client.d.ts +20 -0
- package/dist/infra/transport/socket-io-transport-client.js +88 -1
- package/dist/resources/tools/bash_exec.txt +1 -1
- package/dist/tui/components/api-key-dialog.d.ts +39 -0
- package/dist/tui/components/api-key-dialog.js +94 -0
- package/dist/tui/components/execution/execution-changes.d.ts +3 -1
- package/dist/tui/components/execution/execution-changes.js +4 -4
- package/dist/tui/components/execution/execution-content.d.ts +1 -1
- package/dist/tui/components/execution/execution-content.js +4 -12
- package/dist/tui/components/execution/execution-input.js +1 -1
- package/dist/tui/components/execution/execution-progress.d.ts +10 -13
- package/dist/tui/components/execution/execution-progress.js +70 -17
- package/dist/tui/components/execution/execution-reasoning.d.ts +16 -0
- package/dist/tui/components/execution/execution-reasoning.js +34 -0
- package/dist/tui/components/execution/execution-tool.d.ts +23 -0
- package/dist/tui/components/execution/execution-tool.js +125 -0
- package/dist/tui/components/execution/expanded-log-view.js +3 -3
- package/dist/tui/components/execution/log-item.d.ts +2 -0
- package/dist/tui/components/execution/log-item.js +6 -4
- package/dist/tui/components/index.d.ts +2 -0
- package/dist/tui/components/index.js +2 -0
- package/dist/tui/components/inline-prompts/inline-select.js +3 -2
- package/dist/tui/components/model-dialog.d.ts +63 -0
- package/dist/tui/components/model-dialog.js +89 -0
- package/dist/tui/components/onboarding/onboarding-flow.js +8 -2
- package/dist/tui/components/provider-dialog.d.ts +27 -0
- package/dist/tui/components/provider-dialog.js +31 -0
- package/dist/tui/components/reasoning-text.d.ts +26 -0
- package/dist/tui/components/reasoning-text.js +49 -0
- package/dist/tui/components/selectable-list.d.ts +54 -0
- package/dist/tui/components/selectable-list.js +180 -0
- package/dist/tui/components/streaming-text.d.ts +30 -0
- package/dist/tui/components/streaming-text.js +52 -0
- package/dist/tui/contexts/tasks-context.d.ts +15 -0
- package/dist/tui/contexts/tasks-context.js +224 -40
- package/dist/tui/contexts/theme-context.d.ts +1 -0
- package/dist/tui/contexts/theme-context.js +3 -2
- package/dist/tui/hooks/use-activity-logs.js +7 -1
- package/dist/tui/types/messages.d.ts +32 -5
- package/dist/tui/utils/index.d.ts +1 -1
- package/dist/tui/utils/index.js +1 -1
- package/dist/tui/utils/log.d.ts +0 -9
- package/dist/tui/utils/log.js +2 -53
- package/dist/tui/views/command-view.js +4 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Command-line interface for ByteRover, featuring an interactive REPL with a moder
|
|
|
14
14
|
* [Keyboard Shortcuts](#keyboard-shortcuts)
|
|
15
15
|
* [What is Context Tree?](#what-is-context-tree)
|
|
16
16
|
* [Supported AI Agents](#supported-ai-agents)
|
|
17
|
+
* [LLM Providers](#llm-providers) (BETA)
|
|
17
18
|
* [Slash Commands Reference](#slash-commands-reference)
|
|
18
19
|
* [Authentication](#authentication)
|
|
19
20
|
* [Configuration](#configuration)
|
|
@@ -51,7 +52,7 @@ brv --version
|
|
|
51
52
|
|
|
52
53
|
Visit [**ByteRover Docs**](https://docs.byterover.dev) for more information.
|
|
53
54
|
|
|
54
|
-
Get started with ByteRover CLI
|
|
55
|
+
Get started with ByteRover CLI:
|
|
55
56
|
|
|
56
57
|
### 1. Start the REPL
|
|
57
58
|
|
|
@@ -91,6 +92,14 @@ ByteRover automatically configures the best connector for your installed agents:
|
|
|
91
92
|
- **MCP tools** for most other agents (universal protocol)
|
|
92
93
|
- Switch connector types anytime via `/connectors`
|
|
93
94
|
|
|
95
|
+
### 5. (Optional) Connect an LLM Provider
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
/provider
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
ByteRover works out of the box with its built-in LLM provider. To use your own models, connect to [OpenRouter](https://openrouter.ai/keys) for access to 200+ models. See [LLM Providers](#llm-providers) for details.
|
|
102
|
+
|
|
94
103
|
You're now ready to use ByteRover! Try `/status` to see your project's current state.
|
|
95
104
|
|
|
96
105
|
## Interactive REPL
|
|
@@ -203,6 +212,48 @@ ByteRover supports four connector types:
|
|
|
203
212
|
- All others (16 agents): MCP connector
|
|
204
213
|
- Rules: Available for all agents as fallback
|
|
205
214
|
|
|
215
|
+
## LLM Providers (BETA)
|
|
216
|
+
|
|
217
|
+
ByteRover uses LLMs internally to power `/curate` and `/query` operations. By default, the built-in ByteRover provider is used with no configuration required. You can optionally connect to OpenRouter to access 200+ models.
|
|
218
|
+
|
|
219
|
+
### Available Providers
|
|
220
|
+
|
|
221
|
+
| Provider | API Key Required | Models |
|
|
222
|
+
|----------|-----------------|--------|
|
|
223
|
+
| **ByteRover** (default) | No | Built-in internal model |
|
|
224
|
+
| **OpenRouter** | Yes | 200+ models from Anthropic, OpenAI, Google, Meta, and more |
|
|
225
|
+
|
|
226
|
+
### Connecting a Provider
|
|
227
|
+
|
|
228
|
+
Use `/provider` (aliases: `/providers`, `/connect`) to switch providers:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
/provider
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This opens an interactive prompt showing all available providers with their connection status.
|
|
235
|
+
|
|
236
|
+
### Selecting a Model
|
|
237
|
+
|
|
238
|
+
When connected to OpenRouter, use `/model` (alias: `/models`) to browse and select models:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
/model
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The model browser shows:
|
|
245
|
+
- **Pricing**: Input/output cost per million tokens (e.g., `$3.00/$15.00/M`)
|
|
246
|
+
- **Context window**: Maximum token capacity (e.g., `200K ctx`)
|
|
247
|
+
- **Free models**: Marked with `[Free]`
|
|
248
|
+
- **Favorites and recents**: Starred and recently used models appear first
|
|
249
|
+
|
|
250
|
+
### OpenRouter Setup
|
|
251
|
+
|
|
252
|
+
1. Get an API key at [openrouter.ai/keys](https://openrouter.ai/keys)
|
|
253
|
+
2. Run `/provider` and select **OpenRouter**
|
|
254
|
+
3. Paste your API key when prompted (stored securely in system keychain)
|
|
255
|
+
4. Run `/model` to select a model (default: `anthropic/claude-3.5-sonnet`)
|
|
256
|
+
|
|
206
257
|
## Slash Commands Reference
|
|
207
258
|
|
|
208
259
|
### Core Workflow
|
|
@@ -279,6 +330,15 @@ ByteRover supports four connector types:
|
|
|
279
330
|
**Reset options:**
|
|
280
331
|
- `-y, --yes`: Skip confirmation prompt
|
|
281
332
|
|
|
333
|
+
### LLM Providers (BETA)
|
|
334
|
+
|
|
335
|
+
| Command | Description |
|
|
336
|
+
|---------|-------------|
|
|
337
|
+
| `/provider` | Connect to and switch between LLM providers |
|
|
338
|
+
| `/model` | Select a model from the active provider (OpenRouter) |
|
|
339
|
+
|
|
340
|
+
**Aliases:** `/providers` and `/connect` for `/provider`; `/models` for `/model`
|
|
341
|
+
|
|
282
342
|
### Session Management
|
|
283
343
|
|
|
284
344
|
| Command | Description |
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Configuration Entity
|
|
3
|
+
*
|
|
4
|
+
* Stores user's provider preferences and connection state.
|
|
5
|
+
* Non-sensitive data (API keys stored separately in keychain).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for a single connected provider.
|
|
9
|
+
*/
|
|
10
|
+
export interface ConnectedProviderConfig {
|
|
11
|
+
/** Currently active model for this provider */
|
|
12
|
+
readonly activeModel?: string;
|
|
13
|
+
/** When the provider was connected */
|
|
14
|
+
readonly connectedAt: string;
|
|
15
|
+
/** User's favorite models (for quick access) */
|
|
16
|
+
readonly favoriteModels: readonly string[];
|
|
17
|
+
/** Recently used models (last 10) */
|
|
18
|
+
readonly recentModels: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parameters for creating a ProviderConfig.
|
|
22
|
+
*/
|
|
23
|
+
export interface ProviderConfigParams {
|
|
24
|
+
/** Currently active provider ID */
|
|
25
|
+
readonly activeProvider: string;
|
|
26
|
+
/** Configuration for each connected provider */
|
|
27
|
+
readonly providers: Readonly<Record<string, ConnectedProviderConfig>>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Default configuration when no providers are connected.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_PROVIDER_CONFIG: ProviderConfigParams;
|
|
33
|
+
/**
|
|
34
|
+
* Represents the provider configuration for the CLI.
|
|
35
|
+
* Tracks which providers are connected and user preferences.
|
|
36
|
+
*/
|
|
37
|
+
export declare class ProviderConfig {
|
|
38
|
+
readonly activeProvider: string;
|
|
39
|
+
readonly providers: Readonly<Record<string, ConnectedProviderConfig>>;
|
|
40
|
+
private constructor();
|
|
41
|
+
/**
|
|
42
|
+
* Creates a new ProviderConfig with default values.
|
|
43
|
+
*/
|
|
44
|
+
static createDefault(): ProviderConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Deserializes config from JSON format.
|
|
47
|
+
* Returns default config for invalid JSON structure.
|
|
48
|
+
*/
|
|
49
|
+
static fromJson(json: unknown): ProviderConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Get the active model for a provider.
|
|
52
|
+
*/
|
|
53
|
+
getActiveModel(providerId: string): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Get favorite models for a provider.
|
|
56
|
+
*/
|
|
57
|
+
getFavoriteModels(providerId: string): readonly string[];
|
|
58
|
+
/**
|
|
59
|
+
* Get recent models for a provider.
|
|
60
|
+
*/
|
|
61
|
+
getRecentModels(providerId: string): readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* Check if a provider is connected.
|
|
64
|
+
*/
|
|
65
|
+
isProviderConnected(providerId: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Serializes the config to JSON format.
|
|
68
|
+
*/
|
|
69
|
+
toJson(): ProviderConfigParams;
|
|
70
|
+
/**
|
|
71
|
+
* Create a new config with the active model changed for a provider.
|
|
72
|
+
*/
|
|
73
|
+
withActiveModel(providerId: string, modelId: string): ProviderConfig;
|
|
74
|
+
/**
|
|
75
|
+
* Create a new config with the active provider changed.
|
|
76
|
+
*/
|
|
77
|
+
withActiveProvider(providerId: string): ProviderConfig;
|
|
78
|
+
/**
|
|
79
|
+
* Create a new config with a model toggled as favorite.
|
|
80
|
+
*/
|
|
81
|
+
withFavoriteToggled(providerId: string, modelId: string): ProviderConfig;
|
|
82
|
+
/**
|
|
83
|
+
* Create a new config with a provider connected.
|
|
84
|
+
*/
|
|
85
|
+
withProviderConnected(providerId: string, options?: {
|
|
86
|
+
activeModel?: string;
|
|
87
|
+
}): ProviderConfig;
|
|
88
|
+
/**
|
|
89
|
+
* Create a new config with a provider disconnected.
|
|
90
|
+
*/
|
|
91
|
+
withProviderDisconnected(providerId: string): ProviderConfig;
|
|
92
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Configuration Entity
|
|
3
|
+
*
|
|
4
|
+
* Stores user's provider preferences and connection state.
|
|
5
|
+
* Non-sensitive data (API keys stored separately in keychain).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Type guard for ProviderConfig JSON validation.
|
|
9
|
+
*/
|
|
10
|
+
const isProviderConfigJson = (json) => {
|
|
11
|
+
if (typeof json !== 'object' || json === null)
|
|
12
|
+
return false;
|
|
13
|
+
const obj = json;
|
|
14
|
+
if (typeof obj.activeProvider !== 'string')
|
|
15
|
+
return false;
|
|
16
|
+
if (typeof obj.providers !== 'object' || obj.providers === null)
|
|
17
|
+
return false;
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Default configuration when no providers are connected.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_PROVIDER_CONFIG = {
|
|
24
|
+
activeProvider: 'byterover',
|
|
25
|
+
providers: {},
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Maximum number of recent models to track.
|
|
29
|
+
*/
|
|
30
|
+
const MAX_RECENT_MODELS = 10;
|
|
31
|
+
/**
|
|
32
|
+
* Represents the provider configuration for the CLI.
|
|
33
|
+
* Tracks which providers are connected and user preferences.
|
|
34
|
+
*/
|
|
35
|
+
export class ProviderConfig {
|
|
36
|
+
activeProvider;
|
|
37
|
+
providers;
|
|
38
|
+
constructor(params) {
|
|
39
|
+
this.activeProvider = params.activeProvider;
|
|
40
|
+
this.providers = params.providers;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new ProviderConfig with default values.
|
|
44
|
+
*/
|
|
45
|
+
static createDefault() {
|
|
46
|
+
return new ProviderConfig(DEFAULT_PROVIDER_CONFIG);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Deserializes config from JSON format.
|
|
50
|
+
* Returns default config for invalid JSON structure.
|
|
51
|
+
*/
|
|
52
|
+
static fromJson(json) {
|
|
53
|
+
if (!isProviderConfigJson(json)) {
|
|
54
|
+
return ProviderConfig.createDefault();
|
|
55
|
+
}
|
|
56
|
+
return new ProviderConfig(json);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the active model for a provider.
|
|
60
|
+
*/
|
|
61
|
+
getActiveModel(providerId) {
|
|
62
|
+
return this.providers[providerId]?.activeModel;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get favorite models for a provider.
|
|
66
|
+
*/
|
|
67
|
+
getFavoriteModels(providerId) {
|
|
68
|
+
return this.providers[providerId]?.favoriteModels ?? [];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get recent models for a provider.
|
|
72
|
+
*/
|
|
73
|
+
getRecentModels(providerId) {
|
|
74
|
+
return this.providers[providerId]?.recentModels ?? [];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a provider is connected.
|
|
78
|
+
*/
|
|
79
|
+
isProviderConnected(providerId) {
|
|
80
|
+
return providerId in this.providers;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Serializes the config to JSON format.
|
|
84
|
+
*/
|
|
85
|
+
toJson() {
|
|
86
|
+
return {
|
|
87
|
+
activeProvider: this.activeProvider,
|
|
88
|
+
providers: this.providers,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a new config with the active model changed for a provider.
|
|
93
|
+
*/
|
|
94
|
+
withActiveModel(providerId, modelId) {
|
|
95
|
+
const existingConfig = this.providers[providerId];
|
|
96
|
+
if (!existingConfig) {
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
// Add to recent models (at the front, deduplicated)
|
|
100
|
+
const recentModels = [
|
|
101
|
+
modelId,
|
|
102
|
+
...existingConfig.recentModels.filter((m) => m !== modelId),
|
|
103
|
+
].slice(0, MAX_RECENT_MODELS);
|
|
104
|
+
const newProviderConfig = {
|
|
105
|
+
...existingConfig,
|
|
106
|
+
activeModel: modelId,
|
|
107
|
+
recentModels,
|
|
108
|
+
};
|
|
109
|
+
return new ProviderConfig({
|
|
110
|
+
...this.toJson(),
|
|
111
|
+
providers: {
|
|
112
|
+
...this.providers,
|
|
113
|
+
[providerId]: newProviderConfig,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create a new config with the active provider changed.
|
|
119
|
+
*/
|
|
120
|
+
withActiveProvider(providerId) {
|
|
121
|
+
return new ProviderConfig({
|
|
122
|
+
...this.toJson(),
|
|
123
|
+
activeProvider: providerId,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a new config with a model toggled as favorite.
|
|
128
|
+
*/
|
|
129
|
+
withFavoriteToggled(providerId, modelId) {
|
|
130
|
+
const existingConfig = this.providers[providerId];
|
|
131
|
+
if (!existingConfig) {
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
const isFavorite = existingConfig.favoriteModels.includes(modelId);
|
|
135
|
+
const favoriteModels = isFavorite
|
|
136
|
+
? existingConfig.favoriteModels.filter((m) => m !== modelId)
|
|
137
|
+
: [...existingConfig.favoriteModels, modelId];
|
|
138
|
+
const newProviderConfig = {
|
|
139
|
+
...existingConfig,
|
|
140
|
+
favoriteModels,
|
|
141
|
+
};
|
|
142
|
+
return new ProviderConfig({
|
|
143
|
+
...this.toJson(),
|
|
144
|
+
providers: {
|
|
145
|
+
...this.providers,
|
|
146
|
+
[providerId]: newProviderConfig,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create a new config with a provider connected.
|
|
152
|
+
*/
|
|
153
|
+
withProviderConnected(providerId, options) {
|
|
154
|
+
const existingConfig = this.providers[providerId];
|
|
155
|
+
const newProviderConfig = {
|
|
156
|
+
activeModel: options?.activeModel ?? existingConfig?.activeModel,
|
|
157
|
+
connectedAt: existingConfig?.connectedAt ?? new Date().toISOString(),
|
|
158
|
+
favoriteModels: existingConfig?.favoriteModels ?? [],
|
|
159
|
+
recentModels: existingConfig?.recentModels ?? [],
|
|
160
|
+
};
|
|
161
|
+
return new ProviderConfig({
|
|
162
|
+
...this.toJson(),
|
|
163
|
+
providers: {
|
|
164
|
+
...this.providers,
|
|
165
|
+
[providerId]: newProviderConfig,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create a new config with a provider disconnected.
|
|
171
|
+
*/
|
|
172
|
+
withProviderDisconnected(providerId) {
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
174
|
+
const { [providerId]: _removed, ...remainingProviders } = this.providers;
|
|
175
|
+
const newActiveProvider = this.activeProvider === providerId ? 'byterover' : this.activeProvider;
|
|
176
|
+
return new ProviderConfig({
|
|
177
|
+
activeProvider: newActiveProvider,
|
|
178
|
+
providers: remainingProviders,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Registry
|
|
3
|
+
*
|
|
4
|
+
* Defines available LLM providers that can be connected to byterover-cli.
|
|
5
|
+
* Inspired by OpenCode's provider system.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Definition for an LLM provider.
|
|
9
|
+
*/
|
|
10
|
+
export interface ProviderDefinition {
|
|
11
|
+
/** URL where users can get an API key */
|
|
12
|
+
readonly apiKeyUrl?: string;
|
|
13
|
+
/** API base URL (empty for internal providers) */
|
|
14
|
+
readonly baseUrl: string;
|
|
15
|
+
/** Category for grouping in UI */
|
|
16
|
+
readonly category: 'other' | 'popular';
|
|
17
|
+
/** Default model to use when first connected */
|
|
18
|
+
readonly defaultModel?: string;
|
|
19
|
+
/** Short description */
|
|
20
|
+
readonly description: string;
|
|
21
|
+
/** Default headers for API requests */
|
|
22
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
23
|
+
/** Unique provider identifier */
|
|
24
|
+
readonly id: string;
|
|
25
|
+
/** Endpoint to fetch available models */
|
|
26
|
+
readonly modelsEndpoint: string;
|
|
27
|
+
/** Display name */
|
|
28
|
+
readonly name: string;
|
|
29
|
+
/** Priority for display order (lower = higher priority) */
|
|
30
|
+
readonly priority: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registry of all available providers.
|
|
34
|
+
* Order by priority for consistent display.
|
|
35
|
+
*/
|
|
36
|
+
export declare const PROVIDER_REGISTRY: Readonly<Record<string, ProviderDefinition>>;
|
|
37
|
+
/**
|
|
38
|
+
* Get all providers sorted by priority.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getProvidersSortedByPriority(): ProviderDefinition[];
|
|
41
|
+
/**
|
|
42
|
+
* Get providers grouped by category.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getProvidersGroupedByCategory(): {
|
|
45
|
+
other: ProviderDefinition[];
|
|
46
|
+
popular: ProviderDefinition[];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Get a provider by ID.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getProviderById(id: string): ProviderDefinition | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Check if a provider requires an API key.
|
|
54
|
+
*/
|
|
55
|
+
export declare function providerRequiresApiKey(id: string): boolean;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Registry
|
|
3
|
+
*
|
|
4
|
+
* Defines available LLM providers that can be connected to byterover-cli.
|
|
5
|
+
* Inspired by OpenCode's provider system.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Registry of all available providers.
|
|
9
|
+
* Order by priority for consistent display.
|
|
10
|
+
*/
|
|
11
|
+
export const PROVIDER_REGISTRY = {
|
|
12
|
+
byterover: {
|
|
13
|
+
baseUrl: '',
|
|
14
|
+
category: 'popular',
|
|
15
|
+
description: 'Internal ByteRover LLM',
|
|
16
|
+
headers: {},
|
|
17
|
+
id: 'byterover',
|
|
18
|
+
modelsEndpoint: '',
|
|
19
|
+
name: 'ByteRover',
|
|
20
|
+
priority: 0,
|
|
21
|
+
},
|
|
22
|
+
openrouter: {
|
|
23
|
+
apiKeyUrl: 'https://openrouter.ai/keys',
|
|
24
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
25
|
+
category: 'popular',
|
|
26
|
+
defaultModel: 'anthropic/claude-3.5-sonnet',
|
|
27
|
+
description: 'Access 200+ models',
|
|
28
|
+
headers: {
|
|
29
|
+
'HTTP-Referer': 'https://byterover.dev',
|
|
30
|
+
'X-Title': 'byterover-cli',
|
|
31
|
+
},
|
|
32
|
+
id: 'openrouter',
|
|
33
|
+
modelsEndpoint: '/models',
|
|
34
|
+
name: 'OpenRouter',
|
|
35
|
+
priority: 1,
|
|
36
|
+
},
|
|
37
|
+
// Future providers can be added here:
|
|
38
|
+
// anthropic: { ... },
|
|
39
|
+
// openai: { ... },
|
|
40
|
+
// google: { ... },
|
|
41
|
+
// groq: { ... },
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Get all providers sorted by priority.
|
|
45
|
+
*/
|
|
46
|
+
export function getProvidersSortedByPriority() {
|
|
47
|
+
return Object.values(PROVIDER_REGISTRY).sort((a, b) => a.priority - b.priority);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get providers grouped by category.
|
|
51
|
+
*/
|
|
52
|
+
export function getProvidersGroupedByCategory() {
|
|
53
|
+
const providers = getProvidersSortedByPriority();
|
|
54
|
+
return {
|
|
55
|
+
other: providers.filter((p) => p.category === 'other'),
|
|
56
|
+
popular: providers.filter((p) => p.category === 'popular'),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get a provider by ID.
|
|
61
|
+
*/
|
|
62
|
+
export function getProviderById(id) {
|
|
63
|
+
return PROVIDER_REGISTRY[id];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if a provider requires an API key.
|
|
67
|
+
*/
|
|
68
|
+
export function providerRequiresApiKey(id) {
|
|
69
|
+
const provider = getProviderById(id);
|
|
70
|
+
if (!provider)
|
|
71
|
+
return false;
|
|
72
|
+
// Internal providers (empty baseUrl) don't need API keys
|
|
73
|
+
return provider.baseUrl.length > 0;
|
|
74
|
+
}
|
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
import type { ToolSet } from '../../domain/cipher/tools/types.js';
|
|
13
13
|
import type { ExecutionContext } from './i-cipher-agent.js';
|
|
14
14
|
import type { InternalMessage, ToolCall } from './message-types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Stream chunk type for distinguishing content types during streaming.
|
|
17
|
+
*/
|
|
18
|
+
export declare enum StreamChunkType {
|
|
19
|
+
/** Regular text content */
|
|
20
|
+
CONTENT = "content",
|
|
21
|
+
/** Thinking/reasoning content from models like Gemini */
|
|
22
|
+
THINKING = "thinking",
|
|
23
|
+
/** Tool call request */
|
|
24
|
+
TOOL_CALL = "tool_call"
|
|
25
|
+
}
|
|
15
26
|
/**
|
|
16
27
|
* Configuration for content generation.
|
|
17
28
|
*/
|
|
@@ -76,8 +87,27 @@ export interface GenerateContentChunk {
|
|
|
76
87
|
finishReason?: 'error' | 'max_tokens' | 'stop' | 'tool_calls';
|
|
77
88
|
/** Whether this is the final chunk */
|
|
78
89
|
isComplete: boolean;
|
|
90
|
+
/** Provider-specific metadata */
|
|
91
|
+
providerMetadata?: Record<string, unknown>;
|
|
92
|
+
/**
|
|
93
|
+
* Raw API chunk data for native reasoning extraction.
|
|
94
|
+
* Used by models that return reasoning in native fields (OpenAI, Grok, Gemini).
|
|
95
|
+
*/
|
|
96
|
+
rawChunk?: unknown;
|
|
97
|
+
/**
|
|
98
|
+
* Incremental reasoning/thinking content.
|
|
99
|
+
* For models that provide native reasoning fields (OpenAI o1/o3, Grok, Gemini).
|
|
100
|
+
*/
|
|
101
|
+
reasoning?: string;
|
|
102
|
+
/** Unique ID for the reasoning block (for tracking across deltas) */
|
|
103
|
+
reasoningId?: string;
|
|
79
104
|
/** Tool calls (only on final chunk or when complete) */
|
|
80
105
|
toolCalls?: ToolCall[];
|
|
106
|
+
/**
|
|
107
|
+
* Type of this chunk for distinguishing content from thinking.
|
|
108
|
+
* Defaults to CONTENT if not specified.
|
|
109
|
+
*/
|
|
110
|
+
type?: StreamChunkType;
|
|
81
111
|
}
|
|
82
112
|
/**
|
|
83
113
|
* Content Generator interface.
|
|
@@ -9,4 +9,15 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Based on gemini-cli's ContentGenerator pattern.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Stream chunk type for distinguishing content types during streaming.
|
|
14
|
+
*/
|
|
15
|
+
export var StreamChunkType;
|
|
16
|
+
(function (StreamChunkType) {
|
|
17
|
+
/** Regular text content */
|
|
18
|
+
StreamChunkType["CONTENT"] = "content";
|
|
19
|
+
/** Thinking/reasoning content from models like Gemini */
|
|
20
|
+
StreamChunkType["THINKING"] = "thinking";
|
|
21
|
+
/** Tool call request */
|
|
22
|
+
StreamChunkType["TOOL_CALL"] = "tool_call";
|
|
23
|
+
})(StreamChunkType || (StreamChunkType = {}));
|
|
@@ -40,7 +40,10 @@ export declare function createFilePart(data: FilePart['data'], mimeType: string,
|
|
|
40
40
|
export declare function createReasoningPart(text: string, summary?: {
|
|
41
41
|
description: string;
|
|
42
42
|
subject: string;
|
|
43
|
-
}, options?: PartOptions
|
|
43
|
+
}, options?: PartOptions & {
|
|
44
|
+
id?: string;
|
|
45
|
+
providerMetadata?: Record<string, unknown>;
|
|
46
|
+
}): ReasoningPart;
|
|
44
47
|
/**
|
|
45
48
|
* Create a ToolPart in pending state.
|
|
46
49
|
*/
|
|
@@ -52,9 +52,14 @@ export function createFilePart(data, mimeType, filename, options) {
|
|
|
52
52
|
*/
|
|
53
53
|
export function createReasoningPart(text, summary, options) {
|
|
54
54
|
return {
|
|
55
|
+
id: options?.id ?? `reasoning-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
55
56
|
text,
|
|
57
|
+
time: {
|
|
58
|
+
start: Date.now(),
|
|
59
|
+
},
|
|
56
60
|
type: 'reasoning',
|
|
57
61
|
...(summary && { summary }),
|
|
62
|
+
...(options?.providerMetadata && { providerMetadata: options.providerMetadata }),
|
|
58
63
|
...(options?.synthetic !== undefined && { synthetic: options.synthetic }),
|
|
59
64
|
...(options?.metadata && { metadata: options.metadata }),
|
|
60
65
|
};
|
|
@@ -57,9 +57,17 @@ export interface FilePart extends BasePart {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Reasoning/thinking content part.
|
|
60
|
-
* Represents structured thinking traces from models like Gemini or
|
|
60
|
+
* Represents structured thinking traces from models like Gemini, Claude, Grok, or OpenAI.
|
|
61
|
+
* Follows OpenCode's pattern with id, timing, and provider metadata.
|
|
61
62
|
*/
|
|
62
63
|
export interface ReasoningPart extends BasePart {
|
|
64
|
+
/** Unique identifier for this reasoning block */
|
|
65
|
+
id: string;
|
|
66
|
+
/**
|
|
67
|
+
* Provider-specific metadata (encrypted content, reasoning effort, etc.)
|
|
68
|
+
* Used to preserve original provider data for debugging and special handling.
|
|
69
|
+
*/
|
|
70
|
+
providerMetadata?: Record<string, unknown>;
|
|
63
71
|
/**
|
|
64
72
|
* Parsed summary for display in UI.
|
|
65
73
|
* Extracted from the raw text for easier rendering.
|
|
@@ -72,6 +80,16 @@ export interface ReasoningPart extends BasePart {
|
|
|
72
80
|
};
|
|
73
81
|
/** Raw reasoning/thinking text from the model */
|
|
74
82
|
text: string;
|
|
83
|
+
/**
|
|
84
|
+
* Timing information for the reasoning block.
|
|
85
|
+
* Used for UX feedback and analytics.
|
|
86
|
+
*/
|
|
87
|
+
time: {
|
|
88
|
+
/** Unix timestamp when reasoning ended (undefined if still streaming) */
|
|
89
|
+
end?: number;
|
|
90
|
+
/** Unix timestamp when reasoning started */
|
|
91
|
+
start: number;
|
|
92
|
+
};
|
|
75
93
|
type: 'reasoning';
|
|
76
94
|
}
|
|
77
95
|
/**
|