@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.
- package/dist/.last_build +0 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/src/agents/executor-prompt-builder.d.ts +19 -0
- package/dist/src/agents/executor-prompt-builder.js +44 -0
- package/dist/src/agents/executor-prompt-builder.js.map +1 -0
- package/dist/src/agents/executor-termination.d.ts +22 -0
- package/dist/src/agents/executor-termination.js +44 -0
- package/dist/src/agents/executor-termination.js.map +1 -0
- package/dist/src/agents/executor-validation.d.ts +17 -0
- package/dist/src/agents/executor-validation.js +35 -0
- package/dist/src/agents/executor-validation.js.map +1 -0
- package/dist/src/agents/executor.d.ts +136 -0
- package/dist/src/agents/executor.js +874 -0
- package/dist/src/agents/executor.js.map +1 -0
- package/dist/src/agents/invocation.d.ts +45 -0
- package/dist/src/agents/invocation.js +118 -0
- package/dist/src/agents/invocation.js.map +1 -0
- package/dist/src/agents/recovery.d.ts +75 -0
- package/dist/src/agents/recovery.js +107 -0
- package/dist/src/agents/recovery.js.map +1 -0
- package/dist/src/agents/types.d.ts +149 -0
- package/dist/src/agents/types.js +18 -0
- package/dist/src/agents/types.js.map +1 -0
- package/dist/src/agents/utils.d.ts +15 -0
- package/dist/src/agents/utils.js +28 -0
- package/dist/src/agents/utils.js.map +1 -0
- package/dist/src/compression/CompressionHandler.d.ts +178 -0
- package/dist/src/compression/CompressionHandler.js +630 -0
- package/dist/src/compression/CompressionHandler.js.map +1 -0
- package/dist/src/compression/HighDensityStrategy.d.ts +113 -0
- package/dist/src/compression/HighDensityStrategy.js +769 -0
- package/dist/src/compression/HighDensityStrategy.js.map +1 -0
- package/dist/src/compression/MiddleOutStrategy.d.ts +27 -0
- package/dist/src/compression/MiddleOutStrategy.js +347 -0
- package/dist/src/compression/MiddleOutStrategy.js.map +1 -0
- package/dist/src/compression/OneShotStrategy.d.ts +22 -0
- package/dist/src/compression/OneShotStrategy.js +235 -0
- package/dist/src/compression/OneShotStrategy.js.map +1 -0
- package/dist/src/compression/TopDownTruncationStrategy.d.ts +13 -0
- package/dist/src/compression/TopDownTruncationStrategy.js +74 -0
- package/dist/src/compression/TopDownTruncationStrategy.js.map +1 -0
- package/dist/src/compression/compressionBudgeting.d.ts +34 -0
- package/dist/src/compression/compressionBudgeting.js +139 -0
- package/dist/src/compression/compressionBudgeting.js.map +1 -0
- package/dist/src/compression/compressionStrategyFactory.d.ts +23 -0
- package/dist/src/compression/compressionStrategyFactory.js +40 -0
- package/dist/src/compression/compressionStrategyFactory.js.map +1 -0
- package/dist/src/compression/index.d.ts +16 -0
- package/dist/src/compression/index.js +17 -0
- package/dist/src/compression/index.js.map +1 -0
- package/dist/src/compression/reasoningUtils.d.ts +18 -0
- package/dist/src/compression/reasoningUtils.js +22 -0
- package/dist/src/compression/reasoningUtils.js.map +1 -0
- package/dist/src/compression/utils.d.ts +114 -0
- package/dist/src/compression/utils.js +316 -0
- package/dist/src/compression/utils.js.map +1 -0
- package/dist/src/core/AgentHookManager.d.ts +41 -0
- package/dist/src/core/AgentHookManager.js +65 -0
- package/dist/src/core/AgentHookManager.js.map +1 -0
- package/dist/src/core/ChatSessionFactory.d.ts +50 -0
- package/dist/src/core/ChatSessionFactory.js +216 -0
- package/dist/src/core/ChatSessionFactory.js.map +1 -0
- package/dist/src/core/ConversationManager.d.ts +113 -0
- package/dist/src/core/ConversationManager.js +311 -0
- package/dist/src/core/ConversationManager.js.map +1 -0
- package/dist/src/core/DirectMessageProcessor.d.ts +79 -0
- package/dist/src/core/DirectMessageProcessor.js +478 -0
- package/dist/src/core/DirectMessageProcessor.js.map +1 -0
- package/dist/src/core/IdeContextTracker.d.ts +56 -0
- package/dist/src/core/IdeContextTracker.js +207 -0
- package/dist/src/core/IdeContextTracker.js.map +1 -0
- package/dist/src/core/MessageConverter.d.ts +78 -0
- package/dist/src/core/MessageConverter.js +492 -0
- package/dist/src/core/MessageConverter.js.map +1 -0
- package/dist/src/core/MessageStreamOrchestrator.d.ts +85 -0
- package/dist/src/core/MessageStreamOrchestrator.js +448 -0
- package/dist/src/core/MessageStreamOrchestrator.js.map +1 -0
- package/dist/src/core/MessageStreamTerminalHandler.d.ts +16 -0
- package/dist/src/core/MessageStreamTerminalHandler.js +170 -0
- package/dist/src/core/MessageStreamTerminalHandler.js.map +1 -0
- package/dist/src/core/StreamProcessor.d.ts +105 -0
- package/dist/src/core/StreamProcessor.js +660 -0
- package/dist/src/core/StreamProcessor.js.map +1 -0
- package/dist/src/core/TodoContinuationService.d.ts +74 -0
- package/dist/src/core/TodoContinuationService.js +312 -0
- package/dist/src/core/TodoContinuationService.js.map +1 -0
- package/dist/src/core/TurnProcessor.d.ts +91 -0
- package/dist/src/core/TurnProcessor.js +540 -0
- package/dist/src/core/TurnProcessor.js.map +1 -0
- package/dist/src/core/baseLlmClient.d.ts +115 -0
- package/dist/src/core/baseLlmClient.js +231 -0
- package/dist/src/core/baseLlmClient.js.map +1 -0
- package/dist/src/core/bucketFailoverIntegration.d.ts +62 -0
- package/dist/src/core/bucketFailoverIntegration.js +186 -0
- package/dist/src/core/bucketFailoverIntegration.js.map +1 -0
- package/dist/src/core/chatSession.d.ts +121 -0
- package/dist/src/core/chatSession.js +720 -0
- package/dist/src/core/chatSession.js.map +1 -0
- package/dist/src/core/client.d.ts +132 -0
- package/dist/src/core/client.js +524 -0
- package/dist/src/core/client.js.map +1 -0
- package/dist/src/core/clientHelpers.d.ts +15 -0
- package/dist/src/core/clientHelpers.js +122 -0
- package/dist/src/core/clientHelpers.js.map +1 -0
- package/dist/src/core/clientLlmUtilities.d.ts +30 -0
- package/dist/src/core/clientLlmUtilities.js +125 -0
- package/dist/src/core/clientLlmUtilities.js.map +1 -0
- package/dist/src/core/clientToolGovernance.d.ts +37 -0
- package/dist/src/core/clientToolGovernance.js +104 -0
- package/dist/src/core/clientToolGovernance.js.map +1 -0
- package/dist/src/core/compression-config.d.ts +12 -0
- package/dist/src/core/compression-config.js +21 -0
- package/dist/src/core/compression-config.js.map +1 -0
- package/dist/src/core/coreToolScheduler.d.ts +105 -0
- package/dist/src/core/coreToolScheduler.js +453 -0
- package/dist/src/core/coreToolScheduler.js.map +1 -0
- package/dist/src/core/hookToolRestrictions.d.ts +18 -0
- package/dist/src/core/hookToolRestrictions.js +187 -0
- package/dist/src/core/hookToolRestrictions.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.d.ts +17 -0
- package/dist/src/core/nonInteractiveToolExecutor.js +102 -0
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -0
- package/dist/src/core/subagent.d.ts +105 -0
- package/dist/src/core/subagent.js +650 -0
- package/dist/src/core/subagent.js.map +1 -0
- package/dist/src/core/subagentExecution.d.ts +104 -0
- package/dist/src/core/subagentExecution.js +275 -0
- package/dist/src/core/subagentExecution.js.map +1 -0
- package/dist/src/core/subagentOrchestrator.d.ts +84 -0
- package/dist/src/core/subagentOrchestrator.js +503 -0
- package/dist/src/core/subagentOrchestrator.js.map +1 -0
- package/dist/src/core/subagentRuntimeSetup.d.ts +75 -0
- package/dist/src/core/subagentRuntimeSetup.js +387 -0
- package/dist/src/core/subagentRuntimeSetup.js.map +1 -0
- package/dist/src/core/subagentScheduler.d.ts +17 -0
- package/dist/src/core/subagentScheduler.js +7 -0
- package/dist/src/core/subagentScheduler.js.map +1 -0
- package/dist/src/core/subagentToolProcessing.d.ts +51 -0
- package/dist/src/core/subagentToolProcessing.js +359 -0
- package/dist/src/core/subagentToolProcessing.js.map +1 -0
- package/dist/src/core/toolGovernance.d.ts +18 -0
- package/dist/src/core/toolGovernance.js +50 -0
- package/dist/src/core/toolGovernance.js.map +1 -0
- package/dist/src/core/turn.d.ts +47 -0
- package/dist/src/core/turn.js +455 -0
- package/dist/src/core/turn.js.map +1 -0
- package/dist/src/core/turnLogging.d.ts +35 -0
- package/dist/src/core/turnLogging.js +80 -0
- package/dist/src/core/turnLogging.js.map +1 -0
- package/dist/src/index.d.ts +32 -0
- package/dist/src/index.js +44 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/scheduler/confirmation-coordinator.d.ts +139 -0
- package/dist/src/scheduler/confirmation-coordinator.js +443 -0
- package/dist/src/scheduler/confirmation-coordinator.js.map +1 -0
- package/dist/src/scheduler/result-aggregator.d.ts +127 -0
- package/dist/src/scheduler/result-aggregator.js +302 -0
- package/dist/src/scheduler/result-aggregator.js.map +1 -0
- package/dist/src/scheduler/status-transitions.d.ts +39 -0
- package/dist/src/scheduler/status-transitions.js +184 -0
- package/dist/src/scheduler/status-transitions.js.map +1 -0
- package/dist/src/scheduler/tool-dispatcher.d.ts +40 -0
- package/dist/src/scheduler/tool-dispatcher.js +120 -0
- package/dist/src/scheduler/tool-dispatcher.js.map +1 -0
- package/dist/src/scheduler/tool-executor.d.ts +44 -0
- package/dist/src/scheduler/tool-executor.js +117 -0
- package/dist/src/scheduler/tool-executor.js.map +1 -0
- package/dist/src/scheduler/utils.d.ts +11 -0
- package/dist/src/scheduler/utils.js +19 -0
- package/dist/src/scheduler/utils.js.map +1 -0
- package/dist/src/tools/task.d.ts +128 -0
- package/dist/src/tools/task.js +932 -0
- package/dist/src/tools/task.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { getEnvironmentContext } from '@vybestack/llxprt-code-core/utils/environmentContext.js';
|
|
7
|
+
import { getCoreSystemPromptAsync } from '@vybestack/llxprt-code-core/core/prompts.js';
|
|
8
|
+
import { getToolGovernanceEphemerals, buildToolDeclarationsFromView, getEnabledToolNamesForPrompt, shouldIncludeSubagentDelegationForConfig, } from './clientToolGovernance.js';
|
|
9
|
+
import { reportError } from '@vybestack/llxprt-code-core/utils/errorReporting.js';
|
|
10
|
+
import { ChatSession } from './chatSession.js';
|
|
11
|
+
import { DebugLogger } from '@vybestack/llxprt-code-core/debug/index.js';
|
|
12
|
+
import { HistoryService } from '@vybestack/llxprt-code-core/services/history/HistoryService.js';
|
|
13
|
+
import { ContentConverters } from '@vybestack/llxprt-code-core/services/history/ContentConverters.js';
|
|
14
|
+
import { createSettingsProviderRuntimeContext } from '@vybestack/llxprt-code-core/runtime/settingsRuntimeAdapter.js';
|
|
15
|
+
import { loadAgentRuntime } from '@vybestack/llxprt-code-core/runtime/AgentRuntimeLoader.js';
|
|
16
|
+
import { getErrorMessage } from '@vybestack/llxprt-code-core/utils/errors.js';
|
|
17
|
+
import { isThinkingSupported } from './clientHelpers.js';
|
|
18
|
+
import { estimateTokens as estimateTextTokens } from '@vybestack/llxprt-code-core/utils/toolOutputLimiter.js';
|
|
19
|
+
/**
|
|
20
|
+
* Assembles ephemeral settings into an immutable snapshot for the runtime.
|
|
21
|
+
* Pure function — reads config, no side effects.
|
|
22
|
+
*/
|
|
23
|
+
export function buildSettingsSnapshot(config) {
|
|
24
|
+
const rawCompressionThreshold = config.getEphemeralSetting('compression-threshold');
|
|
25
|
+
const compressionThreshold = typeof rawCompressionThreshold === 'number' &&
|
|
26
|
+
Number.isFinite(rawCompressionThreshold)
|
|
27
|
+
? rawCompressionThreshold
|
|
28
|
+
: undefined;
|
|
29
|
+
const rawContextLimit = config.getEphemeralSetting('context-limit');
|
|
30
|
+
const contextLimit = typeof rawContextLimit === 'number' &&
|
|
31
|
+
Number.isFinite(rawContextLimit) &&
|
|
32
|
+
rawContextLimit > 0
|
|
33
|
+
? rawContextLimit
|
|
34
|
+
: undefined;
|
|
35
|
+
const rawPreserveThreshold = config.getEphemeralSetting('compression-preserve-threshold');
|
|
36
|
+
const preserveThreshold = typeof rawPreserveThreshold === 'number' &&
|
|
37
|
+
Number.isFinite(rawPreserveThreshold)
|
|
38
|
+
? rawPreserveThreshold
|
|
39
|
+
: undefined;
|
|
40
|
+
return {
|
|
41
|
+
compressionThreshold: compressionThreshold ?? 0.85,
|
|
42
|
+
contextLimit,
|
|
43
|
+
preserveThreshold: preserveThreshold ?? 0.2,
|
|
44
|
+
telemetry: { enabled: true, target: null },
|
|
45
|
+
tools: getToolGovernanceEphemerals(config),
|
|
46
|
+
'reasoning.enabled': config.getEphemeralSetting('reasoning.enabled'),
|
|
47
|
+
'reasoning.includeInContext': config.getEphemeralSetting('reasoning.includeInContext'),
|
|
48
|
+
'reasoning.includeInResponse': config.getEphemeralSetting('reasoning.includeInResponse'),
|
|
49
|
+
'reasoning.format': config.getEphemeralSetting('reasoning.format'),
|
|
50
|
+
'reasoning.stripFromContext': config.getEphemeralSetting('reasoning.stripFromContext'),
|
|
51
|
+
'reasoning.effort': config.getEphemeralSetting('reasoning.effort'),
|
|
52
|
+
'reasoning.maxTokens': config.getEphemeralSetting('reasoning.maxTokens'),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Builds the full system instruction: env context, core memory, JIT memory,
|
|
57
|
+
* user memory, MCP instructions, subagent delegation.
|
|
58
|
+
*
|
|
59
|
+
* This is the FULL path used by startChat — differs from the lightweight path
|
|
60
|
+
* in clientLlmUtilities which skips env context, core memory, and JIT memory.
|
|
61
|
+
*/
|
|
62
|
+
export async function buildSystemInstruction(config, enabledToolNames, envParts, model) {
|
|
63
|
+
let userMemory = config.isJitContextEnabled()
|
|
64
|
+
? config.getGlobalMemory()
|
|
65
|
+
: config.getUserMemory();
|
|
66
|
+
const coreMemory = config.getCoreMemory();
|
|
67
|
+
const jitMemory = await config.getJitMemoryForPath(config.getWorkingDir());
|
|
68
|
+
if (jitMemory) {
|
|
69
|
+
userMemory = userMemory ? `${userMemory}\n\n${jitMemory}` : jitMemory;
|
|
70
|
+
}
|
|
71
|
+
const mcpInstructions = config.getMcpClientManager()?.getMcpInstructions();
|
|
72
|
+
const includeSubagentDelegation = await shouldIncludeSubagentDelegationForConfig(config, enabledToolNames);
|
|
73
|
+
const interactionMode = config.isInteractive()
|
|
74
|
+
? 'interactive'
|
|
75
|
+
: 'non-interactive';
|
|
76
|
+
let systemInstruction = await getCoreSystemPromptAsync({
|
|
77
|
+
userMemory,
|
|
78
|
+
coreMemory,
|
|
79
|
+
mcpInstructions,
|
|
80
|
+
model,
|
|
81
|
+
tools: enabledToolNames,
|
|
82
|
+
includeSubagentDelegation,
|
|
83
|
+
interactionMode,
|
|
84
|
+
});
|
|
85
|
+
const envContextText = envParts
|
|
86
|
+
.map((part) => ('text' in part && part.text ? part.text : ''))
|
|
87
|
+
.join('\n');
|
|
88
|
+
if (envContextText) {
|
|
89
|
+
systemInstruction = envContextText + '\n\n' + systemInstruction;
|
|
90
|
+
}
|
|
91
|
+
return systemInstruction;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolves (or creates) the HistoryService and optionally loads extra history.
|
|
95
|
+
*/
|
|
96
|
+
function setupHistoryService(storedHistoryService, extraHistory, runtimeState) {
|
|
97
|
+
const logger = new DebugLogger('llxprt:client:start');
|
|
98
|
+
if (storedHistoryService) {
|
|
99
|
+
logger.debug('Reusing stored HistoryService to preserve UI conversation');
|
|
100
|
+
return { historyService: storedHistoryService, reused: true };
|
|
101
|
+
}
|
|
102
|
+
const historyService = new HistoryService();
|
|
103
|
+
if (extraHistory && extraHistory.length > 0) {
|
|
104
|
+
const currentModel = runtimeState.model;
|
|
105
|
+
for (const content of extraHistory) {
|
|
106
|
+
const turnKey = historyService.generateTurnKey();
|
|
107
|
+
historyService.add(ContentConverters.toIContent(content, undefined, undefined, turnKey), currentModel);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { historyService, reused: false };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Estimates and sets the system prompt token offset on the history service.
|
|
114
|
+
*/
|
|
115
|
+
async function applySystemPromptTokenOffset(historyService, systemInstruction, model, logger) {
|
|
116
|
+
try {
|
|
117
|
+
const tokens = await historyService.estimateTokensForText(systemInstruction, model);
|
|
118
|
+
historyService.setBaseTokenOffset(tokens);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Token estimation failed - use fallback estimation
|
|
122
|
+
logger.debug(() => `Failed to count system instruction tokens for model ${model}, using fallback`);
|
|
123
|
+
historyService.setBaseTokenOffset(estimateTextTokens(systemInstruction));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Builds the GenerateContentConfig with thinking support if applicable.
|
|
128
|
+
*/
|
|
129
|
+
function buildGenerateContentConfig(baseConfig, model) {
|
|
130
|
+
return isThinkingSupported(model)
|
|
131
|
+
? {
|
|
132
|
+
...baseConfig,
|
|
133
|
+
thinkingConfig: { thinkingBudget: -1, includeThoughts: true },
|
|
134
|
+
}
|
|
135
|
+
: baseConfig;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Builds the runtime bundle, tool declarations, and ChatSession instance.
|
|
139
|
+
*/
|
|
140
|
+
async function buildChatFromRuntime(config, runtimeState, contentGenerator, historyService, generateContentConfig, todoContinuationService, toolRegistry, systemInstruction) {
|
|
141
|
+
const model = runtimeState.model;
|
|
142
|
+
const generationConfigWithThinking = buildGenerateContentConfig(generateContentConfig, model);
|
|
143
|
+
const settings = buildSettingsSnapshot(config);
|
|
144
|
+
const providerRuntime = createSettingsProviderRuntimeContext({
|
|
145
|
+
settingsService: config.getSettingsService(),
|
|
146
|
+
config,
|
|
147
|
+
runtimeId: runtimeState.runtimeId,
|
|
148
|
+
metadata: { source: 'AgentClient.startChat' },
|
|
149
|
+
});
|
|
150
|
+
const runtimeBundle = await loadAgentRuntime({
|
|
151
|
+
profile: {
|
|
152
|
+
config,
|
|
153
|
+
state: runtimeState,
|
|
154
|
+
settings,
|
|
155
|
+
providerRuntime,
|
|
156
|
+
contentGeneratorConfig: config.getContentGeneratorConfig(),
|
|
157
|
+
toolRegistry,
|
|
158
|
+
providerManager: config.getProviderManager(),
|
|
159
|
+
},
|
|
160
|
+
overrides: { historyService, contentGenerator },
|
|
161
|
+
});
|
|
162
|
+
const filteredDeclarations = buildToolDeclarationsFromView(toolRegistry, runtimeBundle.toolsView);
|
|
163
|
+
todoContinuationService.updateTodoToolAvailabilityFromDeclarations(filteredDeclarations);
|
|
164
|
+
const tools = [{ functionDeclarations: filteredDeclarations }];
|
|
165
|
+
const chat = new ChatSession(runtimeBundle.runtimeContext, runtimeBundle.contentGenerator, { systemInstruction, ...generationConfigWithThinking, tools }, []);
|
|
166
|
+
chat.setActiveTodosProvider(async () => {
|
|
167
|
+
const todos = await todoContinuationService.readTodoSnapshot();
|
|
168
|
+
const active = todoContinuationService.getActiveTodos(todos);
|
|
169
|
+
if (active.length === 0)
|
|
170
|
+
return undefined;
|
|
171
|
+
return active.map((t) => `- [${t.status}] ${t.content}`).join('\n');
|
|
172
|
+
});
|
|
173
|
+
return chat;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Stateful factory: creates a ChatSession session.
|
|
177
|
+
* Reuses stored HistoryService when available, creates a new one otherwise.
|
|
178
|
+
* Configures thinking, loads the agent runtime, builds tool declarations.
|
|
179
|
+
*/
|
|
180
|
+
export async function createChatSession(deps) {
|
|
181
|
+
const { config, runtimeState, contentGenerator, storedHistoryService, clearStoredHistoryService, extraHistory, generateContentConfig, todoContinuationService, toolRegistry, } = deps;
|
|
182
|
+
const logger = new DebugLogger('llxprt:client:start');
|
|
183
|
+
const { historyService, reused } = setupHistoryService(storedHistoryService, extraHistory, runtimeState);
|
|
184
|
+
const getTokenizerFactory = config['getTokenizerFactory'];
|
|
185
|
+
if (typeof getTokenizerFactory === 'function') {
|
|
186
|
+
const tokenizerFactory = getTokenizerFactory.call(config);
|
|
187
|
+
if (tokenizerFactory) {
|
|
188
|
+
historyService.setTokenizerFactory(tokenizerFactory);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const enabledToolNames = getEnabledToolNamesForPrompt(config);
|
|
192
|
+
const envParts = await getEnvironmentContext(config);
|
|
193
|
+
const model = runtimeState.model;
|
|
194
|
+
logger.debug(() => `DEBUG [client.startChat]: Model from config: ${model}`);
|
|
195
|
+
const systemInstruction = await buildSystemInstruction(config, enabledToolNames, envParts, model);
|
|
196
|
+
await applySystemPromptTokenOffset(historyService, systemInstruction, model, logger);
|
|
197
|
+
logger.debug(() => `DEBUG [client.startChat]: System instruction includes Flash instructions: ${systemInstruction.includes('IMPORTANT: You MUST use the provided tools')}`);
|
|
198
|
+
const chat = await buildChatFromRuntime(config, runtimeState, contentGenerator, historyService, generateContentConfig, todoContinuationService, toolRegistry, systemInstruction);
|
|
199
|
+
if (reused) {
|
|
200
|
+
clearStoredHistoryService();
|
|
201
|
+
}
|
|
202
|
+
return chat;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Wraps createChatSession with error reporting for the startChat call site.
|
|
206
|
+
*/
|
|
207
|
+
export async function createChatSessionSafe(deps) {
|
|
208
|
+
try {
|
|
209
|
+
return await createChatSession(deps);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
await reportError(error, 'Error initializing chat session.', deps.extraHistory ?? [], 'startChat');
|
|
213
|
+
throw new Error(`Failed to initialize chat: ${getErrorMessage(error)}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=ChatSessionFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatSessionFactory.js","sourceRoot":"","sources":["../../../src/core/ChatSessionFactory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ConversationManager - Gemini-specific conversation/history recording layer.
|
|
8
|
+
*
|
|
9
|
+
* This class wraps HistoryService with chatSession-specific recording logic,
|
|
10
|
+
* handling Content→IContent conversion, thinking block attachment, usage
|
|
11
|
+
* metadata injection, and model output consolidation.
|
|
12
|
+
*/
|
|
13
|
+
import type { Content } from '@google/genai';
|
|
14
|
+
import type { AgentRuntimeContext } from '@vybestack/llxprt-code-core/runtime/AgentRuntimeContext.js';
|
|
15
|
+
import type { HistoryService } from '@vybestack/llxprt-code-core/services/history/HistoryService.js';
|
|
16
|
+
import type { IContent, UsageStats } from '@vybestack/llxprt-code-core/services/history/IContent.js';
|
|
17
|
+
import type { CompletedToolCall } from './coreToolScheduler.js';
|
|
18
|
+
/**
|
|
19
|
+
* ConversationManager handles conversation history management for ChatSession.
|
|
20
|
+
* It provides methods for recording turns, converting Content to IContent,
|
|
21
|
+
* and managing conversation state.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ConversationManager {
|
|
24
|
+
private readonly historyService;
|
|
25
|
+
private readonly runtimeContext;
|
|
26
|
+
private readonly model;
|
|
27
|
+
constructor(historyService: HistoryService, runtimeContext: AgentRuntimeContext, model: string);
|
|
28
|
+
/**
|
|
29
|
+
* Get the underlying HistoryService instance
|
|
30
|
+
*/
|
|
31
|
+
getHistoryService(): HistoryService;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a position-based matcher for Gemini tool responses.
|
|
34
|
+
* Returns a function that matches tool responses to their calls, or undefined
|
|
35
|
+
* if there are no unmatched tool calls.
|
|
36
|
+
*/
|
|
37
|
+
makePositionMatcher(): (() => {
|
|
38
|
+
historyId: string;
|
|
39
|
+
toolName?: string;
|
|
40
|
+
}) | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Converts user input (Content or Content[]) to IContent[] for history.
|
|
43
|
+
* This consolidates the Content→IContent conversion pattern used in:
|
|
44
|
+
* - Constructor initial history import
|
|
45
|
+
* - sendMessage pre-send
|
|
46
|
+
* - sendMessageStream pre-send
|
|
47
|
+
*
|
|
48
|
+
* These all share the same semantics: model + matcher/idGen.
|
|
49
|
+
*/
|
|
50
|
+
convertUserInputToIContents(userContent: Content | Content[]): IContent[];
|
|
51
|
+
/**
|
|
52
|
+
* Imports initial history during ChatSession construction.
|
|
53
|
+
* Validates the history and converts each Content to IContent before adding.
|
|
54
|
+
* Called from ChatSession constructor after ConversationManager is created.
|
|
55
|
+
*/
|
|
56
|
+
importInitialHistory(initialHistory: Content[], model: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Records a completed conversation turn to history.
|
|
59
|
+
* This is the main orchestrator that handles both user and model turns.
|
|
60
|
+
*
|
|
61
|
+
* @param userInput - User's input (Content or Content[] for paired tool call/response)
|
|
62
|
+
* @param modelOutput - Model's output Content array
|
|
63
|
+
* @param automaticFunctionCallingHistory - Optional AFC history from Gemini SDK
|
|
64
|
+
* @param usageMetadata - Optional usage statistics
|
|
65
|
+
*/
|
|
66
|
+
recordHistory(userInput: Content | Content[], modelOutput: Content[], automaticFunctionCallingHistory?: Content[], usageMetadata?: UsageStats | null): void;
|
|
67
|
+
/**
|
|
68
|
+
* Handles user Content → IContent conversion, including AFC and paired
|
|
69
|
+
* tool call/response scenarios.
|
|
70
|
+
*
|
|
71
|
+
* Mutates newHistoryEntries by appending user turn entries.
|
|
72
|
+
*/
|
|
73
|
+
private _recordUserTurn;
|
|
74
|
+
/**
|
|
75
|
+
* Handles model output filtering, thinking block attachment, consolidation,
|
|
76
|
+
* and usage metadata injection.
|
|
77
|
+
*
|
|
78
|
+
* Mutates newHistoryEntries by appending model turn entries.
|
|
79
|
+
*/
|
|
80
|
+
private _recordModelTurn;
|
|
81
|
+
/**
|
|
82
|
+
* Consolidates the parts of a model's turn response.
|
|
83
|
+
* Merges adjacent text content to avoid fragmentation.
|
|
84
|
+
*/
|
|
85
|
+
private _consolidateModelOutput;
|
|
86
|
+
/**
|
|
87
|
+
* Adds consolidated model output to history with thinking blocks and usage metadata.
|
|
88
|
+
* Attaches thinking blocks to the first model entry and ensures proper metadata.
|
|
89
|
+
*/
|
|
90
|
+
private _addModelOutputToHistory;
|
|
91
|
+
/**
|
|
92
|
+
* Gets the conversation history in Gemini Content format.
|
|
93
|
+
* @param curated - If true, returns curated history; otherwise returns all history
|
|
94
|
+
*/
|
|
95
|
+
getHistory(curated?: boolean): Content[];
|
|
96
|
+
/**
|
|
97
|
+
* Clears the chat history.
|
|
98
|
+
*/
|
|
99
|
+
clearHistory(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Adds a new entry to the chat history.
|
|
102
|
+
*/
|
|
103
|
+
addHistory(content: Content): void;
|
|
104
|
+
/**
|
|
105
|
+
* Sets the full chat history, replacing any existing history.
|
|
106
|
+
*/
|
|
107
|
+
setHistory(history: Content[]): void;
|
|
108
|
+
/**
|
|
109
|
+
* Compatibility stub for tool call recording.
|
|
110
|
+
* This is a no-op maintained for backward compatibility.
|
|
111
|
+
*/
|
|
112
|
+
recordCompletedToolCalls(_model: string, _toolCalls: CompletedToolCall[]): void;
|
|
113
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { ContentConverters } from '@vybestack/llxprt-code-core/services/history/ContentConverters.js';
|
|
7
|
+
import { isFunctionResponse } from '@vybestack/llxprt-code-core/utils/messageInspectors.js';
|
|
8
|
+
import { isThoughtPart } from '@vybestack/llxprt-code-core/core/chatSessionTypes.js';
|
|
9
|
+
import { extractCuratedHistory, hasTextContent, validateHistory, } from './MessageConverter.js';
|
|
10
|
+
function appendTextContentParts(lastContent, content) {
|
|
11
|
+
const lastParts = lastContent.parts ?? [];
|
|
12
|
+
const contentParts = content.parts ?? [];
|
|
13
|
+
lastParts[0].text += contentParts[0].text ?? '';
|
|
14
|
+
if (contentParts.length > 1) {
|
|
15
|
+
lastParts.push(...contentParts.slice(1));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* ConversationManager handles conversation history management for ChatSession.
|
|
20
|
+
* It provides methods for recording turns, converting Content to IContent,
|
|
21
|
+
* and managing conversation state.
|
|
22
|
+
*/
|
|
23
|
+
export class ConversationManager {
|
|
24
|
+
historyService;
|
|
25
|
+
runtimeContext;
|
|
26
|
+
model;
|
|
27
|
+
constructor(historyService, runtimeContext, model) {
|
|
28
|
+
this.historyService = historyService;
|
|
29
|
+
this.runtimeContext = runtimeContext;
|
|
30
|
+
this.model = model;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the underlying HistoryService instance
|
|
34
|
+
*/
|
|
35
|
+
getHistoryService() {
|
|
36
|
+
return this.historyService;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Creates a position-based matcher for Gemini tool responses.
|
|
40
|
+
* Returns a function that matches tool responses to their calls, or undefined
|
|
41
|
+
* if there are no unmatched tool calls.
|
|
42
|
+
*/
|
|
43
|
+
makePositionMatcher() {
|
|
44
|
+
const queue = this.historyService
|
|
45
|
+
.findUnmatchedToolCalls()
|
|
46
|
+
.map((b) => ({ historyId: b.id, toolName: b.name }));
|
|
47
|
+
// Return undefined if there are no unmatched tool calls
|
|
48
|
+
if (queue.length === 0) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
// Return a function that always returns a valid value (never undefined)
|
|
52
|
+
return () => {
|
|
53
|
+
const result = queue.shift();
|
|
54
|
+
// If queue is empty, return a fallback value
|
|
55
|
+
return result ?? { historyId: '', toolName: undefined };
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Converts user input (Content or Content[]) to IContent[] for history.
|
|
60
|
+
* This consolidates the Content→IContent conversion pattern used in:
|
|
61
|
+
* - Constructor initial history import
|
|
62
|
+
* - sendMessage pre-send
|
|
63
|
+
* - sendMessageStream pre-send
|
|
64
|
+
*
|
|
65
|
+
* These all share the same semantics: model + matcher/idGen.
|
|
66
|
+
*/
|
|
67
|
+
convertUserInputToIContents(userContent) {
|
|
68
|
+
const contents = Array.isArray(userContent) ? userContent : [userContent];
|
|
69
|
+
const matcher = this.makePositionMatcher();
|
|
70
|
+
return contents.map((content) => {
|
|
71
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
72
|
+
const idGen = this.historyService.getIdGeneratorCallback(turnKey);
|
|
73
|
+
return ContentConverters.toIContent(content, idGen, matcher, turnKey);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Imports initial history during ChatSession construction.
|
|
78
|
+
* Validates the history and converts each Content to IContent before adding.
|
|
79
|
+
* Called from ChatSession constructor after ConversationManager is created.
|
|
80
|
+
*/
|
|
81
|
+
importInitialHistory(initialHistory, model) {
|
|
82
|
+
if (initialHistory.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Validate before importing
|
|
86
|
+
validateHistory(initialHistory);
|
|
87
|
+
// Convert and add each entry
|
|
88
|
+
const matcher = this.makePositionMatcher();
|
|
89
|
+
for (const content of initialHistory) {
|
|
90
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
91
|
+
const idGen = this.historyService.getIdGeneratorCallback(turnKey);
|
|
92
|
+
this.historyService.add(ContentConverters.toIContent(content, idGen, matcher, turnKey), model);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Records a completed conversation turn to history.
|
|
97
|
+
* This is the main orchestrator that handles both user and model turns.
|
|
98
|
+
*
|
|
99
|
+
* @param userInput - User's input (Content or Content[] for paired tool call/response)
|
|
100
|
+
* @param modelOutput - Model's output Content array
|
|
101
|
+
* @param automaticFunctionCallingHistory - Optional AFC history from Gemini SDK
|
|
102
|
+
* @param usageMetadata - Optional usage statistics
|
|
103
|
+
*/
|
|
104
|
+
recordHistory(userInput, modelOutput, automaticFunctionCallingHistory, usageMetadata) {
|
|
105
|
+
const newHistoryEntries = [];
|
|
106
|
+
// Capture user input characteristics for model turn logic
|
|
107
|
+
const userInputWasArray = Array.isArray(userInput);
|
|
108
|
+
const userInputWasFunctionResponse = !userInputWasArray && isFunctionResponse(userInput);
|
|
109
|
+
const hasAfc = !!(automaticFunctionCallingHistory &&
|
|
110
|
+
automaticFunctionCallingHistory.length > 0);
|
|
111
|
+
// Record user turn
|
|
112
|
+
this._recordUserTurn(userInput, automaticFunctionCallingHistory, newHistoryEntries);
|
|
113
|
+
// Record model turn
|
|
114
|
+
this._recordModelTurn(modelOutput, usageMetadata, newHistoryEntries, userInputWasArray, userInputWasFunctionResponse, hasAfc);
|
|
115
|
+
// Add all entries to history service
|
|
116
|
+
for (const entry of newHistoryEntries) {
|
|
117
|
+
this.historyService.add(entry, this.model);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Handles user Content → IContent conversion, including AFC and paired
|
|
122
|
+
* tool call/response scenarios.
|
|
123
|
+
*
|
|
124
|
+
* Mutates newHistoryEntries by appending user turn entries.
|
|
125
|
+
*/
|
|
126
|
+
_recordUserTurn(userInput, automaticFunctionCallingHistory, newHistoryEntries) {
|
|
127
|
+
if (automaticFunctionCallingHistory &&
|
|
128
|
+
automaticFunctionCallingHistory.length > 0) {
|
|
129
|
+
// AFC branch: extract curated history
|
|
130
|
+
const curatedAfc = extractCuratedHistory(automaticFunctionCallingHistory);
|
|
131
|
+
for (const content of curatedAfc) {
|
|
132
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
133
|
+
newHistoryEntries.push(ContentConverters.toIContent(content, undefined, undefined, turnKey));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const matcher = this.makePositionMatcher();
|
|
138
|
+
// Handle both single Content and Content[] (for paired tool call/response)
|
|
139
|
+
if (Array.isArray(userInput)) {
|
|
140
|
+
// This is a paired tool call/response from the executor
|
|
141
|
+
// Add each part to history
|
|
142
|
+
for (const content of userInput) {
|
|
143
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
144
|
+
const idGen = this.historyService.getIdGeneratorCallback(turnKey);
|
|
145
|
+
const userIContent = ContentConverters.toIContent(content, idGen, matcher, turnKey);
|
|
146
|
+
newHistoryEntries.push(userIContent);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Normal user message
|
|
151
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
152
|
+
const idGen = this.historyService.getIdGeneratorCallback(turnKey);
|
|
153
|
+
const userIContent = ContentConverters.toIContent(userInput, idGen, matcher, turnKey);
|
|
154
|
+
newHistoryEntries.push(userIContent);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Handles model output filtering, thinking block attachment, consolidation,
|
|
160
|
+
* and usage metadata injection.
|
|
161
|
+
*
|
|
162
|
+
* Mutates newHistoryEntries by appending model turn entries.
|
|
163
|
+
*/
|
|
164
|
+
_recordModelTurn(modelOutput, usageMetadata, newHistoryEntries, userInputWasArray, userInputWasFunctionResponse, hasAfc) {
|
|
165
|
+
// Filter out thoughts based on reasoning configuration
|
|
166
|
+
const includeThoughtsInHistory = this.runtimeContext.ephemerals.reasoning.includeInContext();
|
|
167
|
+
const nonThoughtModelOutput = modelOutput
|
|
168
|
+
.map((content) => ({
|
|
169
|
+
...content,
|
|
170
|
+
parts: (content.parts ?? []).filter((part) => !isThoughtPart(part)),
|
|
171
|
+
}))
|
|
172
|
+
.filter((content) => content.parts.length > 0);
|
|
173
|
+
// Extract thinking blocks if needed
|
|
174
|
+
const thoughtBlocks = includeThoughtsInHistory
|
|
175
|
+
? modelOutput
|
|
176
|
+
.flatMap((content) => content.parts ?? [])
|
|
177
|
+
.filter(isThoughtPart)
|
|
178
|
+
.map((part) => ({
|
|
179
|
+
type: 'thinking',
|
|
180
|
+
thought: (part.text ?? '').trim(),
|
|
181
|
+
sourceField: part.llxprtSourceField ?? 'thought',
|
|
182
|
+
signature: part.thoughtSignature,
|
|
183
|
+
}))
|
|
184
|
+
.filter((block) => block.thought.length > 0)
|
|
185
|
+
: [];
|
|
186
|
+
// Determine output contents
|
|
187
|
+
let outputContents = [];
|
|
188
|
+
if (nonThoughtModelOutput.length > 0) {
|
|
189
|
+
outputContents = nonThoughtModelOutput;
|
|
190
|
+
}
|
|
191
|
+
else if (modelOutput.length === 0 &&
|
|
192
|
+
!userInputWasArray &&
|
|
193
|
+
!userInputWasFunctionResponse &&
|
|
194
|
+
!hasAfc) {
|
|
195
|
+
// Add an empty model response if the model truly returned nothing
|
|
196
|
+
outputContents.push({ role: 'model', parts: [] });
|
|
197
|
+
}
|
|
198
|
+
if (outputContents.length === 0 && thoughtBlocks.length > 0) {
|
|
199
|
+
outputContents = [{ role: 'model', parts: [] }];
|
|
200
|
+
}
|
|
201
|
+
// Consolidate model response parts
|
|
202
|
+
const consolidatedOutputContents = this._consolidateModelOutput(outputContents);
|
|
203
|
+
// Add consolidated output to new history with thinking blocks
|
|
204
|
+
this._addModelOutputToHistory(consolidatedOutputContents, thoughtBlocks, usageMetadata, newHistoryEntries);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Consolidates the parts of a model's turn response.
|
|
208
|
+
* Merges adjacent text content to avoid fragmentation.
|
|
209
|
+
*/
|
|
210
|
+
_consolidateModelOutput(outputContents) {
|
|
211
|
+
const consolidatedOutputContents = [];
|
|
212
|
+
if (outputContents.length > 0) {
|
|
213
|
+
for (const content of outputContents) {
|
|
214
|
+
const lastContent = consolidatedOutputContents[consolidatedOutputContents.length - 1];
|
|
215
|
+
if (hasTextContent(lastContent) && hasTextContent(content)) {
|
|
216
|
+
appendTextContentParts(lastContent, content);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
consolidatedOutputContents.push(content);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return consolidatedOutputContents;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Adds consolidated model output to history with thinking blocks and usage metadata.
|
|
227
|
+
* Attaches thinking blocks to the first model entry and ensures proper metadata.
|
|
228
|
+
*/
|
|
229
|
+
_addModelOutputToHistory(consolidatedOutputContents, thoughtBlocks, usageMetadata, newHistoryEntries) {
|
|
230
|
+
let didAttachThoughtBlocks = false;
|
|
231
|
+
for (const content of consolidatedOutputContents) {
|
|
232
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
233
|
+
const iContent = ContentConverters.toIContent(content, undefined, undefined, turnKey);
|
|
234
|
+
// Attach thinking blocks to first model entry
|
|
235
|
+
if (thoughtBlocks.length > 0 && !didAttachThoughtBlocks) {
|
|
236
|
+
iContent.blocks = [...thoughtBlocks, ...iContent.blocks];
|
|
237
|
+
didAttachThoughtBlocks = true;
|
|
238
|
+
}
|
|
239
|
+
// Add usage metadata if available
|
|
240
|
+
if (usageMetadata !== undefined && usageMetadata !== null) {
|
|
241
|
+
iContent.metadata = {
|
|
242
|
+
...iContent.metadata,
|
|
243
|
+
usage: usageMetadata,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
newHistoryEntries.push(iContent);
|
|
247
|
+
}
|
|
248
|
+
// If we have thinking blocks but nowhere to attach them, create standalone entry
|
|
249
|
+
if (thoughtBlocks.length > 0 && !didAttachThoughtBlocks) {
|
|
250
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
251
|
+
const iContent = {
|
|
252
|
+
speaker: 'ai',
|
|
253
|
+
blocks: thoughtBlocks,
|
|
254
|
+
metadata: { turnId: turnKey },
|
|
255
|
+
};
|
|
256
|
+
if (usageMetadata !== undefined && usageMetadata !== null) {
|
|
257
|
+
iContent.metadata = {
|
|
258
|
+
...iContent.metadata,
|
|
259
|
+
usage: usageMetadata,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
newHistoryEntries.push(iContent);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Gets the conversation history in Gemini Content format.
|
|
267
|
+
* @param curated - If true, returns curated history; otherwise returns all history
|
|
268
|
+
*/
|
|
269
|
+
getHistory(curated = false) {
|
|
270
|
+
// Get history from HistoryService in IContent format
|
|
271
|
+
const iContents = curated
|
|
272
|
+
? this.historyService.getCurated()
|
|
273
|
+
: this.historyService.getAll();
|
|
274
|
+
// Convert to Gemini Content format
|
|
275
|
+
const contents = ContentConverters.toGeminiContents(iContents);
|
|
276
|
+
// Deep copy the history to avoid mutating the history outside of the
|
|
277
|
+
// chat session.
|
|
278
|
+
return structuredClone(contents);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Clears the chat history.
|
|
282
|
+
*/
|
|
283
|
+
clearHistory() {
|
|
284
|
+
this.historyService.clear();
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Adds a new entry to the chat history.
|
|
288
|
+
*/
|
|
289
|
+
addHistory(content) {
|
|
290
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
291
|
+
this.historyService.add(ContentConverters.toIContent(content, undefined, undefined, turnKey), this.model);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Sets the full chat history, replacing any existing history.
|
|
295
|
+
*/
|
|
296
|
+
setHistory(history) {
|
|
297
|
+
this.historyService.clear();
|
|
298
|
+
for (const content of history) {
|
|
299
|
+
const turnKey = this.historyService.generateTurnKey();
|
|
300
|
+
this.historyService.add(ContentConverters.toIContent(content, undefined, undefined, turnKey), this.model);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Compatibility stub for tool call recording.
|
|
305
|
+
* This is a no-op maintained for backward compatibility.
|
|
306
|
+
*/
|
|
307
|
+
recordCompletedToolCalls(_model, _toolCalls) {
|
|
308
|
+
// No-op stub for compatibility
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=ConversationManager.js.map
|