byterover-cli 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +87 -9
  2. package/dist/constants.d.ts +0 -5
  3. package/dist/constants.js +0 -5
  4. package/dist/core/domain/cipher/agent/agent-info.d.ts +17 -17
  5. package/dist/core/domain/cipher/llm/schemas.d.ts +14 -14
  6. package/dist/core/domain/cipher/session/session-metadata.d.ts +2 -2
  7. package/dist/core/domain/entities/agent.js +6 -6
  8. package/dist/core/domain/entities/connector-type.d.ts +2 -1
  9. package/dist/core/domain/entities/connector-type.js +2 -1
  10. package/dist/core/domain/transport/schemas.d.ts +66 -66
  11. package/dist/core/interfaces/cipher/i-chat-session.d.ts +3 -1
  12. package/dist/core/interfaces/connectors/i-connector.d.ts +2 -2
  13. package/dist/core/interfaces/i-file-service.d.ts +7 -0
  14. package/dist/infra/auth/oauth-service.d.ts +15 -0
  15. package/dist/infra/auth/oauth-service.js +38 -2
  16. package/dist/infra/cipher/agent/agent-schemas.d.ts +42 -42
  17. package/dist/infra/cipher/llm/context/context-manager.js +7 -9
  18. package/dist/infra/cipher/llm/internal-llm-service.d.ts +5 -1
  19. package/dist/infra/cipher/llm/internal-llm-service.js +57 -46
  20. package/dist/infra/cipher/session/chat-session.d.ts +3 -1
  21. package/dist/infra/cipher/session/chat-session.js +5 -3
  22. package/dist/infra/cipher/system-prompt/contributor-schemas.d.ts +8 -8
  23. package/dist/infra/cipher/system-prompt/schemas.d.ts +5 -5
  24. package/dist/infra/cipher/tools/implementations/task-tool.js +3 -3
  25. package/dist/infra/connectors/connector-manager.js +2 -0
  26. package/dist/infra/connectors/hook/hook-connector.d.ts +1 -1
  27. package/dist/infra/connectors/hook/hook-connector.js +3 -3
  28. package/dist/infra/connectors/mcp/mcp-connector.d.ts +1 -1
  29. package/dist/infra/connectors/mcp/mcp-connector.js +4 -4
  30. package/dist/infra/connectors/rules/rules-connector.d.ts +1 -1
  31. package/dist/infra/connectors/rules/rules-connector.js +4 -4
  32. package/dist/infra/connectors/shared/template-service.js +4 -0
  33. package/dist/infra/connectors/skill/index.d.ts +1 -0
  34. package/dist/infra/connectors/skill/index.js +1 -0
  35. package/dist/infra/connectors/skill/skill-connector-config.d.ts +45 -0
  36. package/dist/infra/connectors/skill/skill-connector-config.js +26 -0
  37. package/dist/infra/connectors/skill/skill-connector.d.ts +39 -0
  38. package/dist/infra/connectors/skill/skill-connector.js +160 -0
  39. package/dist/infra/connectors/skill/skill-content-loader.d.ts +18 -0
  40. package/dist/infra/connectors/skill/skill-content-loader.js +33 -0
  41. package/dist/infra/file/fs-file-service.d.ts +7 -0
  42. package/dist/infra/file/fs-file-service.js +15 -1
  43. package/dist/infra/mcp/tools/task-result-waiter.js +8 -0
  44. package/dist/infra/process/agent-worker.js +30 -14
  45. package/dist/infra/process/task-queue-manager.d.ts +23 -34
  46. package/dist/infra/process/task-queue-manager.js +57 -118
  47. package/dist/infra/process/transport-handlers.js +1 -7
  48. package/dist/infra/repl/commands/connectors-command.js +1 -1
  49. package/dist/infra/transport/socket-io-transport-client.d.ts +9 -0
  50. package/dist/infra/transport/socket-io-transport-client.js +21 -2
  51. package/dist/infra/usecase/connectors-use-case.js +8 -2
  52. package/dist/infra/usecase/init-use-case.js +1 -1
  53. package/dist/infra/usecase/reset-use-case.d.ts +1 -0
  54. package/dist/infra/usecase/reset-use-case.js +4 -1
  55. package/dist/{commands → oclif/commands}/curate.d.ts +1 -1
  56. package/dist/{commands → oclif/commands}/curate.js +6 -6
  57. package/dist/{commands → oclif/commands}/hook-prompt-submit.d.ts +1 -1
  58. package/dist/{commands → oclif/commands}/hook-prompt-submit.js +3 -3
  59. package/dist/{commands → oclif/commands}/main.js +10 -10
  60. package/dist/{commands → oclif/commands}/mcp.js +2 -2
  61. package/dist/{commands → oclif/commands}/query.d.ts +1 -1
  62. package/dist/{commands → oclif/commands}/query.js +6 -6
  63. package/dist/{commands → oclif/commands}/status.d.ts +1 -1
  64. package/dist/{commands → oclif/commands}/status.js +8 -8
  65. package/dist/{commands → oclif/commands}/watch.d.ts +3 -3
  66. package/dist/{commands → oclif/commands}/watch.js +6 -6
  67. package/dist/oclif/constants.d.ts +11 -0
  68. package/dist/oclif/constants.js +11 -0
  69. package/dist/{hooks → oclif/hooks}/prerun/validate-brv-config-version.d.ts +1 -1
  70. package/dist/{hooks → oclif/hooks}/prerun/validate-brv-config-version.js +2 -2
  71. package/dist/templates/sections/command-reference.md +5 -96
  72. package/dist/templates/sections/workflow.md +21 -16
  73. package/dist/templates/skill/SKILL.md +91 -0
  74. package/dist/templates/skill/TROUBLESHOOTING.md +50 -0
  75. package/dist/templates/skill/WORKFLOWS.md +229 -0
  76. package/dist/utils/type-guards.d.ts +11 -0
  77. package/dist/utils/type-guards.js +13 -0
  78. package/oclif.manifest.json +8 -1
  79. package/package.json +9 -9
  80. package/dist/infra/process/constants.d.ts +0 -1
  81. package/dist/infra/process/constants.js +0 -1
  82. /package/dist/{commands → oclif/commands}/main.d.ts +0 -0
  83. /package/dist/{commands → oclif/commands}/mcp.d.ts +0 -0
  84. /package/dist/{hooks → oclif/hooks}/command_not_found/handle-invalid-commands.d.ts +0 -0
  85. /package/dist/{hooks → oclif/hooks}/command_not_found/handle-invalid-commands.js +0 -0
  86. /package/dist/{hooks → oclif/hooks}/error/clean-errors.d.ts +0 -0
  87. /package/dist/{hooks → oclif/hooks}/error/clean-errors.js +0 -0
  88. /package/dist/{hooks → oclif/hooks}/init/update-notifier.d.ts +0 -0
  89. /package/dist/{hooks → oclif/hooks}/init/update-notifier.js +0 -0
  90. /package/dist/{hooks → oclif/hooks}/init/welcome.d.ts +0 -0
  91. /package/dist/{hooks → oclif/hooks}/init/welcome.js +0 -0
@@ -11,18 +11,18 @@ export declare const LLMConfigSchema: z.ZodObject<{
11
11
  verbose: z.ZodDefault<z.ZodBoolean>;
12
12
  }, "strict", z.ZodTypeAny, {
13
13
  maxIterations: number;
14
- verbose: boolean;
15
- maxTokens: number;
16
14
  temperature: number;
15
+ maxTokens: number;
16
+ verbose: boolean;
17
17
  topK?: number | undefined;
18
18
  topP?: number | undefined;
19
19
  }, {
20
20
  maxIterations?: number | undefined;
21
- verbose?: boolean | undefined;
22
- maxTokens?: number | undefined;
23
21
  temperature?: number | undefined;
22
+ maxTokens?: number | undefined;
24
23
  topK?: number | undefined;
25
24
  topP?: number | undefined;
25
+ verbose?: boolean | undefined;
26
26
  }>;
27
27
  export type LLMConfig = z.input<typeof LLMConfigSchema>;
28
28
  export type ValidatedLLMConfig = z.output<typeof LLMConfigSchema>;
@@ -49,12 +49,12 @@ export declare const FileSystemConfigSchema: z.ZodObject<{
49
49
  maxFileSize: z.ZodOptional<z.ZodNumber>;
50
50
  workingDirectory: z.ZodOptional<z.ZodString>;
51
51
  }, "strict", z.ZodTypeAny, {
52
- workingDirectory?: string | undefined;
53
52
  allowedExtensions?: string[] | undefined;
53
+ workingDirectory?: string | undefined;
54
54
  maxFileSize?: number | undefined;
55
55
  }, {
56
- workingDirectory?: string | undefined;
57
56
  allowedExtensions?: string[] | undefined;
57
+ workingDirectory?: string | undefined;
58
58
  maxFileSize?: number | undefined;
59
59
  }>;
60
60
  export type FileSystemConfig = z.input<typeof FileSystemConfigSchema>;
@@ -67,13 +67,13 @@ export declare const BlobStorageConfigSchema: z.ZodObject<{
67
67
  maxTotalSize: z.ZodDefault<z.ZodNumber>;
68
68
  storageDir: z.ZodString;
69
69
  }, "strict", z.ZodTypeAny, {
70
- maxBlobSize: number;
71
70
  maxTotalSize: number;
71
+ maxBlobSize: number;
72
72
  storageDir: string;
73
73
  }, {
74
74
  storageDir: string;
75
- maxBlobSize?: number | undefined;
76
75
  maxTotalSize?: number | undefined;
76
+ maxBlobSize?: number | undefined;
77
77
  }>;
78
78
  export type BlobStorageConfig = z.input<typeof BlobStorageConfigSchema>;
79
79
  export type ValidatedBlobStorageConfig = z.output<typeof BlobStorageConfigSchema>;
@@ -92,25 +92,25 @@ export declare const AgentConfigSchema: z.ZodObject<{
92
92
  maxTotalSize: z.ZodDefault<z.ZodNumber>;
93
93
  storageDir: z.ZodString;
94
94
  }, "strict", z.ZodTypeAny, {
95
- maxBlobSize: number;
96
95
  maxTotalSize: number;
96
+ maxBlobSize: number;
97
97
  storageDir: string;
98
98
  }, {
99
99
  storageDir: string;
100
- maxBlobSize?: number | undefined;
101
100
  maxTotalSize?: number | undefined;
101
+ maxBlobSize?: number | undefined;
102
102
  }>>;
103
103
  fileSystem: z.ZodOptional<z.ZodObject<{
104
104
  allowedExtensions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
105
105
  maxFileSize: z.ZodOptional<z.ZodNumber>;
106
106
  workingDirectory: z.ZodOptional<z.ZodString>;
107
107
  }, "strict", z.ZodTypeAny, {
108
- workingDirectory?: string | undefined;
109
108
  allowedExtensions?: string[] | undefined;
109
+ workingDirectory?: string | undefined;
110
110
  maxFileSize?: number | undefined;
111
111
  }, {
112
- workingDirectory?: string | undefined;
113
112
  allowedExtensions?: string[] | undefined;
113
+ workingDirectory?: string | undefined;
114
114
  maxFileSize?: number | undefined;
115
115
  }>>;
116
116
  httpReferer: z.ZodOptional<z.ZodString>;
@@ -123,18 +123,18 @@ export declare const AgentConfigSchema: z.ZodObject<{
123
123
  verbose: z.ZodDefault<z.ZodBoolean>;
124
124
  }, "strict", z.ZodTypeAny, {
125
125
  maxIterations: number;
126
- verbose: boolean;
127
- maxTokens: number;
128
126
  temperature: number;
127
+ maxTokens: number;
128
+ verbose: boolean;
129
129
  topK?: number | undefined;
130
130
  topP?: number | undefined;
131
131
  }, {
132
132
  maxIterations?: number | undefined;
133
- verbose?: boolean | undefined;
134
- maxTokens?: number | undefined;
135
133
  temperature?: number | undefined;
134
+ maxTokens?: number | undefined;
136
135
  topK?: number | undefined;
137
136
  topP?: number | undefined;
137
+ verbose?: boolean | undefined;
138
138
  }>>;
139
139
  model: z.ZodString;
140
140
  openRouterApiKey: z.ZodOptional<z.ZodString>;
@@ -156,34 +156,34 @@ export declare const AgentConfigSchema: z.ZodObject<{
156
156
  teamId: z.ZodOptional<z.ZodString>;
157
157
  useGranularStorage: z.ZodDefault<z.ZodBoolean>;
158
158
  }, "strict", z.ZodTypeAny, {
159
- accessToken: string;
160
- sessionKey: string;
161
159
  model: string;
162
- sessions: {
163
- maxSessions: number;
164
- sessionTTL: number;
165
- };
166
- apiBaseUrl: string;
167
160
  llm: {
168
161
  maxIterations: number;
169
- verbose: boolean;
170
- maxTokens: number;
171
162
  temperature: number;
163
+ maxTokens: number;
164
+ verbose: boolean;
172
165
  topK?: number | undefined;
173
166
  topP?: number | undefined;
174
167
  };
168
+ sessions: {
169
+ maxSessions: number;
170
+ sessionTTL: number;
171
+ };
172
+ accessToken: string;
173
+ sessionKey: string;
174
+ apiBaseUrl: string;
175
175
  projectId: string;
176
176
  useGranularStorage: boolean;
177
177
  teamId?: string | undefined;
178
178
  spaceId?: string | undefined;
179
179
  blobStorage?: {
180
- maxBlobSize: number;
181
180
  maxTotalSize: number;
181
+ maxBlobSize: number;
182
182
  storageDir: string;
183
183
  } | undefined;
184
184
  fileSystem?: {
185
- workingDirectory?: string | undefined;
186
185
  allowedExtensions?: string[] | undefined;
186
+ workingDirectory?: string | undefined;
187
187
  maxFileSize?: number | undefined;
188
188
  } | undefined;
189
189
  httpReferer?: string | undefined;
@@ -191,11 +191,19 @@ export declare const AgentConfigSchema: z.ZodObject<{
191
191
  region?: string | undefined;
192
192
  siteName?: string | undefined;
193
193
  }, {
194
+ model: string;
194
195
  accessToken: string;
195
196
  sessionKey: string;
196
- model: string;
197
197
  apiBaseUrl: string;
198
198
  projectId: string;
199
+ llm?: {
200
+ maxIterations?: number | undefined;
201
+ temperature?: number | undefined;
202
+ maxTokens?: number | undefined;
203
+ topK?: number | undefined;
204
+ topP?: number | undefined;
205
+ verbose?: boolean | undefined;
206
+ } | undefined;
199
207
  sessions?: {
200
208
  maxSessions?: number | undefined;
201
209
  sessionTTL?: number | undefined;
@@ -204,23 +212,15 @@ export declare const AgentConfigSchema: z.ZodObject<{
204
212
  spaceId?: string | undefined;
205
213
  blobStorage?: {
206
214
  storageDir: string;
207
- maxBlobSize?: number | undefined;
208
215
  maxTotalSize?: number | undefined;
216
+ maxBlobSize?: number | undefined;
209
217
  } | undefined;
210
218
  fileSystem?: {
211
- workingDirectory?: string | undefined;
212
219
  allowedExtensions?: string[] | undefined;
220
+ workingDirectory?: string | undefined;
213
221
  maxFileSize?: number | undefined;
214
222
  } | undefined;
215
223
  httpReferer?: string | undefined;
216
- llm?: {
217
- maxIterations?: number | undefined;
218
- verbose?: boolean | undefined;
219
- maxTokens?: number | undefined;
220
- temperature?: number | undefined;
221
- topK?: number | undefined;
222
- topP?: number | undefined;
223
- } | undefined;
224
224
  openRouterApiKey?: string | undefined;
225
225
  region?: string | undefined;
226
226
  siteName?: string | undefined;
@@ -241,15 +241,15 @@ export declare const LLMUpdatesSchema: z.ZodObject<{
241
241
  }, "strict", z.ZodTypeAny, {
242
242
  maxIterations?: number | undefined;
243
243
  model?: string | undefined;
244
- verbose?: boolean | undefined;
245
- maxTokens?: number | undefined;
246
244
  temperature?: number | undefined;
245
+ maxTokens?: number | undefined;
246
+ verbose?: boolean | undefined;
247
247
  }, {
248
248
  maxIterations?: number | undefined;
249
249
  model?: string | undefined;
250
- verbose?: boolean | undefined;
251
- maxTokens?: number | undefined;
252
250
  temperature?: number | undefined;
251
+ maxTokens?: number | undefined;
252
+ verbose?: boolean | undefined;
253
253
  }>;
254
254
  export type LLMUpdates = z.input<typeof LLMUpdatesSchema>;
255
255
  /**
@@ -200,15 +200,13 @@ export class ContextManager {
200
200
  content,
201
201
  role: 'user',
202
202
  };
203
- await this.mutex.withLock(async () => {
204
- this.messages.push(message);
205
- try {
206
- await this.persistHistory();
207
- }
208
- catch (error) {
209
- this.logger.error('Failed to persist history after user message', { error, sessionId: this.sessionId });
210
- }
211
- });
203
+ this.messages.push(message);
204
+ try {
205
+ await this.persistHistory();
206
+ }
207
+ catch (error) {
208
+ this.logger.error('Failed to persist history after user message', { error, sessionId: this.sessionId });
209
+ }
212
210
  }
213
211
  /**
214
212
  * Clear all messages from the conversation history.
@@ -72,6 +72,7 @@ export declare class ByteRoverLLMService implements ILLMService {
72
72
  private readonly loopDetector;
73
73
  private readonly memoryManager?;
74
74
  private readonly metadataHandler;
75
+ private readonly mutex;
75
76
  private readonly outputProcessor;
76
77
  private readonly providerType;
77
78
  private readonly sessionEventBus;
@@ -257,10 +258,13 @@ export declare class ByteRoverLLMService implements ILLMService {
257
258
  * Execute a single iteration of the agentic loop.
258
259
  *
259
260
  * @param options - Iteration options
261
+ * @param options.executionContext - Optional execution context
262
+ * @param options.fileData - Optional file data (only used on first iteration)
263
+ * @param options.imageData - Optional image data (only used on first iteration)
260
264
  * @param options.iterationCount - Current iteration number
261
265
  * @param options.taskId - Task ID from usecase for billing tracking
266
+ * @param options.textInput - User input text (only used on first iteration)
262
267
  * @param options.tools - Available tools for this iteration
263
- * @param options.executionContext - Optional execution context
264
268
  * @returns Final response string if complete, null if more iterations needed
265
269
  */
266
270
  private executeAgenticIteration;
@@ -6,6 +6,7 @@ import { NoOpLogger } from '../../../core/interfaces/cipher/i-logger.js';
6
6
  import { getErrorMessage } from '../../../utils/error-helpers.js';
7
7
  import { EnvironmentContextBuilder } from '../system-prompt/environment-context-builder.js';
8
8
  import { ToolMetadataHandler } from '../tools/streaming/metadata-handler.js';
9
+ import { AsyncMutex } from './context/async-mutex.js';
9
10
  import { ContextManager } from './context/context-manager.js';
10
11
  import { LoopDetector } from './context/loop-detector.js';
11
12
  import { ClaudeMessageFormatter } from './formatters/claude-formatter.js';
@@ -44,6 +45,7 @@ export class ByteRoverLLMService {
44
45
  loopDetector;
45
46
  memoryManager;
46
47
  metadataHandler;
48
+ mutex = new AsyncMutex();
47
49
  outputProcessor;
48
50
  providerType;
49
51
  sessionEventBus;
@@ -147,8 +149,6 @@ export class ByteRoverLLMService {
147
149
  async completeTask(textInput, options) {
148
150
  // Extract options with defaults
149
151
  const { executionContext, fileData, imageData, signal, taskId } = options ?? {};
150
- // Add user message to context
151
- await this.contextManager.addUserMessage(textInput, imageData, fileData);
152
152
  // Get filtered tools based on command type (e.g., only read-only tools for 'query')
153
153
  const toolSet = this.toolManager.getToolsForCommand(options?.executionContext?.commandType);
154
154
  // Create state machine with configured limits
@@ -171,8 +171,11 @@ export class ByteRoverLLMService {
171
171
  // eslint-disable-next-line no-await-in-loop -- Sequential iterations required for agentic loop
172
172
  const result = await this.executeAgenticIteration({
173
173
  executionContext,
174
+ fileData,
175
+ imageData,
174
176
  iterationCount: stateMachine.getContext().turnCount,
175
177
  taskId,
178
+ textInput,
176
179
  tools: toolSet,
177
180
  });
178
181
  if (result !== null) {
@@ -477,14 +480,17 @@ export class ByteRoverLLMService {
477
480
  * Execute a single iteration of the agentic loop.
478
481
  *
479
482
  * @param options - Iteration options
483
+ * @param options.executionContext - Optional execution context
484
+ * @param options.fileData - Optional file data (only used on first iteration)
485
+ * @param options.imageData - Optional image data (only used on first iteration)
480
486
  * @param options.iterationCount - Current iteration number
481
487
  * @param options.taskId - Task ID from usecase for billing tracking
488
+ * @param options.textInput - User input text (only used on first iteration)
482
489
  * @param options.tools - Available tools for this iteration
483
- * @param options.executionContext - Optional execution context
484
490
  * @returns Final response string if complete, null if more iterations needed
485
491
  */
486
492
  async executeAgenticIteration(options) {
487
- const { executionContext, iterationCount, taskId, tools } = options;
493
+ const { executionContext, fileData, imageData, iterationCount, taskId, textInput, tools } = options;
488
494
  // Build system prompt using SystemPromptManager (before compression for correct token accounting)
489
495
  // Use filtered tool names based on command type (e.g., only read-only tools for 'query')
490
496
  const availableTools = this.toolManager.getToolNamesForCommand(executionContext?.commandType);
@@ -533,54 +539,59 @@ export class ByteRoverLLMService {
533
539
  reflectionType,
534
540
  });
535
541
  }
536
- // Get token count for logging (using system prompt for token accounting)
537
- const systemPromptTokens = this.generator.estimateTokensSync(systemPrompt);
538
- const messages = this.contextManager.getMessages();
539
- const messageTokenCounts = messages.map((msg) => this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)));
540
- const maxMessageTokens = this.config.maxInputTokens - systemPromptTokens;
541
- // Target utilization to leave headroom for response
542
- const targetMessageTokens = Math.floor(maxMessageTokens * TARGET_MESSAGE_TOKEN_UTILIZATION);
543
- this.contextManager.compressMessage(targetMessageTokens, messageTokenCounts);
544
- // Calculate tokens after compression
545
- const compressedMessagesTokens = this.contextManager
546
- .getMessages()
547
- .reduce((total, msg) => total +
548
- this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
549
- const tokensUsed = systemPromptTokens + compressedMessagesTokens;
550
- // Verbose: Log messages that will be sent to LLM
551
- if (this.config.verbose) {
552
- console.log('\n========== MESSAGES (Sent to LLM) ==========');
553
- console.log(JSON.stringify(this.contextManager.getMessages(), null, 2));
554
- console.log('========== END MESSAGES ==========\n');
555
- // Log token usage for monitoring compression behavior
556
- console.log(`[ByteRoverLLMService] [Iter ${iterationCount + 1}/${this.config.maxIterations}] Sending to LLM: ${tokensUsed} tokens (max: ${this.config.maxInputTokens})`);
557
- }
558
542
  // Final iteration optimization for query: strip tools (reflection already added above)
559
543
  let toolsForThisIteration = tools;
560
544
  if (executionContext?.commandType === 'query' && iterationCount === this.config.maxIterations - 1) {
561
545
  toolsForThisIteration = {}; // Empty toolset forces text response
562
546
  }
563
- // Build generation request
564
- const request = this.buildGenerateContentRequest({
565
- executionContext,
566
- systemPrompt,
567
- taskId,
568
- tools: toolsForThisIteration,
569
- });
570
- // Call LLM via generator (retry + logging handled by decorators)
571
- const lastMessage = await this.callLLMAndParseResponse(request);
572
- // Check if there are tool calls
573
- if (!lastMessage.toolCalls || lastMessage.toolCalls.length === 0) {
574
- const response = await this.handleFinalResponse(lastMessage, taskId);
575
- // Auto-compaction check after assistant response
547
+ // Get token count for logging (using system prompt for token accounting)
548
+ const systemPromptTokens = this.generator.estimateTokensSync(systemPrompt);
549
+ // Add user message and compress context within mutex lock
550
+ return this.mutex.withLock(async () => {
551
+ // Add user message to context only on the first iteration
552
+ await this.contextManager.addUserMessage(textInput, imageData, fileData);
553
+ const messages = this.contextManager.getMessages();
554
+ const messageTokenCounts = messages.map((msg) => this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)));
555
+ const maxMessageTokens = this.config.maxInputTokens - systemPromptTokens;
556
+ // Target utilization to leave headroom for response
557
+ const targetMessageTokens = Math.floor(maxMessageTokens * TARGET_MESSAGE_TOKEN_UTILIZATION);
558
+ this.contextManager.compressMessage(targetMessageTokens, messageTokenCounts);
559
+ // Calculate tokens after compression
560
+ const compressedMessagesTokens = this.contextManager
561
+ .getMessages()
562
+ .reduce((total, msg) => total +
563
+ this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
564
+ const tokensUsed = systemPromptTokens + compressedMessagesTokens;
565
+ // Verbose: Log messages that will be sent to LLM
566
+ if (this.config.verbose) {
567
+ console.log('\n========== MESSAGES (Sent to LLM) ==========');
568
+ console.log(JSON.stringify(this.contextManager.getMessages(), null, 2));
569
+ console.log('========== END MESSAGES ==========\n');
570
+ // Log token usage for monitoring compression behavior
571
+ console.log(`[ByteRoverLLMService] [Iter ${iterationCount + 1}/${this.config.maxIterations}] Sending to LLM: ${tokensUsed} tokens (max: ${this.config.maxInputTokens})`);
572
+ }
573
+ // Build generation request
574
+ const request = this.buildGenerateContentRequest({
575
+ executionContext,
576
+ systemPrompt,
577
+ taskId,
578
+ tools: toolsForThisIteration,
579
+ });
580
+ // Call LLM via generator (retry + logging handled by decorators)
581
+ const lastMessage = await this.callLLMAndParseResponse(request);
582
+ // Check if there are tool calls
583
+ if (!lastMessage.toolCalls || lastMessage.toolCalls.length === 0) {
584
+ const response = await this.handleFinalResponse(lastMessage, taskId);
585
+ // Auto-compaction check after assistant response
586
+ await this.checkAndTriggerCompaction(taskId ?? '');
587
+ return response;
588
+ }
589
+ // Has tool calls - handle them (pass taskId for subagent billing)
590
+ await this.handleToolCalls(lastMessage, taskId);
591
+ // Auto-compaction check after tool execution batch
576
592
  await this.checkAndTriggerCompaction(taskId ?? '');
577
- return response;
578
- }
579
- // Has tool calls - handle them (pass taskId for subagent billing)
580
- await this.handleToolCalls(lastMessage, taskId);
581
- // Auto-compaction check after tool execution batch
582
- await this.checkAndTriggerCompaction(taskId ?? '');
583
- return null;
593
+ return null;
594
+ });
584
595
  }
585
596
  /**
586
597
  * Execute a single tool call in parallel (without adding to context).
@@ -84,9 +84,11 @@ export declare class ChatSession implements IChatSession {
84
84
  * @param input - User message
85
85
  * @param options - Execution options
86
86
  * @param options.executionContext - Optional execution context
87
- * @param options.taskId - Optional task ID for concurrent task isolation
87
+ * @param options.taskId - Optional task ID for billing tracking
88
+ * @param options.emitTaskId - Whether to include taskId in emitted events (default: true)
88
89
  */
89
90
  run(input: string, options?: {
91
+ emitTaskId?: boolean;
90
92
  executionContext?: ExecutionContext;
91
93
  taskId?: string;
92
94
  }): Promise<string>;
@@ -168,10 +168,12 @@ export class ChatSession {
168
168
  * @param input - User message
169
169
  * @param options - Execution options
170
170
  * @param options.executionContext - Optional execution context
171
- * @param options.taskId - Optional task ID for concurrent task isolation
171
+ * @param options.taskId - Optional task ID for billing tracking
172
+ * @param options.emitTaskId - Whether to include taskId in emitted events (default: true)
172
173
  */
173
174
  async run(input, options) {
174
175
  const taskId = options?.taskId;
176
+ const emitTaskId = options?.emitTaskId !== false;
175
177
  const controller = new AbortController();
176
178
  // Track controller per-task for concurrent execution support
177
179
  if (taskId) {
@@ -180,8 +182,8 @@ export class ChatSession {
180
182
  else {
181
183
  this.currentController = controller;
182
184
  }
183
- // Store taskId for event forwarding (last-write-wins for concurrent tasks)
184
- this.currentTaskId = taskId;
185
+ // Store taskId for event forwarding only if emitTaskId is true
186
+ this.currentTaskId = emitTaskId ? taskId : undefined;
185
187
  this.isExecuting = true;
186
188
  sessionStatusManager.setBusy(this.id, this.eventBus);
187
189
  try {
@@ -12,14 +12,14 @@ export declare const StaticContributorConfigSchema: z.ZodObject<{
12
12
  type: z.ZodLiteral<"static">;
13
13
  }, "strict", z.ZodTypeAny, {
14
14
  type: "static";
15
- content: string;
16
15
  id: string;
16
+ content: string;
17
17
  enabled: boolean;
18
18
  priority: number;
19
19
  }, {
20
20
  type: "static";
21
- content: string;
22
21
  id: string;
22
+ content: string;
23
23
  priority: number;
24
24
  enabled?: boolean | undefined;
25
25
  }>;
@@ -179,14 +179,14 @@ export declare const ContributorConfigSchema: z.ZodDiscriminatedUnion<"type", [z
179
179
  type: z.ZodLiteral<"static">;
180
180
  }, "strict", z.ZodTypeAny, {
181
181
  type: "static";
182
- content: string;
183
182
  id: string;
183
+ content: string;
184
184
  enabled: boolean;
185
185
  priority: number;
186
186
  }, {
187
187
  type: "static";
188
- content: string;
189
188
  id: string;
189
+ content: string;
190
190
  priority: number;
191
191
  enabled?: boolean | undefined;
192
192
  }>, z.ZodObject<{
@@ -311,14 +311,14 @@ export declare const SystemPromptManagerConfigSchema: z.ZodObject<{
311
311
  type: z.ZodLiteral<"static">;
312
312
  }, "strict", z.ZodTypeAny, {
313
313
  type: "static";
314
- content: string;
315
314
  id: string;
315
+ content: string;
316
316
  enabled: boolean;
317
317
  priority: number;
318
318
  }, {
319
319
  type: "static";
320
- content: string;
321
320
  id: string;
321
+ content: string;
322
322
  priority: number;
323
323
  enabled?: boolean | undefined;
324
324
  }>, z.ZodObject<{
@@ -433,8 +433,8 @@ export declare const SystemPromptManagerConfigSchema: z.ZodObject<{
433
433
  }, "strict", z.ZodTypeAny, {
434
434
  contributors: ({
435
435
  type: "static";
436
- content: string;
437
436
  id: string;
437
+ content: string;
438
438
  enabled: boolean;
439
439
  priority: number;
440
440
  } | {
@@ -471,8 +471,8 @@ export declare const SystemPromptManagerConfigSchema: z.ZodObject<{
471
471
  }, {
472
472
  contributors: ({
473
473
  type: "static";
474
- content: string;
475
474
  id: string;
475
+ content: string;
476
476
  priority: number;
477
477
  enabled?: boolean | undefined;
478
478
  } | {
@@ -12,28 +12,28 @@ export declare const PromptConfigSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
12
12
  prompt: z.ZodOptional<z.ZodString>;
13
13
  prompts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
14
14
  }, "strict", z.ZodTypeAny, {
15
- prompt?: string | undefined;
16
15
  description?: string | undefined;
16
+ prompt?: string | undefined;
17
17
  excludedTools?: string[] | undefined;
18
18
  prompts?: Record<string, string> | undefined;
19
19
  }, {
20
- prompt?: string | undefined;
21
20
  description?: string | undefined;
21
+ prompt?: string | undefined;
22
22
  excludedTools?: string[] | undefined;
23
23
  prompts?: Record<string, string> | undefined;
24
24
  }>, {
25
- prompt?: string | undefined;
26
25
  description?: string | undefined;
26
+ prompt?: string | undefined;
27
27
  excludedTools?: string[] | undefined;
28
28
  prompts?: Record<string, string> | undefined;
29
29
  }, {
30
- prompt?: string | undefined;
31
30
  description?: string | undefined;
31
+ prompt?: string | undefined;
32
32
  excludedTools?: string[] | undefined;
33
33
  prompts?: Record<string, string> | undefined;
34
34
  }>, {
35
- prompt?: string | undefined;
36
35
  description?: string | undefined;
36
+ prompt?: string | undefined;
37
37
  excludedTools?: string[] | undefined;
38
38
  prompts?: Record<string, string> | undefined;
39
39
  }, unknown>;
@@ -95,7 +95,7 @@ export function createTaskTool(dependencies) {
95
95
  const registry = getAgentRegistry();
96
96
  return {
97
97
  description: buildTaskToolDescription(registry),
98
- // eslint-disable-next-line complexity -- Inherent complexity: validates agent, manages sessions, handles errors
98
+ // eslint-disable-next-line complexity
99
99
  async execute(input, context) {
100
100
  const params = input;
101
101
  const { contextTreeOnly, description, prompt, sessionId, subagentType } = params;
@@ -181,8 +181,8 @@ export function createTaskTool(dependencies) {
181
181
  progress: 10,
182
182
  });
183
183
  }
184
- // Execute the subagent session with parent's taskId for billing tracking
185
- const response = await session.run(fullPrompt, { taskId: context?.taskId });
184
+ // Pass taskId for billing but don't emit it in events (ENG-917)
185
+ const response = await session.run(fullPrompt, { emitTaskId: false, taskId: context?.taskId });
186
186
  // Stream completion update
187
187
  if (context?.metadata) {
188
188
  context.metadata({
@@ -3,6 +3,7 @@ import { CONNECTOR_TYPES } from '../../core/domain/entities/connector-type.js';
3
3
  import { HookConnector } from './hook/hook-connector.js';
4
4
  import { McpConnector } from './mcp/mcp-connector.js';
5
5
  import { RulesConnector } from './rules/rules-connector.js';
6
+ import { SkillConnector } from './skill/skill-connector.js';
6
7
  /**
7
8
  * Factory and orchestration layer for connectors.
8
9
  * Creates connector instances and manages connector operations.
@@ -16,6 +17,7 @@ export class ConnectorManager {
16
17
  ['hook', new HookConnector({ fileService, projectRoot })],
17
18
  ['mcp', new McpConnector({ fileService, projectRoot, templateService })],
18
19
  ['rules', new RulesConnector({ fileService, projectRoot, templateService })],
20
+ ['skill', new SkillConnector({ fileService, projectRoot })],
19
21
  ]);
20
22
  }
21
23
  async getAllInstalledConnectors() {
@@ -21,7 +21,7 @@ type HookConnectorOptions = {
21
21
  * - Safe uninstall: Only removes ByteRover hooks by command match
22
22
  */
23
23
  export declare class HookConnector implements IConnector {
24
- readonly type: ConnectorType;
24
+ readonly connectorType: ConnectorType;
25
25
  private readonly fileService;
26
26
  private readonly projectRoot;
27
27
  private readonly supportedAgents;
@@ -23,7 +23,7 @@ function parseJsonAsRecord(content) {
23
23
  * - Safe uninstall: Only removes ByteRover hooks by command match
24
24
  */
25
25
  export class HookConnector {
26
- type = 'hook';
26
+ connectorType = 'hook';
27
27
  fileService;
28
28
  projectRoot;
29
29
  supportedAgents;
@@ -31,7 +31,7 @@ export class HookConnector {
31
31
  this.fileService = options.fileService;
32
32
  this.projectRoot = options.projectRoot;
33
33
  this.supportedAgents = Object.entries(AGENT_CONNECTOR_CONFIG)
34
- .filter(([_, config]) => config.supported.includes(this.type))
34
+ .filter(([_, config]) => config.supported.includes(this.connectorType))
35
35
  .map(([agent]) => agent);
36
36
  }
37
37
  getConfigPath(agent) {
@@ -104,7 +104,7 @@ export class HookConnector {
104
104
  }
105
105
  }
106
106
  isSupported(agent) {
107
- return AGENT_CONNECTOR_CONFIG[agent].supported.includes(this.type);
107
+ return AGENT_CONNECTOR_CONFIG[agent].supported.includes(this.connectorType);
108
108
  }
109
109
  async status(agent) {
110
110
  if (!this.isSupported(agent)) {
@@ -24,7 +24,7 @@ type McpConnectorOptions = {
24
24
  * - Safe uninstall: Only removes ByteRover's MCP server entry and rule content
25
25
  */
26
26
  export declare class McpConnector implements IConnector {
27
- readonly type: ConnectorType;
27
+ readonly connectorType: ConnectorType;
28
28
  private readonly fileService;
29
29
  private readonly projectRoot;
30
30
  private readonly ruleFileManager;