byterover-cli 2.3.2 → 2.3.4

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.
@@ -51,8 +51,6 @@ export declare class ByteRoverLlmHttpService {
51
51
  * Call ByteRover REST LLM service to generate content.
52
52
  *
53
53
  * Simple forward to remote REST API - delegates all formatting to backend.
54
- * Supports both Gemini and Claude formats - the correct format is determined
55
- * automatically based on the model name.
56
54
  *
57
55
  * Parameter structure differs by provider:
58
56
  * - Gemini: contents = Content[], config = GenerateContentConfig
@@ -60,27 +58,22 @@ export declare class ByteRoverLlmHttpService {
60
58
  *
61
59
  * @param contents - For Gemini: Content[]. For Claude: MessageCreateParamsNonStreaming (complete body)
62
60
  * @param config - For Gemini: GenerateContentConfig. For Claude: RequestOptions (optional HTTP options)
63
- * @param model - Model to use (detects provider from model name)
64
61
  * @param executionMetadata - Optional execution metadata (mode, executionContext)
65
62
  * @returns Response in GenerateContentResponse format
66
63
  */
67
- generateContent(contents: Content[] | MessageCreateParamsNonStreaming, config: GenerateContentConfig | RequestOptions, model: string, executionMetadata?: Record<string, unknown>): Promise<GenerateContentResponse>;
64
+ generateContent(contents: Content[] | MessageCreateParamsNonStreaming, config: GenerateContentConfig | RequestOptions, executionMetadata?: Record<string, unknown>): Promise<GenerateContentResponse>;
68
65
  /**
69
66
  * Call ByteRover REST LLM service to generate content with streaming.
70
67
  *
71
- * Currently falls back to non-streaming endpoint since /api/llm/generate/stream
72
- * doesn't exist on the backend yet. Extracts thinking/reasoning from the complete
73
- * response and yields them as separate chunks.
74
- *
75
- * When backend streaming is available, this will use SSE for true streaming.
68
+ * Currently falls back to non-streaming endpoint. Extracts thinking/reasoning
69
+ * from the complete response and yields them as separate chunks.
76
70
  *
77
71
  * @param contents - For Gemini: Content[]. For Claude: MessageCreateParamsNonStreaming (complete body)
78
72
  * @param config - For Gemini: GenerateContentConfig. For Claude: RequestOptions (optional HTTP options)
79
- * @param model - Model to use (detects provider from model name)
80
73
  * @param executionMetadata - Optional execution metadata (mode, executionContext)
81
74
  * @yields GenerateContentChunk objects as they are generated
82
75
  */
83
- generateContentStream(contents: Content[] | MessageCreateParamsNonStreaming, config: GenerateContentConfig | RequestOptions, model: string, executionMetadata?: Record<string, unknown>): AsyncGenerator<GenerateContentChunk>;
76
+ generateContentStream(contents: Content[] | MessageCreateParamsNonStreaming, config: GenerateContentConfig | RequestOptions, executionMetadata?: Record<string, unknown>): AsyncGenerator<GenerateContentChunk>;
84
77
  /**
85
78
  * Call the ByteRover REST Generate endpoint.
86
79
  *
@@ -91,26 +84,6 @@ export declare class ByteRoverLlmHttpService {
91
84
  * @throws Error if the request fails
92
85
  */
93
86
  private callHttpGenerate;
94
- /**
95
- * Detect LLM provider from model identifier.
96
- *
97
- * Determines which provider (Claude or Gemini) to use based on the model name.
98
- * Defaults to Gemini if the model doesn't match Claude patterns.
99
- *
100
- * @param model - Model identifier (e.g., 'claude-3-5-sonnet', 'gemini-2.5-flash')
101
- * @returns Provider name: 'claude' or 'gemini'
102
- */
103
- private detectProviderFromModel;
104
- /**
105
- * Detect appropriate GCP region from model identifier.
106
- *
107
- * Routes Claude models to us-east5 and Gemini models to global.
108
- * This ensures compatibility with the provider's available regions on Vertex AI.
109
- *
110
- * @param model - Model identifier (e.g., 'claude-3-5-sonnet', 'gemini-2.5-flash')
111
- * @returns GCP region identifier ('us-east5' or 'global')
112
- */
113
- private detectRegionFromModel;
114
87
  /**
115
88
  * Extract content chunks from a complete response.
116
89
  *
@@ -33,7 +33,7 @@ export class ByteRoverLlmHttpService {
33
33
  this.config = {
34
34
  apiBaseUrl: config.apiBaseUrl,
35
35
  projectId: config.projectId ?? 'byterover',
36
- region: config.region ?? 'us-east1',
36
+ region: config.region ?? 'global',
37
37
  sessionKey: config.sessionKey,
38
38
  spaceId: config.spaceId,
39
39
  teamId: config.teamId,
@@ -44,8 +44,6 @@ export class ByteRoverLlmHttpService {
44
44
  * Call ByteRover REST LLM service to generate content.
45
45
  *
46
46
  * Simple forward to remote REST API - delegates all formatting to backend.
47
- * Supports both Gemini and Claude formats - the correct format is determined
48
- * automatically based on the model name.
49
47
  *
50
48
  * Parameter structure differs by provider:
51
49
  * - Gemini: contents = Content[], config = GenerateContentConfig
@@ -53,21 +51,18 @@ export class ByteRoverLlmHttpService {
53
51
  *
54
52
  * @param contents - For Gemini: Content[]. For Claude: MessageCreateParamsNonStreaming (complete body)
55
53
  * @param config - For Gemini: GenerateContentConfig. For Claude: RequestOptions (optional HTTP options)
56
- * @param model - Model to use (detects provider from model name)
57
54
  * @param executionMetadata - Optional execution metadata (mode, executionContext)
58
55
  * @returns Response in GenerateContentResponse format
59
56
  */
60
- async generateContent(contents, config, model, executionMetadata) {
57
+ async generateContent(contents, config, executionMetadata) {
61
58
  const request = {
62
59
  executionMetadata: JSON.stringify(executionMetadata ?? {}),
63
60
  params: {
64
61
  config,
65
62
  contents,
66
- model,
67
63
  },
68
64
  project_id: typeof this.config.projectId === 'function' ? this.config.projectId() : this.config.projectId,
69
- provider: this.detectProviderFromModel(model),
70
- region: this.detectRegionFromModel(model),
65
+ region: this.config.region,
71
66
  spaceId: typeof this.config.spaceId === 'function' ? this.config.spaceId() : this.config.spaceId,
72
67
  teamId: typeof this.config.teamId === 'function' ? this.config.teamId() : this.config.teamId,
73
68
  };
@@ -76,22 +71,18 @@ export class ByteRoverLlmHttpService {
76
71
  /**
77
72
  * Call ByteRover REST LLM service to generate content with streaming.
78
73
  *
79
- * Currently falls back to non-streaming endpoint since /api/llm/generate/stream
80
- * doesn't exist on the backend yet. Extracts thinking/reasoning from the complete
81
- * response and yields them as separate chunks.
82
- *
83
- * When backend streaming is available, this will use SSE for true streaming.
74
+ * Currently falls back to non-streaming endpoint. Extracts thinking/reasoning
75
+ * from the complete response and yields them as separate chunks.
84
76
  *
85
77
  * @param contents - For Gemini: Content[]. For Claude: MessageCreateParamsNonStreaming (complete body)
86
78
  * @param config - For Gemini: GenerateContentConfig. For Claude: RequestOptions (optional HTTP options)
87
- * @param model - Model to use (detects provider from model name)
88
79
  * @param executionMetadata - Optional execution metadata (mode, executionContext)
89
80
  * @yields GenerateContentChunk objects as they are generated
90
81
  */
91
- async *generateContentStream(contents, config, model, executionMetadata) {
82
+ async *generateContentStream(contents, config, executionMetadata) {
92
83
  // Fall back to non-streaming endpoint and simulate streaming
93
84
  // by extracting thinking from the complete response
94
- const response = await this.generateContent(contents, config, model, executionMetadata);
85
+ const response = await this.generateContent(contents, config, executionMetadata);
95
86
  // Extract and yield thinking/reasoning chunks first
96
87
  yield* this.extractThinkingFromResponse(response);
97
88
  // Then yield the final content
@@ -115,30 +106,6 @@ export class ByteRoverLlmHttpService {
115
106
  });
116
107
  return httpResponse.data;
117
108
  }
118
- /**
119
- * Detect LLM provider from model identifier.
120
- *
121
- * Determines which provider (Claude or Gemini) to use based on the model name.
122
- * Defaults to Gemini if the model doesn't match Claude patterns.
123
- *
124
- * @param model - Model identifier (e.g., 'claude-3-5-sonnet', 'gemini-2.5-flash')
125
- * @returns Provider name: 'claude' or 'gemini'
126
- */
127
- detectProviderFromModel(model) {
128
- return model.toLowerCase().startsWith('claude') ? 'claude' : 'gemini';
129
- }
130
- /**
131
- * Detect appropriate GCP region from model identifier.
132
- *
133
- * Routes Claude models to us-east5 and Gemini models to global.
134
- * This ensures compatibility with the provider's available regions on Vertex AI.
135
- *
136
- * @param model - Model identifier (e.g., 'claude-3-5-sonnet', 'gemini-2.5-flash')
137
- * @returns GCP region identifier ('us-east5' or 'global')
138
- */
139
- detectRegionFromModel(model) {
140
- return model.toLowerCase().startsWith('claude') ? 'us-east5' : 'global';
141
- }
142
109
  /**
143
110
  * Extract content chunks from a complete response.
144
111
  *
@@ -31,3 +31,11 @@ export declare class AiSdkContentGenerator implements IContentGenerator {
31
31
  generateContent(request: GenerateContentRequest): Promise<GenerateContentResponse>;
32
32
  generateContentStream(request: GenerateContentRequest): AsyncGenerator<GenerateContentChunk>;
33
33
  }
34
+ /**
35
+ * Extract a human-readable message from an AI SDK stream error.
36
+ *
37
+ * The @ai-sdk/openai Responses API provider passes the raw SSE chunk
38
+ * object as the error value (not an Error instance). The actual message
39
+ * is nested at `.error.message`.
40
+ */
41
+ export declare function extractStreamErrorMessage(error: unknown): string;
@@ -89,7 +89,7 @@ export class AiSdkContentGenerator {
89
89
  // Throw the error so RetryableContentGenerator can catch and retry it.
90
90
  // Yielding it as content would swallow the error and prevent retry logic
91
91
  // from working (e.g., for 429 rate limit errors).
92
- throw event.error instanceof Error ? event.error : new Error(String(event.error));
92
+ throw event.error instanceof Error ? event.error : new Error(extractStreamErrorMessage(event.error));
93
93
  }
94
94
  case 'finish-step': {
95
95
  yield {
@@ -139,6 +139,44 @@ export class AiSdkContentGenerator {
139
139
  }
140
140
  }
141
141
  }
142
+ /**
143
+ * Extract a human-readable message from an AI SDK stream error.
144
+ *
145
+ * The @ai-sdk/openai Responses API provider passes the raw SSE chunk
146
+ * object as the error value (not an Error instance). The actual message
147
+ * is nested at `.error.message`.
148
+ */
149
+ export function extractStreamErrorMessage(error) {
150
+ if (typeof error === 'string') {
151
+ return error;
152
+ }
153
+ if (error && typeof error === 'object') {
154
+ // OpenAI Responses API shape: { type: "error", error: { message: "..." } }
155
+ if ('error' in error) {
156
+ const nested = error.error;
157
+ if (nested && typeof nested === 'object' && 'message' in nested) {
158
+ const msg = nested.message;
159
+ if (typeof msg === 'string') {
160
+ return msg;
161
+ }
162
+ }
163
+ }
164
+ // Direct message property: { message: "..." }
165
+ if ('message' in error) {
166
+ const msg = error.message;
167
+ if (typeof msg === 'string') {
168
+ return msg;
169
+ }
170
+ }
171
+ try {
172
+ return JSON.stringify(error);
173
+ }
174
+ catch {
175
+ // circular reference or other stringify failure
176
+ }
177
+ }
178
+ return String(error);
179
+ }
142
180
  /**
143
181
  * Map AI SDK finish reason to our finish reason format.
144
182
  */
@@ -87,7 +87,7 @@ export class ByteRoverContentGenerator {
87
87
  };
88
88
  // // Debug: Log taskId for tracking
89
89
  // appendFileSync('debug-taskid.log', `[${new Date().toISOString()}] taskId from request: "${request.taskId}"\n`)
90
- const rawResponse = await this.httpService.generateContent(contents, config, this.config.model, executionMetadata);
90
+ const rawResponse = await this.httpService.generateContent(contents, config, executionMetadata);
91
91
  // Parse response to internal format
92
92
  const messages = this.formatter.parseResponse(rawResponse);
93
93
  const lastMessage = messages.at(-1);
@@ -143,7 +143,7 @@ export class ByteRoverContentGenerator {
143
143
  const contents = this.providerType === 'claude' ? genConfig : formattedMessages;
144
144
  const config = this.providerType === 'claude' ? {} : genConfig;
145
145
  // Stream from HTTP service
146
- yield* this.httpService.generateContentStream(contents, config, this.config.model, executionMetadata);
146
+ yield* this.httpService.generateContentStream(contents, config, executionMetadata);
147
147
  }
148
148
  /**
149
149
  * Build Claude-specific generation configuration.
@@ -82,7 +82,27 @@ export function serializeTaskError(error) {
82
82
  name: error.name,
83
83
  };
84
84
  }
85
- // Unknown error type
85
+ // Unknown error type — extract message if possible, JSON.stringify to avoid "[object Object]"
86
+ if (error && typeof error === 'object') {
87
+ if ('message' in error) {
88
+ const msg = error.message;
89
+ if (typeof msg === 'string') {
90
+ return {
91
+ message: msg,
92
+ name: 'Error',
93
+ };
94
+ }
95
+ }
96
+ try {
97
+ return {
98
+ message: JSON.stringify(error),
99
+ name: 'Error',
100
+ };
101
+ }
102
+ catch {
103
+ // circular reference — fall through
104
+ }
105
+ }
86
106
  return {
87
107
  message: String(error),
88
108
  name: 'Error',
@@ -32,7 +32,15 @@ export function getErrorMessage(error) {
32
32
  return message;
33
33
  }
34
34
  }
35
- // Fallback for unknown error types
35
+ // Fallback: JSON for objects (avoids "[object Object]"), String for primitives
36
+ if (error && typeof error === 'object') {
37
+ try {
38
+ return JSON.stringify(error);
39
+ }
40
+ catch {
41
+ return String(error);
42
+ }
43
+ }
36
44
  return String(error);
37
45
  }
38
46
  /**
@@ -1083,104 +1083,6 @@
1083
1083
  "switch.js"
1084
1084
  ]
1085
1085
  },
1086
- "space:list": {
1087
- "aliases": [],
1088
- "args": {},
1089
- "description": "List all teams and spaces",
1090
- "examples": [
1091
- "<%= config.bin %> space list",
1092
- "<%= config.bin %> space list --format json"
1093
- ],
1094
- "flags": {
1095
- "format": {
1096
- "char": "f",
1097
- "description": "Output format",
1098
- "name": "format",
1099
- "default": "text",
1100
- "hasDynamicHelp": false,
1101
- "multiple": false,
1102
- "options": [
1103
- "text",
1104
- "json"
1105
- ],
1106
- "type": "option"
1107
- }
1108
- },
1109
- "hasDynamicHelp": false,
1110
- "hiddenAliases": [],
1111
- "id": "space:list",
1112
- "pluginAlias": "byterover-cli",
1113
- "pluginName": "byterover-cli",
1114
- "pluginType": "core",
1115
- "strict": true,
1116
- "enableJsonFlag": false,
1117
- "isESM": true,
1118
- "relativePath": [
1119
- "dist",
1120
- "oclif",
1121
- "commands",
1122
- "space",
1123
- "list.js"
1124
- ]
1125
- },
1126
- "space:switch": {
1127
- "aliases": [],
1128
- "args": {},
1129
- "description": "Switch to a different space",
1130
- "examples": [
1131
- "<%= config.bin %> space switch --team acme --name my-space",
1132
- "<%= config.bin %> space switch --team acme --name my-space --format json"
1133
- ],
1134
- "flags": {
1135
- "format": {
1136
- "char": "f",
1137
- "description": "Output format",
1138
- "name": "format",
1139
- "default": "text",
1140
- "hasDynamicHelp": false,
1141
- "multiple": false,
1142
- "options": [
1143
- "text",
1144
- "json"
1145
- ],
1146
- "type": "option"
1147
- },
1148
- "name": {
1149
- "char": "n",
1150
- "description": "Name of the space to switch to",
1151
- "name": "name",
1152
- "required": true,
1153
- "hasDynamicHelp": false,
1154
- "multiple": false,
1155
- "type": "option"
1156
- },
1157
- "team": {
1158
- "char": "t",
1159
- "description": "Team name",
1160
- "name": "team",
1161
- "required": true,
1162
- "hasDynamicHelp": false,
1163
- "multiple": false,
1164
- "type": "option"
1165
- }
1166
- },
1167
- "hasDynamicHelp": false,
1168
- "hiddenAliases": [],
1169
- "id": "space:switch",
1170
- "pluginAlias": "byterover-cli",
1171
- "pluginName": "byterover-cli",
1172
- "pluginType": "core",
1173
- "strict": true,
1174
- "enableJsonFlag": false,
1175
- "isESM": true,
1176
- "relativePath": [
1177
- "dist",
1178
- "oclif",
1179
- "commands",
1180
- "space",
1181
- "switch.js"
1182
- ]
1183
- },
1184
1086
  "providers:connect": {
1185
1087
  "aliases": [],
1186
1088
  "args": {
@@ -1437,6 +1339,104 @@
1437
1339
  "switch.js"
1438
1340
  ]
1439
1341
  },
1342
+ "space:list": {
1343
+ "aliases": [],
1344
+ "args": {},
1345
+ "description": "List all teams and spaces",
1346
+ "examples": [
1347
+ "<%= config.bin %> space list",
1348
+ "<%= config.bin %> space list --format json"
1349
+ ],
1350
+ "flags": {
1351
+ "format": {
1352
+ "char": "f",
1353
+ "description": "Output format",
1354
+ "name": "format",
1355
+ "default": "text",
1356
+ "hasDynamicHelp": false,
1357
+ "multiple": false,
1358
+ "options": [
1359
+ "text",
1360
+ "json"
1361
+ ],
1362
+ "type": "option"
1363
+ }
1364
+ },
1365
+ "hasDynamicHelp": false,
1366
+ "hiddenAliases": [],
1367
+ "id": "space:list",
1368
+ "pluginAlias": "byterover-cli",
1369
+ "pluginName": "byterover-cli",
1370
+ "pluginType": "core",
1371
+ "strict": true,
1372
+ "enableJsonFlag": false,
1373
+ "isESM": true,
1374
+ "relativePath": [
1375
+ "dist",
1376
+ "oclif",
1377
+ "commands",
1378
+ "space",
1379
+ "list.js"
1380
+ ]
1381
+ },
1382
+ "space:switch": {
1383
+ "aliases": [],
1384
+ "args": {},
1385
+ "description": "Switch to a different space",
1386
+ "examples": [
1387
+ "<%= config.bin %> space switch --team acme --name my-space",
1388
+ "<%= config.bin %> space switch --team acme --name my-space --format json"
1389
+ ],
1390
+ "flags": {
1391
+ "format": {
1392
+ "char": "f",
1393
+ "description": "Output format",
1394
+ "name": "format",
1395
+ "default": "text",
1396
+ "hasDynamicHelp": false,
1397
+ "multiple": false,
1398
+ "options": [
1399
+ "text",
1400
+ "json"
1401
+ ],
1402
+ "type": "option"
1403
+ },
1404
+ "name": {
1405
+ "char": "n",
1406
+ "description": "Name of the space to switch to",
1407
+ "name": "name",
1408
+ "required": true,
1409
+ "hasDynamicHelp": false,
1410
+ "multiple": false,
1411
+ "type": "option"
1412
+ },
1413
+ "team": {
1414
+ "char": "t",
1415
+ "description": "Team name",
1416
+ "name": "team",
1417
+ "required": true,
1418
+ "hasDynamicHelp": false,
1419
+ "multiple": false,
1420
+ "type": "option"
1421
+ }
1422
+ },
1423
+ "hasDynamicHelp": false,
1424
+ "hiddenAliases": [],
1425
+ "id": "space:switch",
1426
+ "pluginAlias": "byterover-cli",
1427
+ "pluginName": "byterover-cli",
1428
+ "pluginType": "core",
1429
+ "strict": true,
1430
+ "enableJsonFlag": false,
1431
+ "isESM": true,
1432
+ "relativePath": [
1433
+ "dist",
1434
+ "oclif",
1435
+ "commands",
1436
+ "space",
1437
+ "switch.js"
1438
+ ]
1439
+ },
1440
1440
  "hub:registry:add": {
1441
1441
  "aliases": [],
1442
1442
  "args": {
@@ -1636,5 +1636,5 @@
1636
1636
  ]
1637
1637
  }
1638
1638
  },
1639
- "version": "2.3.2"
1639
+ "version": "2.3.4"
1640
1640
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "byterover-cli",
3
3
  "description": "ByteRover's CLI",
4
- "version": "2.3.2",
4
+ "version": "2.3.4",
5
5
  "author": "ByteRover",
6
6
  "bin": {
7
7
  "brv": "./bin/run.js"