@vybestack/llxprt-code-core 0.4.8 → 0.5.0-nightly.251102.f115237d

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 (210) 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 +217 -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.js +41 -1
  61. package/dist/src/parsers/TextToolCallParser.js.map +1 -1
  62. package/dist/src/prompt-config/defaults/core.md +15 -0
  63. package/dist/src/prompt-config/defaults/providers/gemini/core.md +203 -119
  64. package/dist/src/prompt-config/defaults/tool-defaults.js +2 -0
  65. package/dist/src/prompt-config/defaults/tool-defaults.js.map +1 -1
  66. package/dist/src/prompt-config/defaults/tools/list-subagents.md +7 -0
  67. package/dist/src/prompt-config/defaults/tools/task.md +8 -0
  68. package/dist/src/providers/BaseProvider.d.ts +115 -30
  69. package/dist/src/providers/BaseProvider.js +445 -109
  70. package/dist/src/providers/BaseProvider.js.map +1 -1
  71. package/dist/src/providers/IProvider.d.ts +50 -18
  72. package/dist/src/providers/LoggingProviderWrapper.d.ts +60 -16
  73. package/dist/src/providers/LoggingProviderWrapper.js +213 -60
  74. package/dist/src/providers/LoggingProviderWrapper.js.map +1 -1
  75. package/dist/src/providers/ProviderManager.d.ts +73 -2
  76. package/dist/src/providers/ProviderManager.js +492 -40
  77. package/dist/src/providers/ProviderManager.js.map +1 -1
  78. package/dist/src/providers/anthropic/AnthropicProvider.d.ts +35 -38
  79. package/dist/src/providers/anthropic/AnthropicProvider.js +222 -227
  80. package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
  81. package/dist/src/providers/errors.d.ts +86 -0
  82. package/dist/src/providers/errors.js +89 -0
  83. package/dist/src/providers/errors.js.map +1 -1
  84. package/dist/src/providers/gemini/GeminiProvider.d.ts +101 -41
  85. package/dist/src/providers/gemini/GeminiProvider.js +386 -311
  86. package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
  87. package/dist/src/providers/openai/ConversationCache.d.ts +5 -3
  88. package/dist/src/providers/openai/ConversationCache.js +93 -32
  89. package/dist/src/providers/openai/ConversationCache.js.map +1 -1
  90. package/dist/src/providers/openai/OpenAIProvider.d.ts +82 -42
  91. package/dist/src/providers/openai/OpenAIProvider.js +373 -532
  92. package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
  93. package/dist/src/providers/openai/getOpenAIProviderInfo.d.ts +1 -1
  94. package/dist/src/providers/openai/getOpenAIProviderInfo.js +52 -22
  95. package/dist/src/providers/openai/getOpenAIProviderInfo.js.map +1 -1
  96. package/dist/src/providers/openai/openaiRequestParams.d.ts +7 -0
  97. package/dist/src/providers/openai/openaiRequestParams.js +66 -0
  98. package/dist/src/providers/openai/openaiRequestParams.js.map +1 -0
  99. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.d.ts +6 -33
  100. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js +84 -183
  101. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js.map +1 -1
  102. package/dist/src/providers/types/providerRuntime.d.ts +17 -0
  103. package/dist/src/providers/types/providerRuntime.js +7 -0
  104. package/dist/src/providers/types/providerRuntime.js.map +1 -0
  105. package/dist/src/providers/utils/authToken.d.ts +12 -0
  106. package/dist/src/providers/utils/authToken.js +17 -0
  107. package/dist/src/providers/utils/authToken.js.map +1 -0
  108. package/dist/src/providers/utils/userMemory.d.ts +8 -0
  109. package/dist/src/providers/utils/userMemory.js +34 -0
  110. package/dist/src/providers/utils/userMemory.js.map +1 -0
  111. package/dist/src/runtime/AgentRuntimeContext.d.ts +213 -0
  112. package/dist/src/runtime/AgentRuntimeContext.js +17 -0
  113. package/dist/src/runtime/AgentRuntimeContext.js.map +1 -0
  114. package/dist/src/runtime/AgentRuntimeLoader.d.ts +47 -0
  115. package/dist/src/runtime/AgentRuntimeLoader.js +122 -0
  116. package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -0
  117. package/dist/src/runtime/AgentRuntimeState.d.ts +232 -0
  118. package/dist/src/runtime/AgentRuntimeState.js +439 -0
  119. package/dist/src/runtime/AgentRuntimeState.js.map +1 -0
  120. package/dist/src/runtime/RuntimeInvocationContext.d.ts +51 -0
  121. package/dist/src/runtime/RuntimeInvocationContext.js +52 -0
  122. package/dist/src/runtime/RuntimeInvocationContext.js.map +1 -0
  123. package/dist/src/runtime/createAgentRuntimeContext.d.ts +7 -0
  124. package/dist/src/runtime/createAgentRuntimeContext.js +58 -0
  125. package/dist/src/runtime/createAgentRuntimeContext.js.map +1 -0
  126. package/dist/src/runtime/index.d.ts +13 -0
  127. package/dist/src/runtime/index.js +14 -0
  128. package/dist/src/runtime/index.js.map +1 -0
  129. package/dist/src/runtime/providerRuntimeContext.d.ts +30 -0
  130. package/dist/src/runtime/providerRuntimeContext.js +70 -0
  131. package/dist/src/runtime/providerRuntimeContext.js.map +1 -0
  132. package/dist/src/runtime/runtimeAdapters.d.ts +22 -0
  133. package/dist/src/runtime/runtimeAdapters.js +81 -0
  134. package/dist/src/runtime/runtimeAdapters.js.map +1 -0
  135. package/dist/src/runtime/runtimeStateFactory.d.ts +21 -0
  136. package/dist/src/runtime/runtimeStateFactory.js +104 -0
  137. package/dist/src/runtime/runtimeStateFactory.js.map +1 -0
  138. package/dist/src/services/todo-context-tracker.d.ts +10 -8
  139. package/dist/src/services/todo-context-tracker.js +26 -10
  140. package/dist/src/services/todo-context-tracker.js.map +1 -1
  141. package/dist/src/services/tool-call-tracker-service.d.ts +11 -7
  142. package/dist/src/services/tool-call-tracker-service.js +89 -29
  143. package/dist/src/services/tool-call-tracker-service.js.map +1 -1
  144. package/dist/src/settings/SettingsService.d.ts +4 -0
  145. package/dist/src/settings/SettingsService.js +58 -2
  146. package/dist/src/settings/SettingsService.js.map +1 -1
  147. package/dist/src/settings/settingsServiceInstance.d.ts +6 -1
  148. package/dist/src/settings/settingsServiceInstance.js +28 -8
  149. package/dist/src/settings/settingsServiceInstance.js.map +1 -1
  150. package/dist/src/telemetry/loggers.d.ts +5 -1
  151. package/dist/src/telemetry/loggers.js.map +1 -1
  152. package/dist/src/telemetry/loggers.test.circular.js +4 -0
  153. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  154. package/dist/src/telemetry/metrics.d.ts +3 -1
  155. package/dist/src/telemetry/metrics.js.map +1 -1
  156. package/dist/src/telemetry/types.d.ts +1 -0
  157. package/dist/src/telemetry/types.js +3 -0
  158. package/dist/src/telemetry/types.js.map +1 -1
  159. package/dist/src/test-utils/index.d.ts +2 -0
  160. package/dist/src/test-utils/index.js +2 -0
  161. package/dist/src/test-utils/index.js.map +1 -1
  162. package/dist/src/test-utils/mockWorkspaceContext.d.ts +0 -3
  163. package/dist/src/test-utils/mockWorkspaceContext.js +3 -4
  164. package/dist/src/test-utils/mockWorkspaceContext.js.map +1 -1
  165. package/dist/src/test-utils/providerCallOptions.d.ts +43 -0
  166. package/dist/src/test-utils/providerCallOptions.js +137 -0
  167. package/dist/src/test-utils/providerCallOptions.js.map +1 -0
  168. package/dist/src/test-utils/runtime.d.ts +92 -0
  169. package/dist/src/test-utils/runtime.js +226 -0
  170. package/dist/src/test-utils/runtime.js.map +1 -0
  171. package/dist/src/test-utils/tools.d.ts +4 -4
  172. package/dist/src/test-utils/tools.js +20 -10
  173. package/dist/src/test-utils/tools.js.map +1 -1
  174. package/dist/src/tools/list-subagents.d.ts +31 -0
  175. package/dist/src/tools/list-subagents.js +109 -0
  176. package/dist/src/tools/list-subagents.js.map +1 -0
  177. package/dist/src/tools/task.d.ts +87 -0
  178. package/dist/src/tools/task.js +427 -0
  179. package/dist/src/tools/task.js.map +1 -0
  180. package/dist/src/tools/todo-read.js +1 -1
  181. package/dist/src/tools/todo-read.js.map +1 -1
  182. package/dist/src/tools/todo-store.js +4 -2
  183. package/dist/src/tools/todo-store.js.map +1 -1
  184. package/dist/src/tools/todo-write.js +4 -2
  185. package/dist/src/tools/todo-write.js.map +1 -1
  186. package/dist/src/tools/tool-error.d.ts +1 -0
  187. package/dist/src/tools/tool-error.js +1 -0
  188. package/dist/src/tools/tool-error.js.map +1 -1
  189. package/dist/src/tools/tool-registry.d.ts +2 -0
  190. package/dist/src/tools/tool-registry.js +46 -21
  191. package/dist/src/tools/tool-registry.js.map +1 -1
  192. package/dist/src/types/modelParams.d.ts +4 -0
  193. package/dist/src/utils/gitIgnoreParser.js +15 -3
  194. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  195. package/package.json +1 -1
  196. package/dist/src/prompt-config/defaults/providers/anthropic/core.md +0 -97
  197. package/dist/src/prompt-config/defaults/providers/anthropic/tools/glob.md +0 -34
  198. package/dist/src/prompt-config/defaults/providers/anthropic/tools/list-directory.md +0 -11
  199. package/dist/src/prompt-config/defaults/providers/anthropic/tools/read-file.md +0 -14
  200. package/dist/src/prompt-config/defaults/providers/anthropic/tools/read-many-files.md +0 -31
  201. package/dist/src/prompt-config/defaults/providers/anthropic/tools/replace.md +0 -41
  202. package/dist/src/prompt-config/defaults/providers/anthropic/tools/run-shell-command.md +0 -32
  203. package/dist/src/prompt-config/defaults/providers/anthropic/tools/save-memory.md +0 -35
  204. package/dist/src/prompt-config/defaults/providers/anthropic/tools/search-file-content.md +0 -44
  205. package/dist/src/prompt-config/defaults/providers/anthropic/tools/todo-write.md +0 -45
  206. package/dist/src/prompt-config/defaults/providers/anthropic/tools/write-file.md +0 -11
  207. package/dist/src/prompt-config/defaults/providers/openai/core.md +0 -97
  208. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-pause.md +0 -28
  209. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-read.md +0 -5
  210. 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,48 @@ 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 settings = {
512
+ compressionThreshold: this.config.getEphemeralSetting('compression-threshold') ?? 0.8,
513
+ contextLimit: this.config.getEphemeralSetting('context-limit') ?? 60000,
514
+ preserveThreshold: this.config.getEphemeralSetting('compression-preserve-threshold') ?? 0.2,
515
+ telemetry: {
516
+ enabled: true,
517
+ target: null,
518
+ },
519
+ tools: this.getToolGovernanceEphemerals(),
520
+ };
521
+ const providerRuntime = createProviderRuntimeContext({
522
+ settingsService: this.config.getSettingsService(),
523
+ config: this.config,
524
+ runtimeId: this.runtimeState.runtimeId,
525
+ metadata: { source: 'GeminiClient.startChat' },
526
+ });
527
+ const runtimeBundle = await loadAgentRuntime({
528
+ profile: {
529
+ config: this.config,
530
+ state: this.runtimeState,
531
+ settings,
532
+ providerRuntime,
533
+ contentGeneratorConfig: this.config.getContentGeneratorConfig(),
534
+ toolRegistry,
535
+ providerManager: this.config.getProviderManager?.(),
536
+ },
537
+ overrides: {
538
+ historyService,
539
+ contentGenerator: this.getContentGenerator(),
540
+ },
541
+ });
542
+ const filteredDeclarations = this.buildToolDeclarationsFromView(toolRegistry, runtimeBundle.toolsView);
543
+ this.updateTodoToolAvailabilityFromDeclarations(filteredDeclarations);
544
+ const tools = [{ functionDeclarations: filteredDeclarations }];
545
+ return new GeminiChat(runtimeBundle.runtimeContext, runtimeBundle.contentGenerator, {
448
546
  systemInstruction,
449
547
  ...generateContentConfigWithThinking,
450
548
  tools,
451
- }, [], // Empty initial history since we're using HistoryService
452
- historyService);
549
+ }, []);
453
550
  }
454
551
  catch (error) {
455
552
  await reportError(error, 'Error initializing chat session.', extraHistory ?? [], 'startChat');
@@ -624,13 +721,6 @@ export class GeminiClient {
624
721
  this.chat = await this.startChat();
625
722
  }
626
723
  }
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
724
  if (this.lastPromptId !== prompt_id) {
635
725
  this.loopDetector.reset(prompt_id);
636
726
  this.lastPromptId = prompt_id;
@@ -644,7 +734,7 @@ export class GeminiClient {
644
734
  const contentGenConfig = this.config.getContentGeneratorConfig();
645
735
  const providerManager = contentGenConfig?.providerManager;
646
736
  const providerName = providerManager?.getActiveProviderName() || 'backend';
647
- return new Turn(this.getChat(), prompt_id, providerName);
737
+ return new Turn(this.getChat(), prompt_id, DEFAULT_AGENT_ID, providerName);
648
738
  }
649
739
  // Ensure turns never exceeds MAX_TURNS to prevent infinite loops
650
740
  const boundedTurns = Math.min(turns, this.MAX_TURNS);
@@ -652,16 +742,15 @@ export class GeminiClient {
652
742
  const contentGenConfig = this.config.getContentGeneratorConfig();
653
743
  const providerManager = contentGenConfig?.providerManager;
654
744
  const providerName = providerManager?.getActiveProviderName() || 'backend';
655
- return new Turn(this.getChat(), prompt_id, providerName);
745
+ return new Turn(this.getChat(), prompt_id, DEFAULT_AGENT_ID, providerName);
656
746
  }
657
747
  // 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
- }
748
+ // @plan PLAN-20251027-STATELESS5.P10
749
+ // @requirement REQ-STAT5-003.1
750
+ const initialModel = originalModel || this.runtimeState.model;
751
+ const compressed = await this.tryCompressChat(prompt_id);
752
+ if (compressed.compressionStatus === CompressionStatus.COMPRESSED) {
753
+ yield { type: GeminiEventType.ChatCompressed, value: compressed };
665
754
  }
666
755
  // Prevent context updates from being sent while a tool call is
667
756
  // waiting for a response. The Gemini API requires that a functionResponse
@@ -712,7 +801,7 @@ export class GeminiClient {
712
801
  const contentGenConfig = this.config.getContentGeneratorConfig();
713
802
  const providerManager = contentGenConfig?.providerManager;
714
803
  const providerName = providerManager?.getActiveProviderName() || 'backend';
715
- const turn = new Turn(this.getChat(), prompt_id, providerName);
804
+ const turn = new Turn(this.getChat(), prompt_id, DEFAULT_AGENT_ID, providerName);
716
805
  const loopDetected = await this.loopDetector.turnStarted(signal);
717
806
  if (loopDetected) {
718
807
  yield { type: GeminiEventType.LoopDetected };
@@ -735,7 +824,7 @@ export class GeminiClient {
735
824
  return turn;
736
825
  }
737
826
  }
738
- if (this.toolCallReminderLevel !== 'none') {
827
+ if (this.todoToolsAvailable && this.toolCallReminderLevel !== 'none') {
739
828
  const reminderText = this.toolCallReminderLevel === 'escalated'
740
829
  ? TOOL_ESCALATED_TODO_MESSAGE
741
830
  : TOOL_BASE_TODO_MESSAGE;
@@ -752,7 +841,9 @@ export class GeminiClient {
752
841
  }
753
842
  if (!turn.pendingToolCalls.length && signal && !signal.aborted) {
754
843
  // Check if model was switched during the call (likely due to quota error)
755
- const currentModel = this.config.getModel();
844
+ // @plan PLAN-20251027-STATELESS5.P10
845
+ // @requirement REQ-STAT5-003.1
846
+ const currentModel = this.runtimeState.model;
756
847
  if (currentModel !== initialModel) {
757
848
  // Model was switched (likely due to quota error fallback)
758
849
  // Don't continue with recursive call to prevent unwanted Flash execution
@@ -768,6 +859,7 @@ export class GeminiClient {
768
859
  const modelToUse = model;
769
860
  try {
770
861
  const userMemory = this.config.getUserMemory();
862
+ // Provider name removed from prompt call signature
771
863
  const systemInstruction = await getCoreSystemPromptAsync(userMemory, modelToUse, this.getEnabledToolNamesForPrompt());
772
864
  const requestConfig = {
773
865
  abortSignal,
@@ -846,6 +938,7 @@ export class GeminiClient {
846
938
  };
847
939
  try {
848
940
  const userMemory = this.config.getUserMemory();
941
+ // Provider name removed from prompt call signature
849
942
  const systemInstruction = await getCoreSystemPromptAsync(userMemory, modelToUse, this.getEnabledToolNamesForPrompt());
850
943
  const requestConfig = {
851
944
  abortSignal,
@@ -920,7 +1013,9 @@ export class GeminiClient {
920
1013
  };
921
1014
  }
922
1015
  // Note: chat variable used later in method
923
- const model = this.config.getModel();
1016
+ // @plan PLAN-20251027-STATELESS5.P10
1017
+ // @requirement REQ-STAT5-003.1
1018
+ const model = this.runtimeState.model;
924
1019
  // Get the ACTUAL token count from the history service, not the curated subset
925
1020
  const historyService = this.getChat().getHistoryService();
926
1021
  const originalTokenCount = historyService
@@ -1017,10 +1112,12 @@ export class GeminiClient {
1017
1112
  const historyService = compressedChat.getHistoryService();
1018
1113
  if (historyService) {
1019
1114
  const userContextLimit = this.config.getEphemeralSetting('context-limit');
1115
+ // @plan PLAN-20251027-STATELESS5.P10
1116
+ // @requirement REQ-STAT5-003.1
1020
1117
  historyService.emit('tokensUpdated', {
1021
1118
  totalTokens: newTokenCount,
1022
1119
  addedTokens: newTokenCount - originalTokenCount,
1023
- tokenLimit: tokenLimit(this.config.getModel(), userContextLimit),
1120
+ tokenLimit: tokenLimit(this.runtimeState.model, userContextLimit),
1024
1121
  });
1025
1122
  }
1026
1123
  }
@@ -1031,6 +1128,63 @@ export class GeminiClient {
1031
1128
  compressionStatus: CompressionStatus.COMPRESSED,
1032
1129
  };
1033
1130
  }
1131
+ getToolGovernanceEphemerals() {
1132
+ const allowedList = this.readToolList(this.config.getEphemeralSetting('tools.allowed'));
1133
+ const disabledList = this.readToolList(this.config.getEphemeralSetting('tools.disabled') ??
1134
+ this.config.getEphemeralSetting('disabled-tools'));
1135
+ const hasAllowed = allowedList.length > 0;
1136
+ const hasDisabled = disabledList.length > 0;
1137
+ if (!hasAllowed && !hasDisabled) {
1138
+ return undefined;
1139
+ }
1140
+ return {
1141
+ allowed: hasAllowed ? allowedList : undefined,
1142
+ disabled: hasDisabled ? disabledList : undefined,
1143
+ };
1144
+ }
1145
+ readToolList(value) {
1146
+ if (!Array.isArray(value)) {
1147
+ return [];
1148
+ }
1149
+ const filtered = value.filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
1150
+ return filtered.length > 0 ? [...filtered] : [];
1151
+ }
1152
+ buildToolDeclarationsFromView(toolRegistry, view) {
1153
+ if (!toolRegistry) {
1154
+ return [];
1155
+ }
1156
+ const allowedNames = view.listToolNames();
1157
+ if (allowedNames.length === 0) {
1158
+ return [];
1159
+ }
1160
+ const declarations = [];
1161
+ if (typeof toolRegistry.getAllTools === 'function') {
1162
+ const toolsByName = new Map(toolRegistry.getAllTools().map((tool) => [tool.name, tool]));
1163
+ for (const name of allowedNames) {
1164
+ const tool = toolsByName.get(name);
1165
+ if (!tool) {
1166
+ continue;
1167
+ }
1168
+ const schema = tool.schema;
1169
+ if (schema) {
1170
+ declarations.push(schema);
1171
+ }
1172
+ }
1173
+ return declarations;
1174
+ }
1175
+ if (typeof toolRegistry.getFunctionDeclarations === 'function') {
1176
+ const declarationsByName = new Map(toolRegistry
1177
+ .getFunctionDeclarations()
1178
+ .map((decl) => [decl.name, decl]));
1179
+ for (const name of allowedNames) {
1180
+ const declaration = declarationsByName.get(name);
1181
+ if (declaration) {
1182
+ declarations.push(declaration);
1183
+ }
1184
+ }
1185
+ }
1186
+ return declarations;
1187
+ }
1034
1188
  getEnabledToolNamesForPrompt() {
1035
1189
  const toolRegistry = this.config.getToolRegistry();
1036
1190
  if (!toolRegistry ||
@@ -1043,5 +1197,13 @@ export class GeminiClient {
1043
1197
  .map((tool) => tool.name)
1044
1198
  .filter(Boolean)));
1045
1199
  }
1200
+ updateTodoToolAvailabilityFromDeclarations(declarations) {
1201
+ const normalizedNames = new Set(declarations
1202
+ .map((decl) => decl?.name)
1203
+ .filter((name) => typeof name === 'string')
1204
+ .map((name) => name.toLowerCase()));
1205
+ this.todoToolsAvailable =
1206
+ normalizedNames.has('todo_write') && normalizedNames.has('todo_read');
1207
+ }
1046
1208
  }
1047
1209
  //# sourceMappingURL=client.js.map