@vybestack/llxprt-code-core 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.
Files changed (213) hide show
  1. package/dist/prompt-config/defaults/default-prompts.json +4 -17
  2. package/dist/src/auth/precedence.d.ts +69 -9
  3. package/dist/src/auth/precedence.js +467 -69
  4. package/dist/src/auth/precedence.js.map +1 -1
  5. package/dist/src/auth/types.d.ts +2 -2
  6. package/dist/src/config/config.d.ts +15 -1
  7. package/dist/src/config/config.js +118 -6
  8. package/dist/src/config/config.js.map +1 -1
  9. package/dist/src/config/index.d.ts +6 -0
  10. package/dist/src/config/index.js +5 -0
  11. package/dist/src/config/index.js.map +1 -1
  12. package/dist/src/config/profileManager.d.ts +23 -3
  13. package/dist/src/config/profileManager.js +54 -7
  14. package/dist/src/config/profileManager.js.map +1 -1
  15. package/dist/src/config/subagentManager.d.ts +96 -0
  16. package/dist/src/config/subagentManager.js +371 -0
  17. package/dist/src/config/subagentManager.js.map +1 -0
  18. package/dist/src/config/types.d.ts +18 -0
  19. package/dist/src/config/types.js +3 -0
  20. package/dist/src/config/types.js.map +1 -0
  21. package/dist/src/core/client.d.ts +27 -7
  22. package/dist/src/core/client.js +231 -55
  23. package/dist/src/core/client.js.map +1 -1
  24. package/dist/src/core/contentGenerator.d.ts +3 -1
  25. package/dist/src/core/contentGenerator.js +3 -0
  26. package/dist/src/core/contentGenerator.js.map +1 -1
  27. package/dist/src/core/coreToolScheduler.d.ts +1 -5
  28. package/dist/src/core/coreToolScheduler.js +95 -23
  29. package/dist/src/core/coreToolScheduler.js.map +1 -1
  30. package/dist/src/core/geminiChat.d.ts +42 -12
  31. package/dist/src/core/geminiChat.js +405 -205
  32. package/dist/src/core/geminiChat.js.map +1 -1
  33. package/dist/src/core/nonInteractiveToolExecutor.d.ts +3 -2
  34. package/dist/src/core/nonInteractiveToolExecutor.js +94 -10
  35. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  36. package/dist/src/core/subagent.d.ts +86 -7
  37. package/dist/src/core/subagent.js +809 -79
  38. package/dist/src/core/subagent.js.map +1 -1
  39. package/dist/src/core/subagentOrchestrator.d.ts +73 -0
  40. package/dist/src/core/subagentOrchestrator.js +383 -0
  41. package/dist/src/core/subagentOrchestrator.js.map +1 -0
  42. package/dist/src/core/subagentScheduler.d.ts +16 -0
  43. package/dist/src/core/subagentScheduler.js +7 -0
  44. package/dist/src/core/subagentScheduler.js.map +1 -0
  45. package/dist/src/core/turn.d.ts +5 -1
  46. package/dist/src/core/turn.js +5 -1
  47. package/dist/src/core/turn.js.map +1 -1
  48. package/dist/src/hooks/tool-render-suppression-hook.js +6 -1
  49. package/dist/src/hooks/tool-render-suppression-hook.js.map +1 -1
  50. package/dist/src/ide/ideContext.d.ts +32 -32
  51. package/dist/src/index.d.ts +19 -1
  52. package/dist/src/index.js +15 -2
  53. package/dist/src/index.js.map +1 -1
  54. package/dist/src/interfaces/index.d.ts +1 -0
  55. package/dist/src/interfaces/index.js +4 -0
  56. package/dist/src/interfaces/index.js.map +1 -0
  57. package/dist/src/interfaces/nodejs-error.interface.d.ts +4 -0
  58. package/dist/src/interfaces/nodejs-error.interface.js +2 -0
  59. package/dist/src/interfaces/nodejs-error.interface.js.map +1 -0
  60. package/dist/src/parsers/TextToolCallParser.d.ts +17 -1
  61. package/dist/src/parsers/TextToolCallParser.js +542 -148
  62. package/dist/src/parsers/TextToolCallParser.js.map +1 -1
  63. package/dist/src/prompt-config/defaults/core.md +15 -0
  64. package/dist/src/prompt-config/defaults/providers/gemini/core.md +203 -119
  65. package/dist/src/prompt-config/defaults/tool-defaults.js +2 -0
  66. package/dist/src/prompt-config/defaults/tool-defaults.js.map +1 -1
  67. package/dist/src/prompt-config/defaults/tools/list-subagents.md +7 -0
  68. package/dist/src/prompt-config/defaults/tools/task.md +8 -0
  69. package/dist/src/providers/BaseProvider.d.ts +115 -30
  70. package/dist/src/providers/BaseProvider.js +445 -109
  71. package/dist/src/providers/BaseProvider.js.map +1 -1
  72. package/dist/src/providers/IProvider.d.ts +50 -18
  73. package/dist/src/providers/LoggingProviderWrapper.d.ts +60 -16
  74. package/dist/src/providers/LoggingProviderWrapper.js +213 -60
  75. package/dist/src/providers/LoggingProviderWrapper.js.map +1 -1
  76. package/dist/src/providers/ProviderManager.d.ts +73 -2
  77. package/dist/src/providers/ProviderManager.js +492 -40
  78. package/dist/src/providers/ProviderManager.js.map +1 -1
  79. package/dist/src/providers/anthropic/AnthropicProvider.d.ts +35 -38
  80. package/dist/src/providers/anthropic/AnthropicProvider.js +222 -227
  81. package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
  82. package/dist/src/providers/errors.d.ts +86 -0
  83. package/dist/src/providers/errors.js +89 -0
  84. package/dist/src/providers/errors.js.map +1 -1
  85. package/dist/src/providers/gemini/GeminiProvider.d.ts +101 -41
  86. package/dist/src/providers/gemini/GeminiProvider.js +386 -311
  87. package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
  88. package/dist/src/providers/openai/ConversationCache.d.ts +5 -3
  89. package/dist/src/providers/openai/ConversationCache.js +93 -32
  90. package/dist/src/providers/openai/ConversationCache.js.map +1 -1
  91. package/dist/src/providers/openai/OpenAIProvider.d.ts +82 -42
  92. package/dist/src/providers/openai/OpenAIProvider.js +391 -536
  93. package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
  94. package/dist/src/providers/openai/getOpenAIProviderInfo.d.ts +1 -1
  95. package/dist/src/providers/openai/getOpenAIProviderInfo.js +52 -22
  96. package/dist/src/providers/openai/getOpenAIProviderInfo.js.map +1 -1
  97. package/dist/src/providers/openai/openaiRequestParams.d.ts +7 -0
  98. package/dist/src/providers/openai/openaiRequestParams.js +66 -0
  99. package/dist/src/providers/openai/openaiRequestParams.js.map +1 -0
  100. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.d.ts +6 -33
  101. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js +84 -183
  102. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js.map +1 -1
  103. package/dist/src/providers/types/providerRuntime.d.ts +17 -0
  104. package/dist/src/providers/types/providerRuntime.js +7 -0
  105. package/dist/src/providers/types/providerRuntime.js.map +1 -0
  106. package/dist/src/providers/utils/authToken.d.ts +12 -0
  107. package/dist/src/providers/utils/authToken.js +17 -0
  108. package/dist/src/providers/utils/authToken.js.map +1 -0
  109. package/dist/src/providers/utils/userMemory.d.ts +8 -0
  110. package/dist/src/providers/utils/userMemory.js +34 -0
  111. package/dist/src/providers/utils/userMemory.js.map +1 -0
  112. package/dist/src/runtime/AgentRuntimeContext.d.ts +213 -0
  113. package/dist/src/runtime/AgentRuntimeContext.js +17 -0
  114. package/dist/src/runtime/AgentRuntimeContext.js.map +1 -0
  115. package/dist/src/runtime/AgentRuntimeLoader.d.ts +47 -0
  116. package/dist/src/runtime/AgentRuntimeLoader.js +122 -0
  117. package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -0
  118. package/dist/src/runtime/AgentRuntimeState.d.ts +232 -0
  119. package/dist/src/runtime/AgentRuntimeState.js +439 -0
  120. package/dist/src/runtime/AgentRuntimeState.js.map +1 -0
  121. package/dist/src/runtime/RuntimeInvocationContext.d.ts +51 -0
  122. package/dist/src/runtime/RuntimeInvocationContext.js +52 -0
  123. package/dist/src/runtime/RuntimeInvocationContext.js.map +1 -0
  124. package/dist/src/runtime/createAgentRuntimeContext.d.ts +7 -0
  125. package/dist/src/runtime/createAgentRuntimeContext.js +65 -0
  126. package/dist/src/runtime/createAgentRuntimeContext.js.map +1 -0
  127. package/dist/src/runtime/index.d.ts +13 -0
  128. package/dist/src/runtime/index.js +14 -0
  129. package/dist/src/runtime/index.js.map +1 -0
  130. package/dist/src/runtime/providerRuntimeContext.d.ts +30 -0
  131. package/dist/src/runtime/providerRuntimeContext.js +70 -0
  132. package/dist/src/runtime/providerRuntimeContext.js.map +1 -0
  133. package/dist/src/runtime/runtimeAdapters.d.ts +22 -0
  134. package/dist/src/runtime/runtimeAdapters.js +81 -0
  135. package/dist/src/runtime/runtimeAdapters.js.map +1 -0
  136. package/dist/src/runtime/runtimeStateFactory.d.ts +21 -0
  137. package/dist/src/runtime/runtimeStateFactory.js +104 -0
  138. package/dist/src/runtime/runtimeStateFactory.js.map +1 -0
  139. package/dist/src/services/todo-context-tracker.d.ts +10 -8
  140. package/dist/src/services/todo-context-tracker.js +26 -10
  141. package/dist/src/services/todo-context-tracker.js.map +1 -1
  142. package/dist/src/services/tool-call-tracker-service.d.ts +11 -7
  143. package/dist/src/services/tool-call-tracker-service.js +89 -29
  144. package/dist/src/services/tool-call-tracker-service.js.map +1 -1
  145. package/dist/src/settings/SettingsService.d.ts +4 -0
  146. package/dist/src/settings/SettingsService.js +65 -2
  147. package/dist/src/settings/SettingsService.js.map +1 -1
  148. package/dist/src/settings/settingsServiceInstance.d.ts +6 -1
  149. package/dist/src/settings/settingsServiceInstance.js +28 -8
  150. package/dist/src/settings/settingsServiceInstance.js.map +1 -1
  151. package/dist/src/telemetry/loggers.d.ts +5 -1
  152. package/dist/src/telemetry/loggers.js.map +1 -1
  153. package/dist/src/telemetry/loggers.test.circular.js +4 -0
  154. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  155. package/dist/src/telemetry/metrics.d.ts +3 -1
  156. package/dist/src/telemetry/metrics.js.map +1 -1
  157. package/dist/src/telemetry/types.d.ts +1 -0
  158. package/dist/src/telemetry/types.js +3 -0
  159. package/dist/src/telemetry/types.js.map +1 -1
  160. package/dist/src/test-utils/index.d.ts +2 -0
  161. package/dist/src/test-utils/index.js +2 -0
  162. package/dist/src/test-utils/index.js.map +1 -1
  163. package/dist/src/test-utils/mockWorkspaceContext.d.ts +0 -3
  164. package/dist/src/test-utils/mockWorkspaceContext.js +3 -4
  165. package/dist/src/test-utils/mockWorkspaceContext.js.map +1 -1
  166. package/dist/src/test-utils/providerCallOptions.d.ts +43 -0
  167. package/dist/src/test-utils/providerCallOptions.js +137 -0
  168. package/dist/src/test-utils/providerCallOptions.js.map +1 -0
  169. package/dist/src/test-utils/runtime.d.ts +92 -0
  170. package/dist/src/test-utils/runtime.js +226 -0
  171. package/dist/src/test-utils/runtime.js.map +1 -0
  172. package/dist/src/test-utils/tools.d.ts +4 -4
  173. package/dist/src/test-utils/tools.js +20 -10
  174. package/dist/src/test-utils/tools.js.map +1 -1
  175. package/dist/src/tools/list-subagents.d.ts +31 -0
  176. package/dist/src/tools/list-subagents.js +109 -0
  177. package/dist/src/tools/list-subagents.js.map +1 -0
  178. package/dist/src/tools/task.d.ts +87 -0
  179. package/dist/src/tools/task.js +427 -0
  180. package/dist/src/tools/task.js.map +1 -0
  181. package/dist/src/tools/todo-read.js +1 -1
  182. package/dist/src/tools/todo-read.js.map +1 -1
  183. package/dist/src/tools/todo-store.js +4 -2
  184. package/dist/src/tools/todo-store.js.map +1 -1
  185. package/dist/src/tools/todo-write.js +4 -2
  186. package/dist/src/tools/todo-write.js.map +1 -1
  187. package/dist/src/tools/tool-error.d.ts +1 -0
  188. package/dist/src/tools/tool-error.js +1 -0
  189. package/dist/src/tools/tool-error.js.map +1 -1
  190. package/dist/src/tools/tool-registry.d.ts +2 -0
  191. package/dist/src/tools/tool-registry.js +46 -21
  192. package/dist/src/tools/tool-registry.js.map +1 -1
  193. package/dist/src/types/modelParams.d.ts +4 -0
  194. package/dist/src/utils/editor.js +10 -8
  195. package/dist/src/utils/editor.js.map +1 -1
  196. package/dist/src/utils/gitIgnoreParser.js +15 -3
  197. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  198. package/package.json +1 -1
  199. package/dist/src/prompt-config/defaults/providers/anthropic/core.md +0 -97
  200. package/dist/src/prompt-config/defaults/providers/anthropic/tools/glob.md +0 -34
  201. package/dist/src/prompt-config/defaults/providers/anthropic/tools/list-directory.md +0 -11
  202. package/dist/src/prompt-config/defaults/providers/anthropic/tools/read-file.md +0 -14
  203. package/dist/src/prompt-config/defaults/providers/anthropic/tools/read-many-files.md +0 -31
  204. package/dist/src/prompt-config/defaults/providers/anthropic/tools/replace.md +0 -41
  205. package/dist/src/prompt-config/defaults/providers/anthropic/tools/run-shell-command.md +0 -32
  206. package/dist/src/prompt-config/defaults/providers/anthropic/tools/save-memory.md +0 -35
  207. package/dist/src/prompt-config/defaults/providers/anthropic/tools/search-file-content.md +0 -44
  208. package/dist/src/prompt-config/defaults/providers/anthropic/tools/todo-write.md +0 -45
  209. package/dist/src/prompt-config/defaults/providers/anthropic/tools/write-file.md +0 -11
  210. package/dist/src/prompt-config/defaults/providers/openai/core.md +0 -97
  211. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-pause.md +0 -28
  212. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-read.md +0 -5
  213. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-write.md +0 -45
@@ -4,18 +4,20 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { getDirectoryContextString, getEnvironmentContext, } from '../utils/environmentContext.js';
7
- import { Turn, GeminiEventType } from './turn.js';
7
+ import { Turn, GeminiEventType, DEFAULT_AGENT_ID, } from './turn.js';
8
8
  import { CompressionStatus } from './turn.js';
9
- import { getCoreSystemPromptAsync, getCompressionPrompt, drainPromptInstallerNotices, } from './prompts.js';
9
+ import { getCoreSystemPromptAsync, getCompressionPrompt } from './prompts.js';
10
10
  import { getResponseText } from '../utils/generateContentResponseUtilities.js';
11
11
  import { reportError } from '../utils/errorReporting.js';
12
12
  import { GeminiChat } from './geminiChat.js';
13
13
  import { DebugLogger } from '../debug/index.js';
14
14
  import { HistoryService } from '../services/history/HistoryService.js';
15
15
  import { ContentConverters } from '../services/history/ContentConverters.js';
16
+ import { createProviderRuntimeContext } from '../runtime/providerRuntimeContext.js';
17
+ import { loadAgentRuntime } from '../runtime/AgentRuntimeLoader.js';
16
18
  import { retryWithBackoff } from '../utils/retry.js';
17
19
  import { getErrorMessage } from '../utils/errors.js';
18
- import { createContentGenerator, } from './contentGenerator.js';
20
+ import { AuthType, createContentGenerator, } from './contentGenerator.js';
19
21
  import { ProxyAgent, setGlobalDispatcher } from 'undici';
20
22
  import { tokenLimit } from './tokenLimits.js';
21
23
  import { COMPRESSION_TOKEN_THRESHOLD, COMPRESSION_PRESERVE_THRESHOLD, } from './compression-config.js';
@@ -25,10 +27,11 @@ import { ComplexityAnalyzer, } from '../services/complexity-analyzer.js';
25
27
  import { TodoReminderService } from '../services/todo-reminder-service.js';
26
28
  import { isFunctionResponse } from '../utils/messageInspectors.js';
27
29
  import { estimateTokens as estimateTextTokens } from '../utils/toolOutputLimiter.js';
30
+ import { subscribeToAgentRuntimeState } from '../runtime/AgentRuntimeState.js';
28
31
  const COMPLEXITY_ESCALATION_TURN_THRESHOLD = 3;
29
- const TODO_PROMPT_SUFFIX = 'Consider using a todo list to organize this multi-step work.';
30
- const TOOL_BASE_TODO_MESSAGE = 'Consider using a todo list to help organize your work if this is a complex task.';
31
- const TOOL_ESCALATED_TODO_MESSAGE = 'This task seems to involve multiple steps. A todo list might help track your progress.';
32
+ const TODO_PROMPT_SUFFIX = 'Use TODO List to organize this effort.';
33
+ const TOOL_BASE_TODO_MESSAGE = 'After this next tool call I need to call todo_write and create a todo list to organize this effort.';
34
+ const TOOL_ESCALATED_TODO_MESSAGE = 'I have already made several tool calls without a todo list. Immediately call todo_write after this next tool call to organize the work.';
32
35
  function isThinkingSupported(model) {
33
36
  if (model.startsWith('gemini-2.5'))
34
37
  return true;
@@ -88,6 +91,7 @@ export class GeminiClient {
88
91
  lastPromptId;
89
92
  complexityAnalyzer;
90
93
  todoReminderService;
94
+ todoToolsAvailable = false;
91
95
  lastComplexitySuggestionTime = 0;
92
96
  complexitySuggestionCooldown;
93
97
  lastSentIdeContext;
@@ -97,21 +101,61 @@ export class GeminiClient {
97
101
  lastComplexitySuggestionTurn;
98
102
  toolActivityCount = 0;
99
103
  toolCallReminderLevel = 'none';
100
- pendingInstallerNotices = [];
101
104
  /**
102
105
  * At any point in this conversation, was compression triggered without
103
106
  * being forced and did it fail?
104
107
  */
105
108
  hasFailedCompressionAttempt = false;
106
- constructor(config) {
109
+ /**
110
+ * Runtime state for stateless operation (Phase 5)
111
+ * @plan PLAN-20251027-STATELESS5.P10
112
+ * @requirement REQ-STAT5-003.1
113
+ * @pseudocode gemini-runtime.md lines 21-42
114
+ */
115
+ runtimeState;
116
+ _historyService;
117
+ _unsubscribe;
118
+ /**
119
+ * @plan PLAN-20251027-STATELESS5.P10
120
+ * @requirement REQ-STAT5-003.1
121
+ * @pseudocode gemini-runtime.md lines 11-66
122
+ *
123
+ * Phase 5 constructor: Accept optional AgentRuntimeState and HistoryService
124
+ * When provided, client operates in stateless mode using runtime state
125
+ * Otherwise falls back to Config-based operation (backward compatibility)
126
+ */
127
+ constructor(config, runtimeState, historyService) {
107
128
  this.config = config;
108
- if (config.getProxy()) {
109
- setGlobalDispatcher(new ProxyAgent(config.getProxy()));
129
+ if (!runtimeState.provider || runtimeState.provider === '') {
130
+ throw new Error('AgentRuntimeState must have a valid provider');
131
+ }
132
+ if (!runtimeState.model || runtimeState.model === '') {
133
+ throw new Error('AgentRuntimeState must have a valid model');
134
+ }
135
+ if (runtimeState.authType === AuthType.API_KEY &&
136
+ !runtimeState.authPayload?.apiKey) {
137
+ throw new Error('AgentRuntimeState must include apiKey when authType is API_KEY');
138
+ }
139
+ if (runtimeState.authType === AuthType.OAUTH &&
140
+ !runtimeState.authPayload?.token) {
141
+ throw new Error('AgentRuntimeState must include token when authType is OAUTH');
110
142
  }
143
+ this.runtimeState = runtimeState;
144
+ this._historyService = historyService;
111
145
  this.logger = new DebugLogger('llxprt:core:client');
112
- this.embeddingModel = config.getEmbeddingModel();
146
+ this._unsubscribe = subscribeToAgentRuntimeState(runtimeState.runtimeId, (event) => {
147
+ this.logger.debug('Runtime state changed', event);
148
+ });
149
+ void this._historyService;
150
+ void this._unsubscribe;
151
+ const proxyUrl = runtimeState.proxyUrl;
152
+ if (proxyUrl) {
153
+ setGlobalDispatcher(new ProxyAgent(proxyUrl));
154
+ }
155
+ const embeddingModel = config.getEmbeddingModel();
156
+ this.embeddingModel = embeddingModel || runtimeState.model;
113
157
  this.loopDetector = new LoopDetectionService(config);
114
- this.lastPromptId = this.config.getSessionId();
158
+ this.lastPromptId = runtimeState.sessionId;
115
159
  // Initialize complexity analyzer with config settings
116
160
  const complexitySettings = config.getComplexityAnalyzerSettings();
117
161
  this.complexityAnalyzer = new ComplexityAnalyzer({
@@ -121,7 +165,6 @@ export class GeminiClient {
121
165
  this.complexitySuggestionCooldown =
122
166
  complexitySettings.suggestionCooldownMs ?? 300000;
123
167
  this.todoReminderService = new TodoReminderService();
124
- this.pendingInstallerNotices = [];
125
168
  }
126
169
  async initialize(contentGeneratorConfig) {
127
170
  // Preserve chat history before resetting, but only if we don't already have stored history
@@ -162,6 +205,10 @@ export class GeminiClient {
162
205
  return this.contentGenerator?.userTier;
163
206
  }
164
207
  processComplexityAnalysis(analysis) {
208
+ if (!this.todoToolsAvailable) {
209
+ this.consecutiveComplexTurns = 0;
210
+ return undefined;
211
+ }
165
212
  if (!analysis.isComplex || !analysis.shouldSuggestTodos) {
166
213
  this.consecutiveComplexTurns = 0;
167
214
  return undefined;
@@ -213,25 +260,22 @@ export class GeminiClient {
213
260
  return request;
214
261
  }
215
262
  recordModelActivity(event) {
263
+ if (!this.todoToolsAvailable) {
264
+ return;
265
+ }
216
266
  if (event.type !== GeminiEventType.Content &&
217
267
  event.type !== GeminiEventType.ToolCallRequest) {
218
268
  return;
219
269
  }
220
270
  this.toolActivityCount += 1;
221
- // Always trigger reminders after a certain number of tool calls
222
- // regardless of complexity - this is the core safety feature
223
- if (this.toolActivityCount > 5) {
271
+ if (this.toolActivityCount > 4) {
224
272
  this.toolCallReminderLevel = 'escalated';
225
273
  }
226
- else if (this.toolActivityCount === 5 &&
274
+ else if (this.toolActivityCount === 4 &&
227
275
  this.toolCallReminderLevel === 'none') {
228
276
  this.toolCallReminderLevel = 'base';
229
277
  }
230
278
  }
231
- /**
232
- * Analyzes the current user request to determine if it's complex enough
233
- * to warrant todo reminders
234
- */
235
279
  async addHistory(content) {
236
280
  // Ensure chat is initialized before adding history
237
281
  if (!this.hasChatInitialized()) {
@@ -331,7 +375,16 @@ export class GeminiClient {
331
375
  }
332
376
  async setTools() {
333
377
  const toolRegistry = this.config.getToolRegistry();
334
- const toolDeclarations = toolRegistry.getFunctionDeclarations();
378
+ if (!toolRegistry) {
379
+ return;
380
+ }
381
+ const toolsView = typeof this.chat?.getToolsView === 'function'
382
+ ? this.chat.getToolsView()
383
+ : undefined;
384
+ const toolDeclarations = toolsView
385
+ ? this.buildToolDeclarationsFromView(toolRegistry, toolsView)
386
+ : toolRegistry.getFunctionDeclarations();
387
+ this.updateTodoToolAvailabilityFromDeclarations(toolDeclarations);
335
388
  // Debug log for intermittent tool issues
336
389
  const logger = new DebugLogger('llxprt:client:setTools');
337
390
  logger.debug(() => `setTools called, declarations count: ${toolDeclarations.length}`);
@@ -378,6 +431,13 @@ export class GeminiClient {
378
431
  parts: [{ text: await getDirectoryContextString(this.config) }],
379
432
  });
380
433
  }
434
+ async generateDirectMessage(params, promptId) {
435
+ await this.lazyInitialize();
436
+ if (!this.chat) {
437
+ this.chat = await this.startChat([]);
438
+ }
439
+ return this.getChat().generateDirectMessage(params, promptId);
440
+ }
381
441
  async startChat(extraHistory) {
382
442
  this.forceFullIdeContext = true;
383
443
  this.hasFailedCompressionAttempt = false;
@@ -385,8 +445,6 @@ export class GeminiClient {
385
445
  await this.lazyInitialize();
386
446
  const envParts = await getEnvironmentContext(this.config);
387
447
  const toolRegistry = this.config.getToolRegistry();
388
- const toolDeclarations = toolRegistry.getFunctionDeclarations();
389
- const tools = [{ functionDeclarations: toolDeclarations }];
390
448
  const enabledToolNames = this.getEnabledToolNamesForPrompt();
391
449
  // CRITICAL: Reuse stored HistoryService if available to preserve UI conversation display
392
450
  // This is essential for maintaining conversation history across provider switches
@@ -403,21 +461,22 @@ export class GeminiClient {
403
461
  }
404
462
  // Add extraHistory if provided
405
463
  if (extraHistory && extraHistory.length > 0) {
406
- const currentModel = this.config.getModel();
464
+ // @plan PLAN-20251027-STATELESS5.P10
465
+ // @requirement REQ-STAT5-003.1
466
+ const currentModel = this.runtimeState.model;
407
467
  for (const content of extraHistory) {
408
468
  historyService.add(ContentConverters.toIContent(content), currentModel);
409
469
  }
410
470
  }
411
471
  try {
412
472
  const userMemory = this.config.getUserMemory();
413
- const model = this.config.getModel();
473
+ // @plan PLAN-20251027-STATELESS5.P10
474
+ // @requirement REQ-STAT5-003.1
475
+ const model = this.runtimeState.model;
476
+ // Provider name removed from prompt call signature
414
477
  const logger = new DebugLogger('llxprt:client:start');
415
478
  logger.debug(() => `DEBUG [client.startChat]: Model from config: ${model}`);
416
479
  let systemInstruction = await getCoreSystemPromptAsync(userMemory, model, enabledToolNames);
417
- const installerNotices = await drainPromptInstallerNotices();
418
- if (installerNotices.length > 0) {
419
- this.pendingInstallerNotices.push(...installerNotices);
420
- }
421
480
  // Add environment context to system instruction
422
481
  const envContextText = envParts
423
482
  .map((part) => ('text' in part ? part.text : ''))
@@ -435,7 +494,9 @@ export class GeminiClient {
435
494
  }
436
495
  historyService.setBaseTokenOffset(systemPromptTokens);
437
496
  logger.debug(() => `DEBUG [client.startChat]: System instruction includes Flash instructions: ${systemInstruction.includes('IMPORTANT: You MUST use the provided tools')}`);
438
- const generateContentConfigWithThinking = isThinkingSupported(this.config.getModel())
497
+ // @plan PLAN-20251027-STATELESS5.P10
498
+ // @requirement REQ-STAT5-003.1
499
+ const generateContentConfigWithThinking = isThinkingSupported(this.runtimeState.model)
439
500
  ? {
440
501
  ...this.generateContentConfig,
441
502
  thinkingConfig: {
@@ -444,12 +505,62 @@ export class GeminiClient {
444
505
  },
445
506
  }
446
507
  : this.generateContentConfig;
447
- return new GeminiChat(this.config, this.getContentGenerator(), {
508
+ // @plan PLAN-20251028-STATELESS6.P10
509
+ // @requirement REQ-STAT6-001.2
510
+ // Create runtime context from config (foreground agent)
511
+ const rawCompressionThreshold = this.config.getEphemeralSetting('compression-threshold');
512
+ const compressionThreshold = typeof rawCompressionThreshold === 'number'
513
+ ? rawCompressionThreshold
514
+ : undefined;
515
+ const rawContextLimit = this.config.getEphemeralSetting('context-limit');
516
+ const contextLimit = typeof rawContextLimit === 'number' &&
517
+ Number.isFinite(rawContextLimit) &&
518
+ rawContextLimit > 0
519
+ ? rawContextLimit
520
+ : undefined;
521
+ const rawPreserveThreshold = this.config.getEphemeralSetting('compression-preserve-threshold');
522
+ const preserveThreshold = typeof rawPreserveThreshold === 'number'
523
+ ? rawPreserveThreshold
524
+ : undefined;
525
+ const settings = {
526
+ compressionThreshold: compressionThreshold ?? 0.8,
527
+ contextLimit,
528
+ preserveThreshold: preserveThreshold ?? 0.2,
529
+ telemetry: {
530
+ enabled: true,
531
+ target: null,
532
+ },
533
+ tools: this.getToolGovernanceEphemerals(),
534
+ };
535
+ const providerRuntime = createProviderRuntimeContext({
536
+ settingsService: this.config.getSettingsService(),
537
+ config: this.config,
538
+ runtimeId: this.runtimeState.runtimeId,
539
+ metadata: { source: 'GeminiClient.startChat' },
540
+ });
541
+ const runtimeBundle = await loadAgentRuntime({
542
+ profile: {
543
+ config: this.config,
544
+ state: this.runtimeState,
545
+ settings,
546
+ providerRuntime,
547
+ contentGeneratorConfig: this.config.getContentGeneratorConfig(),
548
+ toolRegistry,
549
+ providerManager: this.config.getProviderManager?.(),
550
+ },
551
+ overrides: {
552
+ historyService,
553
+ contentGenerator: this.getContentGenerator(),
554
+ },
555
+ });
556
+ const filteredDeclarations = this.buildToolDeclarationsFromView(toolRegistry, runtimeBundle.toolsView);
557
+ this.updateTodoToolAvailabilityFromDeclarations(filteredDeclarations);
558
+ const tools = [{ functionDeclarations: filteredDeclarations }];
559
+ return new GeminiChat(runtimeBundle.runtimeContext, runtimeBundle.contentGenerator, {
448
560
  systemInstruction,
449
561
  ...generateContentConfigWithThinking,
450
562
  tools,
451
- }, [], // Empty initial history since we're using HistoryService
452
- historyService);
563
+ }, []);
453
564
  }
454
565
  catch (error) {
455
566
  await reportError(error, 'Error initializing chat session.', extraHistory ?? [], 'startChat');
@@ -624,13 +735,6 @@ export class GeminiClient {
624
735
  this.chat = await this.startChat();
625
736
  }
626
737
  }
627
- if (this.pendingInstallerNotices.length > 0) {
628
- const notices = [...this.pendingInstallerNotices];
629
- this.pendingInstallerNotices = [];
630
- for (const notice of notices) {
631
- yield { type: GeminiEventType.SystemNotice, value: notice };
632
- }
633
- }
634
738
  if (this.lastPromptId !== prompt_id) {
635
739
  this.loopDetector.reset(prompt_id);
636
740
  this.lastPromptId = prompt_id;
@@ -644,7 +748,7 @@ export class GeminiClient {
644
748
  const contentGenConfig = this.config.getContentGeneratorConfig();
645
749
  const providerManager = contentGenConfig?.providerManager;
646
750
  const providerName = providerManager?.getActiveProviderName() || 'backend';
647
- return new Turn(this.getChat(), prompt_id, providerName);
751
+ return new Turn(this.getChat(), prompt_id, DEFAULT_AGENT_ID, providerName);
648
752
  }
649
753
  // Ensure turns never exceeds MAX_TURNS to prevent infinite loops
650
754
  const boundedTurns = Math.min(turns, this.MAX_TURNS);
@@ -652,16 +756,15 @@ export class GeminiClient {
652
756
  const contentGenConfig = this.config.getContentGeneratorConfig();
653
757
  const providerManager = contentGenConfig?.providerManager;
654
758
  const providerName = providerManager?.getActiveProviderName() || 'backend';
655
- return new Turn(this.getChat(), prompt_id, providerName);
759
+ return new Turn(this.getChat(), prompt_id, DEFAULT_AGENT_ID, providerName);
656
760
  }
657
761
  // Track the original model from the first call to detect model switching
658
- const initialModel = originalModel || this.config.getModel();
659
- // Only try compression if chat is properly initialized
660
- if (this.chat && typeof this.chat.getHistoryService === 'function') {
661
- const compressed = await this.tryCompressChat(prompt_id);
662
- if (compressed.compressionStatus === CompressionStatus.COMPRESSED) {
663
- yield { type: GeminiEventType.ChatCompressed, value: compressed };
664
- }
762
+ // @plan PLAN-20251027-STATELESS5.P10
763
+ // @requirement REQ-STAT5-003.1
764
+ const initialModel = originalModel || this.runtimeState.model;
765
+ const compressed = await this.tryCompressChat(prompt_id);
766
+ if (compressed.compressionStatus === CompressionStatus.COMPRESSED) {
767
+ yield { type: GeminiEventType.ChatCompressed, value: compressed };
665
768
  }
666
769
  // Prevent context updates from being sent while a tool call is
667
770
  // waiting for a response. The Gemini API requires that a functionResponse
@@ -712,7 +815,7 @@ export class GeminiClient {
712
815
  const contentGenConfig = this.config.getContentGeneratorConfig();
713
816
  const providerManager = contentGenConfig?.providerManager;
714
817
  const providerName = providerManager?.getActiveProviderName() || 'backend';
715
- const turn = new Turn(this.getChat(), prompt_id, providerName);
818
+ const turn = new Turn(this.getChat(), prompt_id, DEFAULT_AGENT_ID, providerName);
716
819
  const loopDetected = await this.loopDetector.turnStarted(signal);
717
820
  if (loopDetected) {
718
821
  yield { type: GeminiEventType.LoopDetected };
@@ -735,7 +838,7 @@ export class GeminiClient {
735
838
  return turn;
736
839
  }
737
840
  }
738
- if (this.toolCallReminderLevel !== 'none') {
841
+ if (this.todoToolsAvailable && this.toolCallReminderLevel !== 'none') {
739
842
  const reminderText = this.toolCallReminderLevel === 'escalated'
740
843
  ? TOOL_ESCALATED_TODO_MESSAGE
741
844
  : TOOL_BASE_TODO_MESSAGE;
@@ -752,7 +855,9 @@ export class GeminiClient {
752
855
  }
753
856
  if (!turn.pendingToolCalls.length && signal && !signal.aborted) {
754
857
  // Check if model was switched during the call (likely due to quota error)
755
- const currentModel = this.config.getModel();
858
+ // @plan PLAN-20251027-STATELESS5.P10
859
+ // @requirement REQ-STAT5-003.1
860
+ const currentModel = this.runtimeState.model;
756
861
  if (currentModel !== initialModel) {
757
862
  // Model was switched (likely due to quota error fallback)
758
863
  // Don't continue with recursive call to prevent unwanted Flash execution
@@ -768,6 +873,7 @@ export class GeminiClient {
768
873
  const modelToUse = model;
769
874
  try {
770
875
  const userMemory = this.config.getUserMemory();
876
+ // Provider name removed from prompt call signature
771
877
  const systemInstruction = await getCoreSystemPromptAsync(userMemory, modelToUse, this.getEnabledToolNamesForPrompt());
772
878
  const requestConfig = {
773
879
  abortSignal,
@@ -846,6 +952,7 @@ export class GeminiClient {
846
952
  };
847
953
  try {
848
954
  const userMemory = this.config.getUserMemory();
955
+ // Provider name removed from prompt call signature
849
956
  const systemInstruction = await getCoreSystemPromptAsync(userMemory, modelToUse, this.getEnabledToolNamesForPrompt());
850
957
  const requestConfig = {
851
958
  abortSignal,
@@ -920,7 +1027,9 @@ export class GeminiClient {
920
1027
  };
921
1028
  }
922
1029
  // Note: chat variable used later in method
923
- const model = this.config.getModel();
1030
+ // @plan PLAN-20251027-STATELESS5.P10
1031
+ // @requirement REQ-STAT5-003.1
1032
+ const model = this.runtimeState.model;
924
1033
  // Get the ACTUAL token count from the history service, not the curated subset
925
1034
  const historyService = this.getChat().getHistoryService();
926
1035
  const originalTokenCount = historyService
@@ -1017,10 +1126,12 @@ export class GeminiClient {
1017
1126
  const historyService = compressedChat.getHistoryService();
1018
1127
  if (historyService) {
1019
1128
  const userContextLimit = this.config.getEphemeralSetting('context-limit');
1129
+ // @plan PLAN-20251027-STATELESS5.P10
1130
+ // @requirement REQ-STAT5-003.1
1020
1131
  historyService.emit('tokensUpdated', {
1021
1132
  totalTokens: newTokenCount,
1022
1133
  addedTokens: newTokenCount - originalTokenCount,
1023
- tokenLimit: tokenLimit(this.config.getModel(), userContextLimit),
1134
+ tokenLimit: tokenLimit(this.runtimeState.model, userContextLimit),
1024
1135
  });
1025
1136
  }
1026
1137
  }
@@ -1031,6 +1142,63 @@ export class GeminiClient {
1031
1142
  compressionStatus: CompressionStatus.COMPRESSED,
1032
1143
  };
1033
1144
  }
1145
+ getToolGovernanceEphemerals() {
1146
+ const allowedList = this.readToolList(this.config.getEphemeralSetting('tools.allowed'));
1147
+ const disabledList = this.readToolList(this.config.getEphemeralSetting('tools.disabled') ??
1148
+ this.config.getEphemeralSetting('disabled-tools'));
1149
+ const hasAllowed = allowedList.length > 0;
1150
+ const hasDisabled = disabledList.length > 0;
1151
+ if (!hasAllowed && !hasDisabled) {
1152
+ return undefined;
1153
+ }
1154
+ return {
1155
+ allowed: hasAllowed ? allowedList : undefined,
1156
+ disabled: hasDisabled ? disabledList : undefined,
1157
+ };
1158
+ }
1159
+ readToolList(value) {
1160
+ if (!Array.isArray(value)) {
1161
+ return [];
1162
+ }
1163
+ const filtered = value.filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
1164
+ return filtered.length > 0 ? [...filtered] : [];
1165
+ }
1166
+ buildToolDeclarationsFromView(toolRegistry, view) {
1167
+ if (!toolRegistry) {
1168
+ return [];
1169
+ }
1170
+ const allowedNames = view.listToolNames();
1171
+ if (allowedNames.length === 0) {
1172
+ return [];
1173
+ }
1174
+ const declarations = [];
1175
+ if (typeof toolRegistry.getAllTools === 'function') {
1176
+ const toolsByName = new Map(toolRegistry.getAllTools().map((tool) => [tool.name, tool]));
1177
+ for (const name of allowedNames) {
1178
+ const tool = toolsByName.get(name);
1179
+ if (!tool) {
1180
+ continue;
1181
+ }
1182
+ const schema = tool.schema;
1183
+ if (schema) {
1184
+ declarations.push(schema);
1185
+ }
1186
+ }
1187
+ return declarations;
1188
+ }
1189
+ if (typeof toolRegistry.getFunctionDeclarations === 'function') {
1190
+ const declarationsByName = new Map(toolRegistry
1191
+ .getFunctionDeclarations()
1192
+ .map((decl) => [decl.name, decl]));
1193
+ for (const name of allowedNames) {
1194
+ const declaration = declarationsByName.get(name);
1195
+ if (declaration) {
1196
+ declarations.push(declaration);
1197
+ }
1198
+ }
1199
+ }
1200
+ return declarations;
1201
+ }
1034
1202
  getEnabledToolNamesForPrompt() {
1035
1203
  const toolRegistry = this.config.getToolRegistry();
1036
1204
  if (!toolRegistry ||
@@ -1043,5 +1211,13 @@ export class GeminiClient {
1043
1211
  .map((tool) => tool.name)
1044
1212
  .filter(Boolean)));
1045
1213
  }
1214
+ updateTodoToolAvailabilityFromDeclarations(declarations) {
1215
+ const normalizedNames = new Set(declarations
1216
+ .map((decl) => decl?.name)
1217
+ .filter((name) => typeof name === 'string')
1218
+ .map((name) => name.toLowerCase()));
1219
+ this.todoToolsAvailable =
1220
+ normalizedNames.has('todo_write') && normalizedNames.has('todo_read');
1221
+ }
1046
1222
  }
1047
1223
  //# sourceMappingURL=client.js.map