@vybestack/llxprt-code-agents 0.10.0-nightly.260613.1adad3b34

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 (176) hide show
  1. package/dist/.last_build +0 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +11 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/src/agents/executor-prompt-builder.d.ts +19 -0
  6. package/dist/src/agents/executor-prompt-builder.js +44 -0
  7. package/dist/src/agents/executor-prompt-builder.js.map +1 -0
  8. package/dist/src/agents/executor-termination.d.ts +22 -0
  9. package/dist/src/agents/executor-termination.js +44 -0
  10. package/dist/src/agents/executor-termination.js.map +1 -0
  11. package/dist/src/agents/executor-validation.d.ts +17 -0
  12. package/dist/src/agents/executor-validation.js +35 -0
  13. package/dist/src/agents/executor-validation.js.map +1 -0
  14. package/dist/src/agents/executor.d.ts +136 -0
  15. package/dist/src/agents/executor.js +874 -0
  16. package/dist/src/agents/executor.js.map +1 -0
  17. package/dist/src/agents/invocation.d.ts +45 -0
  18. package/dist/src/agents/invocation.js +118 -0
  19. package/dist/src/agents/invocation.js.map +1 -0
  20. package/dist/src/agents/recovery.d.ts +75 -0
  21. package/dist/src/agents/recovery.js +107 -0
  22. package/dist/src/agents/recovery.js.map +1 -0
  23. package/dist/src/agents/types.d.ts +149 -0
  24. package/dist/src/agents/types.js +18 -0
  25. package/dist/src/agents/types.js.map +1 -0
  26. package/dist/src/agents/utils.d.ts +15 -0
  27. package/dist/src/agents/utils.js +28 -0
  28. package/dist/src/agents/utils.js.map +1 -0
  29. package/dist/src/compression/CompressionHandler.d.ts +178 -0
  30. package/dist/src/compression/CompressionHandler.js +630 -0
  31. package/dist/src/compression/CompressionHandler.js.map +1 -0
  32. package/dist/src/compression/HighDensityStrategy.d.ts +113 -0
  33. package/dist/src/compression/HighDensityStrategy.js +769 -0
  34. package/dist/src/compression/HighDensityStrategy.js.map +1 -0
  35. package/dist/src/compression/MiddleOutStrategy.d.ts +27 -0
  36. package/dist/src/compression/MiddleOutStrategy.js +347 -0
  37. package/dist/src/compression/MiddleOutStrategy.js.map +1 -0
  38. package/dist/src/compression/OneShotStrategy.d.ts +22 -0
  39. package/dist/src/compression/OneShotStrategy.js +235 -0
  40. package/dist/src/compression/OneShotStrategy.js.map +1 -0
  41. package/dist/src/compression/TopDownTruncationStrategy.d.ts +13 -0
  42. package/dist/src/compression/TopDownTruncationStrategy.js +74 -0
  43. package/dist/src/compression/TopDownTruncationStrategy.js.map +1 -0
  44. package/dist/src/compression/compressionBudgeting.d.ts +34 -0
  45. package/dist/src/compression/compressionBudgeting.js +139 -0
  46. package/dist/src/compression/compressionBudgeting.js.map +1 -0
  47. package/dist/src/compression/compressionStrategyFactory.d.ts +23 -0
  48. package/dist/src/compression/compressionStrategyFactory.js +40 -0
  49. package/dist/src/compression/compressionStrategyFactory.js.map +1 -0
  50. package/dist/src/compression/index.d.ts +16 -0
  51. package/dist/src/compression/index.js +17 -0
  52. package/dist/src/compression/index.js.map +1 -0
  53. package/dist/src/compression/reasoningUtils.d.ts +18 -0
  54. package/dist/src/compression/reasoningUtils.js +22 -0
  55. package/dist/src/compression/reasoningUtils.js.map +1 -0
  56. package/dist/src/compression/utils.d.ts +114 -0
  57. package/dist/src/compression/utils.js +316 -0
  58. package/dist/src/compression/utils.js.map +1 -0
  59. package/dist/src/core/AgentHookManager.d.ts +41 -0
  60. package/dist/src/core/AgentHookManager.js +65 -0
  61. package/dist/src/core/AgentHookManager.js.map +1 -0
  62. package/dist/src/core/ChatSessionFactory.d.ts +50 -0
  63. package/dist/src/core/ChatSessionFactory.js +216 -0
  64. package/dist/src/core/ChatSessionFactory.js.map +1 -0
  65. package/dist/src/core/ConversationManager.d.ts +113 -0
  66. package/dist/src/core/ConversationManager.js +311 -0
  67. package/dist/src/core/ConversationManager.js.map +1 -0
  68. package/dist/src/core/DirectMessageProcessor.d.ts +79 -0
  69. package/dist/src/core/DirectMessageProcessor.js +478 -0
  70. package/dist/src/core/DirectMessageProcessor.js.map +1 -0
  71. package/dist/src/core/IdeContextTracker.d.ts +56 -0
  72. package/dist/src/core/IdeContextTracker.js +207 -0
  73. package/dist/src/core/IdeContextTracker.js.map +1 -0
  74. package/dist/src/core/MessageConverter.d.ts +78 -0
  75. package/dist/src/core/MessageConverter.js +492 -0
  76. package/dist/src/core/MessageConverter.js.map +1 -0
  77. package/dist/src/core/MessageStreamOrchestrator.d.ts +85 -0
  78. package/dist/src/core/MessageStreamOrchestrator.js +448 -0
  79. package/dist/src/core/MessageStreamOrchestrator.js.map +1 -0
  80. package/dist/src/core/MessageStreamTerminalHandler.d.ts +16 -0
  81. package/dist/src/core/MessageStreamTerminalHandler.js +170 -0
  82. package/dist/src/core/MessageStreamTerminalHandler.js.map +1 -0
  83. package/dist/src/core/StreamProcessor.d.ts +105 -0
  84. package/dist/src/core/StreamProcessor.js +660 -0
  85. package/dist/src/core/StreamProcessor.js.map +1 -0
  86. package/dist/src/core/TodoContinuationService.d.ts +74 -0
  87. package/dist/src/core/TodoContinuationService.js +312 -0
  88. package/dist/src/core/TodoContinuationService.js.map +1 -0
  89. package/dist/src/core/TurnProcessor.d.ts +91 -0
  90. package/dist/src/core/TurnProcessor.js +540 -0
  91. package/dist/src/core/TurnProcessor.js.map +1 -0
  92. package/dist/src/core/baseLlmClient.d.ts +115 -0
  93. package/dist/src/core/baseLlmClient.js +231 -0
  94. package/dist/src/core/baseLlmClient.js.map +1 -0
  95. package/dist/src/core/bucketFailoverIntegration.d.ts +62 -0
  96. package/dist/src/core/bucketFailoverIntegration.js +186 -0
  97. package/dist/src/core/bucketFailoverIntegration.js.map +1 -0
  98. package/dist/src/core/chatSession.d.ts +121 -0
  99. package/dist/src/core/chatSession.js +720 -0
  100. package/dist/src/core/chatSession.js.map +1 -0
  101. package/dist/src/core/client.d.ts +132 -0
  102. package/dist/src/core/client.js +524 -0
  103. package/dist/src/core/client.js.map +1 -0
  104. package/dist/src/core/clientHelpers.d.ts +15 -0
  105. package/dist/src/core/clientHelpers.js +122 -0
  106. package/dist/src/core/clientHelpers.js.map +1 -0
  107. package/dist/src/core/clientLlmUtilities.d.ts +30 -0
  108. package/dist/src/core/clientLlmUtilities.js +125 -0
  109. package/dist/src/core/clientLlmUtilities.js.map +1 -0
  110. package/dist/src/core/clientToolGovernance.d.ts +37 -0
  111. package/dist/src/core/clientToolGovernance.js +104 -0
  112. package/dist/src/core/clientToolGovernance.js.map +1 -0
  113. package/dist/src/core/compression-config.d.ts +12 -0
  114. package/dist/src/core/compression-config.js +21 -0
  115. package/dist/src/core/compression-config.js.map +1 -0
  116. package/dist/src/core/coreToolScheduler.d.ts +105 -0
  117. package/dist/src/core/coreToolScheduler.js +453 -0
  118. package/dist/src/core/coreToolScheduler.js.map +1 -0
  119. package/dist/src/core/hookToolRestrictions.d.ts +18 -0
  120. package/dist/src/core/hookToolRestrictions.js +187 -0
  121. package/dist/src/core/hookToolRestrictions.js.map +1 -0
  122. package/dist/src/core/nonInteractiveToolExecutor.d.ts +17 -0
  123. package/dist/src/core/nonInteractiveToolExecutor.js +102 -0
  124. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -0
  125. package/dist/src/core/subagent.d.ts +105 -0
  126. package/dist/src/core/subagent.js +650 -0
  127. package/dist/src/core/subagent.js.map +1 -0
  128. package/dist/src/core/subagentExecution.d.ts +104 -0
  129. package/dist/src/core/subagentExecution.js +275 -0
  130. package/dist/src/core/subagentExecution.js.map +1 -0
  131. package/dist/src/core/subagentOrchestrator.d.ts +84 -0
  132. package/dist/src/core/subagentOrchestrator.js +503 -0
  133. package/dist/src/core/subagentOrchestrator.js.map +1 -0
  134. package/dist/src/core/subagentRuntimeSetup.d.ts +75 -0
  135. package/dist/src/core/subagentRuntimeSetup.js +387 -0
  136. package/dist/src/core/subagentRuntimeSetup.js.map +1 -0
  137. package/dist/src/core/subagentScheduler.d.ts +17 -0
  138. package/dist/src/core/subagentScheduler.js +7 -0
  139. package/dist/src/core/subagentScheduler.js.map +1 -0
  140. package/dist/src/core/subagentToolProcessing.d.ts +51 -0
  141. package/dist/src/core/subagentToolProcessing.js +359 -0
  142. package/dist/src/core/subagentToolProcessing.js.map +1 -0
  143. package/dist/src/core/toolGovernance.d.ts +18 -0
  144. package/dist/src/core/toolGovernance.js +50 -0
  145. package/dist/src/core/toolGovernance.js.map +1 -0
  146. package/dist/src/core/turn.d.ts +47 -0
  147. package/dist/src/core/turn.js +455 -0
  148. package/dist/src/core/turn.js.map +1 -0
  149. package/dist/src/core/turnLogging.d.ts +35 -0
  150. package/dist/src/core/turnLogging.js +80 -0
  151. package/dist/src/core/turnLogging.js.map +1 -0
  152. package/dist/src/index.d.ts +32 -0
  153. package/dist/src/index.js +44 -0
  154. package/dist/src/index.js.map +1 -0
  155. package/dist/src/scheduler/confirmation-coordinator.d.ts +139 -0
  156. package/dist/src/scheduler/confirmation-coordinator.js +443 -0
  157. package/dist/src/scheduler/confirmation-coordinator.js.map +1 -0
  158. package/dist/src/scheduler/result-aggregator.d.ts +127 -0
  159. package/dist/src/scheduler/result-aggregator.js +302 -0
  160. package/dist/src/scheduler/result-aggregator.js.map +1 -0
  161. package/dist/src/scheduler/status-transitions.d.ts +39 -0
  162. package/dist/src/scheduler/status-transitions.js +184 -0
  163. package/dist/src/scheduler/status-transitions.js.map +1 -0
  164. package/dist/src/scheduler/tool-dispatcher.d.ts +40 -0
  165. package/dist/src/scheduler/tool-dispatcher.js +120 -0
  166. package/dist/src/scheduler/tool-dispatcher.js.map +1 -0
  167. package/dist/src/scheduler/tool-executor.d.ts +44 -0
  168. package/dist/src/scheduler/tool-executor.js +117 -0
  169. package/dist/src/scheduler/tool-executor.js.map +1 -0
  170. package/dist/src/scheduler/utils.d.ts +11 -0
  171. package/dist/src/scheduler/utils.js +19 -0
  172. package/dist/src/scheduler/utils.js.map +1 -0
  173. package/dist/src/tools/task.d.ts +128 -0
  174. package/dist/src/tools/task.js +932 -0
  175. package/dist/src/tools/task.js.map +1 -0
  176. package/package.json +54 -0
@@ -0,0 +1,720 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /* eslint-disable max-lines -- ChatSession coordinates legacy decomposed modules; further splitting is outside this change. */
7
+ // ChatSession — thin coordinator that wires up the decomposed modules.
8
+ import fs from 'node:fs/promises';
9
+ import os from 'node:os';
10
+ import path from 'node:path';
11
+ import { DebugLogger } from '@vybestack/llxprt-code-core/debug/index.js';
12
+ import { triggerPreCompressHook } from '@vybestack/llxprt-code-core/core/lifecycleHookTriggers.js';
13
+ import { PreCompressTrigger } from '@vybestack/llxprt-code-core/hooks/types.js';
14
+ import { SettingsService } from '@vybestack/llxprt-code-settings';
15
+ import { getProviderKeyStorage } from '@vybestack/llxprt-code-core/storage/provider-key-storage.js';
16
+ import { createRuntimeInvocationContext } from '@vybestack/llxprt-code-core/runtime/RuntimeInvocationContext.js';
17
+ import { isLoadBalancerProfile } from '@vybestack/llxprt-code-settings';
18
+ // Decomposed modules
19
+ import { CompressionHandler } from '../compression/CompressionHandler.js';
20
+ import { ConversationManager } from './ConversationManager.js';
21
+ import { TurnProcessor } from './TurnProcessor.js';
22
+ import { StreamProcessor } from './StreamProcessor.js';
23
+ import { DirectMessageProcessor } from './DirectMessageProcessor.js';
24
+ import { convertPartListUnionToIContent, convertIContentToResponse, validateHistory, } from './MessageConverter.js';
25
+ // Re-exports — consumers import from './chatSession.js'
26
+ export { StreamEventType, } from '@vybestack/llxprt-code-core/core/chatSessionTypes.js';
27
+ export { InvalidStreamError, EmptyStreamError, isSchemaDepthError, } from '@vybestack/llxprt-code-core/core/chatSessionTypes.js';
28
+ export { aggregateTextWithSpacing, isValidNonThoughtTextPart, } from './MessageConverter.js';
29
+ import { CompressionProfileNotFoundError } from '@vybestack/llxprt-code-core/core/compression/types.js';
30
+ /**
31
+ * Error thrown when agent execution is stopped by a hook.
32
+ */
33
+ export class AgentExecutionStoppedError extends Error {
34
+ reason;
35
+ systemMessage;
36
+ contextCleared;
37
+ constructor(reason, systemMessage, contextCleared) {
38
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional falsy coalescing: empty systemMessage should fall through to reason
39
+ super(`Agent execution stopped: ${systemMessage || reason}`);
40
+ this.name = 'AgentExecutionStoppedError';
41
+ this.reason = reason;
42
+ this.systemMessage = systemMessage;
43
+ this.contextCleared = contextCleared;
44
+ }
45
+ }
46
+ /**
47
+ * Error thrown when agent execution is blocked by a hook.
48
+ */
49
+ export class AgentExecutionBlockedError extends Error {
50
+ reason;
51
+ systemMessage;
52
+ syntheticResponse;
53
+ contextCleared;
54
+ constructor(reason, syntheticResponse, systemMessage, contextCleared) {
55
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional falsy coalescing: empty systemMessage should fall through to reason
56
+ super(`Agent execution blocked: ${systemMessage || reason}`);
57
+ this.name = 'AgentExecutionBlockedError';
58
+ this.reason = reason;
59
+ this.systemMessage = systemMessage;
60
+ this.syntheticResponse = syntheticResponse;
61
+ this.contextCleared = contextCleared;
62
+ }
63
+ }
64
+ class CompressionLoadBalancingProvider {
65
+ strategy;
66
+ candidates;
67
+ name = 'load-balancer';
68
+ selectedRoundRobinCandidate;
69
+ constructor(strategy, candidates, initialIndex) {
70
+ this.strategy = strategy;
71
+ this.candidates = candidates;
72
+ if (candidates.length === 0) {
73
+ throw new Error('Load-balanced compression profile requires subprofiles');
74
+ }
75
+ if (strategy === 'round-robin') {
76
+ this.selectedRoundRobinCandidate =
77
+ this.candidates[initialIndex % this.candidates.length];
78
+ }
79
+ }
80
+ async getModels() {
81
+ return [];
82
+ }
83
+ getDefaultModel() {
84
+ return this.candidates[0]?.resolved?.model ?? '';
85
+ }
86
+ getServerTools() {
87
+ return [];
88
+ }
89
+ async invokeServerTool(toolName) {
90
+ throw new Error(`Server tool '${toolName}' not supported by compression load-balancer provider`);
91
+ }
92
+ async *generateChatCompletion(optionsOrContent) {
93
+ const options = Array.isArray(optionsOrContent)
94
+ ? { contents: optionsOrContent }
95
+ : optionsOrContent;
96
+ if (this.strategy === 'failover') {
97
+ yield* this.generateWithFailover(options);
98
+ return;
99
+ }
100
+ yield* this.generateWithCandidate(this.selectedRoundRobinCandidate ?? this.candidates[0], options);
101
+ }
102
+ async *generateWithFailover(options) {
103
+ let lastError;
104
+ for (const candidate of this.candidates) {
105
+ try {
106
+ const bufferedChunks = [];
107
+ for await (const chunk of this.generateWithCandidate(candidate, options)) {
108
+ bufferedChunks.push(chunk);
109
+ }
110
+ yield* bufferedChunks;
111
+ return;
112
+ }
113
+ catch (error) {
114
+ lastError = error;
115
+ }
116
+ }
117
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
118
+ }
119
+ async *generateWithCandidate(candidate, options) {
120
+ const candidateOptions = {
121
+ ...options,
122
+ runtime: candidate.runtime,
123
+ settings: candidate.runtime
124
+ .settingsService,
125
+ config: candidate.config,
126
+ resolved: {
127
+ ...options.resolved,
128
+ ...candidate.resolved,
129
+ },
130
+ invocation: candidate.invocation,
131
+ metadata: {
132
+ ...options.metadata,
133
+ ...candidate.invocation.metadata,
134
+ selectedCompressionProfile: candidate.profileName,
135
+ },
136
+ };
137
+ yield* candidate.provider.generateChatCompletion(candidateOptions);
138
+ }
139
+ }
140
+ /**
141
+ * Chat session that enables sending messages to the model with previous
142
+ * conversation context.
143
+ *
144
+ * @remarks
145
+ * The session maintains all the turns between user and model.
146
+ * Delegates to focused modules: CompressionHandler, ConversationManager,
147
+ * TurnProcessor, StreamProcessor, DirectMessageProcessor.
148
+ */
149
+ export class ChatSession {
150
+ logger = new DebugLogger('llxprt:gemini:chat');
151
+ runtimeState;
152
+ historyService;
153
+ runtimeContext;
154
+ generationConfig;
155
+ // Composed modules
156
+ compressionHandler;
157
+ conversationManager;
158
+ turnProcessor;
159
+ streamProcessor;
160
+ directMessageProcessor;
161
+ compressionLoadBalancerRoundRobinIndexes = new Map();
162
+ constructor(view, contentGenerator, generationConfig = {}, initialHistory = []) {
163
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
164
+ if (view == null) {
165
+ throw new Error('AgentRuntimeContext is required for ChatSession');
166
+ }
167
+ this.runtimeContext = view;
168
+ this.runtimeState = view.state;
169
+ this.historyService = view.history;
170
+ this.generationConfig = generationConfig;
171
+ void contentGenerator;
172
+ // Wire density-dirty tracking on historyService.add
173
+ this._installDensityWrapper();
174
+ validateHistory(initialHistory);
175
+ const model = this.runtimeState.model;
176
+ this.logger.debug('ChatSession initialized:', {
177
+ model,
178
+ initialHistoryLength: initialHistory.length,
179
+ hasHistoryService: true,
180
+ hasRuntimeState: true,
181
+ });
182
+ // Create composed modules
183
+ const providerResolver = (ctx) => this.resolveProviderForRuntime(ctx);
184
+ const providerRuntimeBuilder = (s, m) => this.buildProviderRuntime(s, m);
185
+ const makePositionMatcher = () => this._makePositionMatcher();
186
+ const resolveBaseUrl = (p) => this.resolveProviderBaseUrl(p);
187
+ this.compressionHandler = new CompressionHandler(view, this.historyService, this.generationConfig, this.resolveCompressionProvider.bind(this), async (context) => {
188
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
189
+ const config = view.providerRuntime?.config;
190
+ if (config) {
191
+ await triggerPreCompressHook(config, context.trigger === 'auto'
192
+ ? PreCompressTrigger.Auto
193
+ : PreCompressTrigger.Manual);
194
+ }
195
+ });
196
+ this.conversationManager = new ConversationManager(this.historyService, view, model);
197
+ this.conversationManager.importInitialHistory(initialHistory, model);
198
+ this.streamProcessor = new StreamProcessor(view, this.conversationManager, this.compressionHandler, providerResolver, providerRuntimeBuilder, this.historyService, this.generationConfig);
199
+ const { turnProcessor, directMessageProcessor } = this._buildMessageProcessors(view, providerResolver, providerRuntimeBuilder, makePositionMatcher, resolveBaseUrl);
200
+ this.turnProcessor = turnProcessor;
201
+ this.directMessageProcessor = directMessageProcessor;
202
+ }
203
+ _buildMessageProcessors(view, providerResolver, providerRuntimeBuilder, makePositionMatcher, resolveBaseUrl) {
204
+ const turnProcessor = new TurnProcessor(view, this.compressionHandler, providerResolver, providerRuntimeBuilder, this.generationConfig, this.historyService, this.streamProcessor, makePositionMatcher, resolveBaseUrl);
205
+ const directMessageProcessor = new DirectMessageProcessor(view, providerResolver, providerRuntimeBuilder, this.generationConfig, this.historyService, makePositionMatcher);
206
+ return { turnProcessor, directMessageProcessor };
207
+ }
208
+ // ── Density wrapper ──────────────────────────────────────────────
209
+ static DENSITY_WRAPPED = Symbol('densityWrapped');
210
+ _installDensityWrapper() {
211
+ if (typeof this.historyService.add !== 'function')
212
+ return;
213
+ const hs = this.historyService;
214
+ if (hs[ChatSession.DENSITY_WRAPPED] === true)
215
+ return;
216
+ const originalAdd = this.historyService.add.bind(this.historyService);
217
+ this.historyService.add = (...args) => {
218
+ const result = originalAdd(...args);
219
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
220
+ this.compressionHandler?.markDensityDirty();
221
+ return result;
222
+ };
223
+ hs[ChatSession.DENSITY_WRAPPED] = true;
224
+ }
225
+ // ── Provider resolution (stays on coordinator) ───────────────────
226
+ getActiveProvider() {
227
+ try {
228
+ return this.runtimeContext.provider.getActiveProvider();
229
+ }
230
+ catch {
231
+ return undefined;
232
+ }
233
+ }
234
+ lookupProviderByName(adapter, desiredProviderName, compressionProfileName) {
235
+ if (typeof adapter.getProviderByName !== 'function') {
236
+ return undefined;
237
+ }
238
+ try {
239
+ const candidate = adapter.getProviderByName(desiredProviderName);
240
+ if (candidate !== undefined) {
241
+ const active = this.getActiveProvider();
242
+ if (active !== undefined && active.name !== desiredProviderName) {
243
+ this.logger.debug(() => `[ChatSession] selected provider '${desiredProviderName}' via getProviderByName (active remains '${active.name}') [${compressionProfileName}]`);
244
+ }
245
+ return candidate;
246
+ }
247
+ }
248
+ catch (error) {
249
+ this.logger.debug(() => `[ChatSession] provider lookup skipped (${compressionProfileName}): ${error instanceof Error ? error.message : String(error)}`);
250
+ }
251
+ return undefined;
252
+ }
253
+ resolveProviderForRuntime(compressionProfileName) {
254
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
255
+ const desiredProviderName = this.runtimeState.provider?.trim();
256
+ const adapter = this.runtimeContext.provider;
257
+ if (desiredProviderName) {
258
+ const candidate = this.lookupProviderByName(adapter, desiredProviderName, compressionProfileName);
259
+ if (candidate) {
260
+ return candidate;
261
+ }
262
+ }
263
+ let provider = this.getActiveProvider();
264
+ if (!provider) {
265
+ throw new Error('No active provider configured');
266
+ }
267
+ if (desiredProviderName && provider.name !== desiredProviderName) {
268
+ const previousProviderName = provider.name;
269
+ try {
270
+ adapter.setActiveProvider(desiredProviderName);
271
+ const updatedProvider = adapter.getActiveProvider();
272
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
273
+ if (updatedProvider != null) {
274
+ provider = updatedProvider;
275
+ }
276
+ this.logger.debug(() => `[ChatSession] enforced provider switch to '${desiredProviderName}' (previous '${previousProviderName}') [${compressionProfileName}]`);
277
+ }
278
+ catch (error) {
279
+ this.logger.debug(() => `[ChatSession] provider switch skipped (${compressionProfileName}, read-only context): ${error instanceof Error ? error.message : String(error)}`);
280
+ }
281
+ }
282
+ return provider;
283
+ }
284
+ resolveExplicitCompressionProvider(profileName, providerName, lookupProviderName = providerName) {
285
+ const adapter = this.runtimeContext.provider;
286
+ const lookupResult = this.lookupProviderByName(adapter, lookupProviderName, `compression.profile:${profileName}`);
287
+ if (lookupResult) {
288
+ return lookupResult;
289
+ }
290
+ const activeProvider = this.getActiveProvider();
291
+ if (activeProvider?.name === providerName ||
292
+ activeProvider?.name === lookupProviderName) {
293
+ return activeProvider;
294
+ }
295
+ throw new CompressionProfileNotFoundError(profileName, `provider '${lookupProviderName}' is not available for compression.profile: provider lookup by name is required when it is not already active`);
296
+ }
297
+ providerSupportsIContent(provider) {
298
+ if (!provider)
299
+ return false;
300
+ return (typeof provider
301
+ .generateChatCompletion === 'function');
302
+ }
303
+ async resolveCompressionProvider(profileName) {
304
+ if (!profileName) {
305
+ const provider = this.resolveProviderForRuntime('ChatSession.resolveCompressionProvider.default');
306
+ const runtime = this.runtimeContext.providerRuntime;
307
+ return {
308
+ provider,
309
+ runtime,
310
+ config: runtime.config,
311
+ };
312
+ }
313
+ const config = this.runtimeContext.providerRuntime.config;
314
+ const profileManager = config?.getProfileManager();
315
+ if (!profileManager) {
316
+ throw new CompressionProfileNotFoundError(profileName, 'profile manager is unavailable');
317
+ }
318
+ let profile;
319
+ try {
320
+ profile = await profileManager.loadProfile(profileName);
321
+ }
322
+ catch (error) {
323
+ throw new CompressionProfileNotFoundError(profileName, error instanceof Error ? error.message : String(error));
324
+ }
325
+ if (isLoadBalancerProfile(profile)) {
326
+ return this.resolveLoadBalancedCompressionProvider(profileName, profile, config, profileManager);
327
+ }
328
+ return this.resolveStandardCompressionProvider(profileName, profile, profileManager, config);
329
+ }
330
+ async resolveLoadBalancedCompressionProvider(profileName, profile, config, profileManager) {
331
+ const candidates = await this.buildCompressionLoadBalancerCandidates(profileName, profile, config, profileManager);
332
+ const strategy = profile.policy === 'failover' ? 'failover' : 'round-robin';
333
+ const initialIndex = this.compressionLoadBalancerRoundRobinIndexes.get(profileName) ?? 0;
334
+ if (strategy === 'round-robin') {
335
+ this.compressionLoadBalancerRoundRobinIndexes.set(profileName, (initialIndex + 1) % candidates.length);
336
+ }
337
+ const provider = new CompressionLoadBalancingProvider(strategy, candidates, initialIndex);
338
+ const runtimeId = `${this.runtimeContext.providerRuntime.runtimeId}::compression-profile:${profileName}`;
339
+ const settings = new SettingsService();
340
+ settings.setCurrentProfileName(profileName);
341
+ settings.set('activeProvider', 'load-balancer');
342
+ settings.set('model', profile.model || provider.getDefaultModel());
343
+ for (const [key, value] of Object.entries(profile.ephemeralSettings)) {
344
+ if (value !== undefined) {
345
+ settings.set(key, value);
346
+ }
347
+ }
348
+ const metadata = {
349
+ ...(this.runtimeContext.providerRuntime.metadata ?? {}),
350
+ source: 'ChatSession.resolveCompressionProvider',
351
+ compressionProfile: profileName,
352
+ compressionProvider: 'load-balancer',
353
+ runtimeId,
354
+ provider: 'load-balancer',
355
+ model: settings.get('model'),
356
+ };
357
+ const runtime = {
358
+ settingsService: settings,
359
+ config,
360
+ runtimeId,
361
+ metadata,
362
+ };
363
+ const invocation = createRuntimeInvocationContext({
364
+ runtime,
365
+ settings,
366
+ providerName: 'load-balancer',
367
+ ephemeralsSnapshot: this.buildCompressionProfileEphemeralsSnapshot(settings, 'load-balancer'),
368
+ metadata,
369
+ fallbackRuntimeId: runtimeId,
370
+ });
371
+ return {
372
+ provider,
373
+ runtime,
374
+ config,
375
+ invocation,
376
+ };
377
+ }
378
+ async buildCompressionLoadBalancerCandidates(profileName, profile, config, profileManager) {
379
+ const candidates = [];
380
+ for (const subProfileName of profile.profiles) {
381
+ const subProfile = await profileManager.loadProfile(subProfileName);
382
+ if (isLoadBalancerProfile(subProfile)) {
383
+ throw new CompressionProfileNotFoundError(profileName, `load-balanced compression profile references nested load-balanced profile '${subProfileName}'`);
384
+ }
385
+ const candidate = await this.resolveStandardCompressionProvider(subProfileName, subProfile, profileManager, config, profile.ephemeralSettings);
386
+ if (!candidate.invocation) {
387
+ throw new CompressionProfileNotFoundError(profileName, `failed to build invocation context for subprofile '${subProfileName}'`);
388
+ }
389
+ candidates.push({
390
+ profileName: subProfileName,
391
+ provider: candidate.provider,
392
+ runtime: candidate.runtime,
393
+ config: candidate.config,
394
+ resolved: candidate.resolved,
395
+ invocation: candidate.invocation,
396
+ });
397
+ }
398
+ return candidates;
399
+ }
400
+ async resolveStandardCompressionProvider(profileName, profile, profileManager, config, parentEphemerals = {}) {
401
+ const provider = this.resolveExplicitCompressionProvider(profileName, profile.provider);
402
+ const profileSettings = new SettingsService();
403
+ await profileManager.applyLoadedProfile(profileName, profile, profileSettings);
404
+ this.applyCompressionProfileParentEphemerals(profileSettings, profile.provider, parentEphemerals);
405
+ this.applyCompressionProfileSettings(profileSettings, profileName, profile);
406
+ const runtimeId = `${this.runtimeContext.providerRuntime.runtimeId}::compression-profile:${profileName}`;
407
+ const metadata = {
408
+ ...(this.runtimeContext.providerRuntime.metadata ?? {}),
409
+ source: 'ChatSession.resolveCompressionProvider',
410
+ compressionProfile: profileName,
411
+ compressionProvider: profile.provider,
412
+ runtimeId,
413
+ provider: profile.provider,
414
+ model: profile.model,
415
+ };
416
+ const runtime = {
417
+ settingsService: profileSettings,
418
+ config,
419
+ runtimeId,
420
+ metadata,
421
+ };
422
+ const resolved = await this.buildCompressionProfileResolvedOptions(profileSettings, profile);
423
+ const invocation = createRuntimeInvocationContext({
424
+ runtime,
425
+ settings: profileSettings,
426
+ providerName: profile.provider,
427
+ ephemeralsSnapshot: this.buildCompressionProfileEphemeralsSnapshot(profileSettings, profile.provider),
428
+ metadata,
429
+ fallbackRuntimeId: runtimeId,
430
+ });
431
+ return {
432
+ provider,
433
+ runtime,
434
+ config,
435
+ resolved,
436
+ invocation,
437
+ };
438
+ }
439
+ applyCompressionProfileSettings(profileSettings, profileName, profile) {
440
+ const provider = profile.provider;
441
+ const ephemerals = profile.ephemeralSettings;
442
+ profileSettings.setCurrentProfileName(profileName);
443
+ profileSettings.set('activeProvider', provider);
444
+ profileSettings.set('model', profile.model);
445
+ profileSettings.setProviderSetting(provider, 'enabled', true);
446
+ profileSettings.setProviderSetting(provider, 'model', profile.model);
447
+ for (const [key, value] of Object.entries(ephemerals)) {
448
+ if (value !== undefined) {
449
+ profileSettings.set(key, value);
450
+ profileSettings.setProviderSetting(provider, key, value);
451
+ }
452
+ }
453
+ this.applyCompressionProfileAuthSettings(profileSettings, provider, ephemerals);
454
+ this.applyCompressionProfileModelParams(profileSettings, provider, profile.modelParams);
455
+ if (profile.auth) {
456
+ profileSettings.set('auth.type', profile.auth.type);
457
+ profileSettings.setProviderSetting(provider, 'auth', profile.auth);
458
+ if (profile.auth.buckets) {
459
+ profileSettings.set('auth.buckets', profile.auth.buckets);
460
+ }
461
+ }
462
+ }
463
+ applyCompressionProfileAuthSettings(profileSettings, provider, ephemerals) {
464
+ this.copyProfileSettingAliases(profileSettings, provider, ephemerals, [
465
+ { sourceKey: 'auth-key', globalKey: 'auth-key', providerKey: 'auth-key' },
466
+ {
467
+ sourceKey: 'auth-keyfile',
468
+ globalKey: 'auth-keyfile',
469
+ providerKey: 'auth-keyfile',
470
+ },
471
+ {
472
+ sourceKey: 'auth-key-name',
473
+ globalKey: 'auth-key-name',
474
+ providerKey: 'auth-key-name',
475
+ },
476
+ ]);
477
+ for (const key of ['base-url', 'sandbox-base-url', 'api-version']) {
478
+ const value = ephemerals[key];
479
+ if (value !== undefined) {
480
+ profileSettings.setProviderSetting(provider, key, value);
481
+ }
482
+ }
483
+ }
484
+ copyProfileSettingAliases(profileSettings, provider, ephemerals, aliases) {
485
+ for (const alias of aliases) {
486
+ const value = ephemerals[alias.sourceKey];
487
+ if (value === undefined) {
488
+ continue;
489
+ }
490
+ if (alias.globalKey) {
491
+ profileSettings.set(alias.globalKey, value);
492
+ }
493
+ profileSettings.setProviderSetting(provider, alias.providerKey, value);
494
+ }
495
+ }
496
+ applyCompressionProfileModelParams(profileSettings, provider, modelParams) {
497
+ for (const [key, value] of Object.entries(modelParams)) {
498
+ if (value !== undefined) {
499
+ profileSettings.setProviderSetting(provider, key, value);
500
+ }
501
+ }
502
+ const aliases = {
503
+ temperature: modelParams.temperature,
504
+ maxTokens: modelParams.max_tokens,
505
+ topP: modelParams.top_p,
506
+ topK: modelParams.top_k,
507
+ presencePenalty: modelParams.presence_penalty,
508
+ frequencyPenalty: modelParams.frequency_penalty,
509
+ };
510
+ for (const [key, value] of Object.entries(aliases)) {
511
+ if (value !== undefined) {
512
+ profileSettings.setProviderSetting(provider, key, value);
513
+ }
514
+ }
515
+ }
516
+ applyCompressionProfileParentEphemerals(profileSettings, provider, parentEphemerals) {
517
+ for (const [key, value] of Object.entries(parentEphemerals)) {
518
+ if (value !== undefined && profileSettings.get(key) === undefined) {
519
+ profileSettings.set(key, value);
520
+ }
521
+ }
522
+ for (const key of ['custom-headers', 'api-version', 'sandbox-base-url']) {
523
+ const value = parentEphemerals[key];
524
+ if (value !== undefined) {
525
+ profileSettings.setProviderSetting(provider, key, value);
526
+ }
527
+ }
528
+ }
529
+ resolveProviderBaseUrl(_provider) {
530
+ return this.runtimeState.baseUrl;
531
+ }
532
+ buildProviderRuntime(source, metadata = {}) {
533
+ const baseRuntime = this.runtimeContext.providerRuntime;
534
+ const runtimeId =
535
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
536
+ baseRuntime.runtimeId ?? this.runtimeState.runtimeId ?? 'chatSession';
537
+ return {
538
+ ...baseRuntime,
539
+ runtimeId,
540
+ metadata: {
541
+ ...(baseRuntime.metadata ?? {}),
542
+ source,
543
+ ...metadata,
544
+ },
545
+ };
546
+ }
547
+ async buildCompressionProfileResolvedOptions(profileSettings, profile) {
548
+ const providerSettings = profileSettings.getProviderSettings(profile.provider);
549
+ const baseURL = typeof providerSettings['base-url'] === 'string'
550
+ ? providerSettings['base-url']
551
+ : undefined;
552
+ const temperature = typeof providerSettings.temperature === 'number'
553
+ ? providerSettings.temperature
554
+ : undefined;
555
+ const maxTokens = typeof providerSettings.maxTokens === 'number'
556
+ ? providerSettings.maxTokens
557
+ : undefined;
558
+ return {
559
+ model: profile.model,
560
+ ...(baseURL ? { baseURL } : {}),
561
+ ...(await this.resolveCompressionProfileAuthToken(profileSettings, profile.provider)),
562
+ ...(temperature !== undefined ? { temperature } : {}),
563
+ ...(maxTokens !== undefined ? { maxTokens } : {}),
564
+ };
565
+ }
566
+ async resolveCompressionProfileAuthToken(profileSettings, provider) {
567
+ const providerSettings = profileSettings.getProviderSettings(provider);
568
+ const directAuth = this.getStringSettingFromValues([
569
+ providerSettings['auth-key'],
570
+ profileSettings.get('auth-key'),
571
+ ]);
572
+ if (directAuth) {
573
+ return { authToken: directAuth };
574
+ }
575
+ const keyName = this.getStringSettingFromValues([
576
+ providerSettings['auth-key-name'],
577
+ profileSettings.get('auth-key-name'),
578
+ ]);
579
+ if (keyName) {
580
+ const token = await getProviderKeyStorage().getKey(keyName);
581
+ if (token) {
582
+ return { authToken: token };
583
+ }
584
+ }
585
+ const keyFile = this.getStringSettingFromValues([
586
+ providerSettings['auth-keyfile'],
587
+ profileSettings.get('auth-keyfile'),
588
+ ]);
589
+ if (keyFile) {
590
+ const token = (await fs.readFile(this.expandProfilePath(keyFile), 'utf8')).trim();
591
+ if (token) {
592
+ return { authToken: token };
593
+ }
594
+ }
595
+ return {};
596
+ }
597
+ getStringSettingFromValues(values) {
598
+ for (const value of values) {
599
+ if (typeof value === 'string' && value.trim() !== '') {
600
+ return value.trim();
601
+ }
602
+ }
603
+ return undefined;
604
+ }
605
+ expandProfilePath(value) {
606
+ if (value.startsWith('~/')) {
607
+ return path.join(os.homedir(), value.slice(2));
608
+ }
609
+ return value;
610
+ }
611
+ buildCompressionProfileEphemeralsSnapshot(profileSettings, provider) {
612
+ return {
613
+ ...profileSettings.getAllGlobalSettings(),
614
+ [provider]: { ...profileSettings.getProviderSettings(provider) },
615
+ };
616
+ }
617
+ // ── Position matcher (used by multiple modules) ──────────────────
618
+ _makePositionMatcher() {
619
+ return this.conversationManager.makePositionMatcher();
620
+ }
621
+ // ── Public API — thin delegation ─────────────────────────────────
622
+ async sendMessage(params, prompt_id) {
623
+ return this.turnProcessor.sendMessage(params, prompt_id);
624
+ }
625
+ async sendMessageStream(params, prompt_id) {
626
+ return this.turnProcessor.sendMessageStream(params, prompt_id);
627
+ }
628
+ async generateDirectMessage(params, prompt_id) {
629
+ return this.directMessageProcessor.generateDirectMessage(params, prompt_id);
630
+ }
631
+ async waitForIdle() {
632
+ return this.turnProcessor.waitForIdle();
633
+ }
634
+ setSystemInstruction(sysInstr) {
635
+ this.generationConfig.systemInstruction = sysInstr;
636
+ }
637
+ getHistoryService() {
638
+ return this.historyService;
639
+ }
640
+ getToolsView() {
641
+ return this.runtimeContext.tools;
642
+ }
643
+ setTools(tools) {
644
+ this.generationConfig.tools = tools;
645
+ }
646
+ clearTools() {
647
+ this.generationConfig.tools = undefined;
648
+ }
649
+ getHistory(curated = false) {
650
+ return this.conversationManager.getHistory(curated);
651
+ }
652
+ clearHistory() {
653
+ this.conversationManager.clearHistory();
654
+ }
655
+ addHistory(content) {
656
+ this.conversationManager.addHistory(content);
657
+ }
658
+ setHistory(history) {
659
+ this.conversationManager.setHistory(history);
660
+ }
661
+ setActiveTodosProvider(provider) {
662
+ this.compressionHandler.setActiveTodosProvider(provider);
663
+ }
664
+ async performCompression(prompt_id, options) {
665
+ return this.compressionHandler.performCompression(prompt_id, options);
666
+ }
667
+ wasRecentlyCompressed() {
668
+ return this.compressionHandler.wasRecentlyCompressed();
669
+ }
670
+ getLastPromptTokenCount() {
671
+ return this.compressionHandler.lastPromptTokenCount ?? 0;
672
+ }
673
+ recordCompletedToolCalls(_model, _toolCalls) {
674
+ // No-op stub for compatibility
675
+ }
676
+ // Public conversion methods — delegated to standalone functions
677
+ convertPartListUnionToIContent(input) {
678
+ return convertPartListUnionToIContent(input);
679
+ }
680
+ convertIContentToResponse(input) {
681
+ return convertIContentToResponse(input);
682
+ }
683
+ async estimatePendingTokens(contents) {
684
+ return this.turnProcessor.estimatePendingTokens(contents);
685
+ }
686
+ // ── Internal compat pass-throughs (used by tests via `as never` casts) ──
687
+ get densityDirty() {
688
+ return this.compressionHandler.densityDirty;
689
+ }
690
+ set densityDirty(value) {
691
+ this.compressionHandler.densityDirty = value;
692
+ }
693
+ get _suppressDensityDirty() {
694
+ return this.compressionHandler._suppressDensityDirty;
695
+ }
696
+ set _suppressDensityDirty(value) {
697
+ this.compressionHandler._suppressDensityDirty = value;
698
+ }
699
+ async ensureDensityOptimized() {
700
+ return this.compressionHandler.ensureDensityOptimized();
701
+ }
702
+ async ensureCompressionBeforeSend(promptId, pendingTokens, source, trigger = 'auto') {
703
+ return this.compressionHandler.ensureCompressionBeforeSend(promptId, pendingTokens, source, trigger);
704
+ }
705
+ async enforceContextWindow(pendingTokens, promptId) {
706
+ return this.compressionHandler.enforceContextWindow(pendingTokens, promptId);
707
+ }
708
+ shouldCompress(pendingTokens) {
709
+ return this.compressionHandler.shouldCompress(pendingTokens);
710
+ }
711
+ /**
712
+ * Returns the Config instance from the provider runtime.
713
+ * Used by Turn and other consumers to access ephemeral settings.
714
+ */
715
+ getConfig() {
716
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Gemini chat runtime payloads.
717
+ return this.runtimeContext.providerRuntime?.config;
718
+ }
719
+ }
720
+ //# sourceMappingURL=chatSession.js.map