@dexto/core 1.5.3 → 1.5.4

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 (92) hide show
  1. package/dist/agent/DextoAgent.cjs +284 -1
  2. package/dist/agent/DextoAgent.d.ts +114 -0
  3. package/dist/agent/DextoAgent.d.ts.map +1 -1
  4. package/dist/agent/DextoAgent.js +275 -1
  5. package/dist/agent/schemas.d.ts +51 -21
  6. package/dist/agent/schemas.d.ts.map +1 -1
  7. package/dist/context/compaction/overflow.cjs +6 -10
  8. package/dist/context/compaction/overflow.d.ts +14 -11
  9. package/dist/context/compaction/overflow.d.ts.map +1 -1
  10. package/dist/context/compaction/overflow.js +6 -10
  11. package/dist/context/compaction/providers/reactive-overflow-provider.cjs +15 -0
  12. package/dist/context/compaction/providers/reactive-overflow-provider.d.ts +15 -0
  13. package/dist/context/compaction/providers/reactive-overflow-provider.d.ts.map +1 -1
  14. package/dist/context/compaction/providers/reactive-overflow-provider.js +15 -0
  15. package/dist/context/compaction/schemas.cjs +22 -2
  16. package/dist/context/compaction/schemas.d.ts +45 -0
  17. package/dist/context/compaction/schemas.d.ts.map +1 -1
  18. package/dist/context/compaction/schemas.js +22 -2
  19. package/dist/context/compaction/strategies/reactive-overflow.cjs +166 -26
  20. package/dist/context/compaction/strategies/reactive-overflow.d.ts +21 -0
  21. package/dist/context/compaction/strategies/reactive-overflow.d.ts.map +1 -1
  22. package/dist/context/compaction/strategies/reactive-overflow.js +166 -26
  23. package/dist/context/manager.cjs +278 -31
  24. package/dist/context/manager.d.ts +192 -5
  25. package/dist/context/manager.d.ts.map +1 -1
  26. package/dist/context/manager.js +285 -32
  27. package/dist/context/types.d.ts +6 -0
  28. package/dist/context/types.d.ts.map +1 -1
  29. package/dist/context/utils.cjs +77 -11
  30. package/dist/context/utils.d.ts +86 -8
  31. package/dist/context/utils.d.ts.map +1 -1
  32. package/dist/context/utils.js +71 -11
  33. package/dist/events/index.cjs +4 -0
  34. package/dist/events/index.d.ts +41 -7
  35. package/dist/events/index.d.ts.map +1 -1
  36. package/dist/events/index.js +4 -0
  37. package/dist/llm/executor/stream-processor.cjs +19 -1
  38. package/dist/llm/executor/stream-processor.d.ts +3 -0
  39. package/dist/llm/executor/stream-processor.d.ts.map +1 -1
  40. package/dist/llm/executor/stream-processor.js +19 -1
  41. package/dist/llm/executor/turn-executor.cjs +219 -30
  42. package/dist/llm/executor/turn-executor.d.ts +62 -10
  43. package/dist/llm/executor/turn-executor.d.ts.map +1 -1
  44. package/dist/llm/executor/turn-executor.js +219 -30
  45. package/dist/llm/executor/types.d.ts +28 -0
  46. package/dist/llm/executor/types.d.ts.map +1 -1
  47. package/dist/llm/formatters/vercel.cjs +36 -28
  48. package/dist/llm/formatters/vercel.d.ts.map +1 -1
  49. package/dist/llm/formatters/vercel.js +36 -28
  50. package/dist/llm/services/factory.cjs +3 -2
  51. package/dist/llm/services/factory.d.ts +3 -1
  52. package/dist/llm/services/factory.d.ts.map +1 -1
  53. package/dist/llm/services/factory.js +3 -2
  54. package/dist/llm/services/vercel.cjs +34 -6
  55. package/dist/llm/services/vercel.d.ts +23 -3
  56. package/dist/llm/services/vercel.d.ts.map +1 -1
  57. package/dist/llm/services/vercel.js +34 -6
  58. package/dist/session/chat-session.cjs +20 -11
  59. package/dist/session/chat-session.d.ts +9 -4
  60. package/dist/session/chat-session.d.ts.map +1 -1
  61. package/dist/session/chat-session.js +20 -11
  62. package/dist/session/compaction-service.cjs +139 -0
  63. package/dist/session/compaction-service.d.ts +81 -0
  64. package/dist/session/compaction-service.d.ts.map +1 -0
  65. package/dist/session/compaction-service.js +106 -0
  66. package/dist/session/session-manager.cjs +146 -0
  67. package/dist/session/session-manager.d.ts +50 -0
  68. package/dist/session/session-manager.d.ts.map +1 -1
  69. package/dist/session/session-manager.js +146 -0
  70. package/dist/session/title-generator.cjs +2 -2
  71. package/dist/session/title-generator.js +2 -2
  72. package/dist/systemPrompt/in-built-prompts.cjs +36 -0
  73. package/dist/systemPrompt/in-built-prompts.d.ts +18 -1
  74. package/dist/systemPrompt/in-built-prompts.d.ts.map +1 -1
  75. package/dist/systemPrompt/in-built-prompts.js +25 -0
  76. package/dist/systemPrompt/manager.cjs +22 -0
  77. package/dist/systemPrompt/manager.d.ts +10 -0
  78. package/dist/systemPrompt/manager.d.ts.map +1 -1
  79. package/dist/systemPrompt/manager.js +22 -0
  80. package/dist/systemPrompt/registry.cjs +2 -1
  81. package/dist/systemPrompt/registry.d.ts +1 -1
  82. package/dist/systemPrompt/registry.d.ts.map +1 -1
  83. package/dist/systemPrompt/registry.js +2 -1
  84. package/dist/systemPrompt/schemas.cjs +7 -0
  85. package/dist/systemPrompt/schemas.d.ts +13 -13
  86. package/dist/systemPrompt/schemas.d.ts.map +1 -1
  87. package/dist/systemPrompt/schemas.js +7 -0
  88. package/dist/utils/index.cjs +3 -1
  89. package/dist/utils/index.d.ts +1 -0
  90. package/dist/utils/index.d.ts.map +1 -1
  91. package/dist/utils/index.js +1 -0
  92. package/package.json +1 -1
@@ -38,6 +38,23 @@ export declare class ContextManager<TMessage = unknown> {
38
38
  * Maximum number of tokens allowed in the conversation (if specified)
39
39
  */
40
40
  private maxInputTokens;
41
+ /**
42
+ * Last known actual input token count from the LLM API response.
43
+ * Updated after each LLM call. Used by /context for accurate reporting.
44
+ */
45
+ private lastActualInputTokens;
46
+ /**
47
+ * Last known actual output token count from the LLM API response.
48
+ * Updated after each LLM call. Used in the context estimation formula:
49
+ * estimatedNextInput = lastInputTokens + lastOutputTokens + newMessagesEstimate
50
+ */
51
+ private lastActualOutputTokens;
52
+ /**
53
+ * Message count at the time of the last LLM call.
54
+ * Used to identify which messages are "new" since the last call.
55
+ * Messages after this index are estimated with length/4 heuristic.
56
+ */
57
+ private lastCallMessageCount;
41
58
  private historyProvider;
42
59
  private readonly sessionId;
43
60
  /**
@@ -72,6 +89,71 @@ export declare class ContextManager<TMessage = unknown> {
72
89
  * Returns the configured maximum number of input tokens for the conversation.
73
90
  */
74
91
  getMaxInputTokens(): number;
92
+ /**
93
+ * Returns the last known actual input token count from the LLM API.
94
+ * Returns null if no LLM call has been made yet.
95
+ */
96
+ getLastActualInputTokens(): number | null;
97
+ /**
98
+ * Updates the last known actual input token count.
99
+ * Called after each LLM response with the actual usage from the API.
100
+ */
101
+ setLastActualInputTokens(tokens: number): void;
102
+ /**
103
+ * Returns the last known actual output token count from the LLM API.
104
+ * Returns null if no LLM call has been made yet.
105
+ */
106
+ getLastActualOutputTokens(): number | null;
107
+ /**
108
+ * Updates the last known actual output token count.
109
+ * Called after each LLM response with the actual usage from the API.
110
+ */
111
+ setLastActualOutputTokens(tokens: number): void;
112
+ /**
113
+ * Returns the message count at the time of the last LLM call.
114
+ * Returns null if no LLM call has been made yet.
115
+ */
116
+ getLastCallMessageCount(): number | null;
117
+ /**
118
+ * Records the current message count after an LLM call completes.
119
+ * This marks the boundary for "new messages" calculation.
120
+ */
121
+ recordLastCallMessageCount(): Promise<void>;
122
+ /**
123
+ * Resets the actual token tracking state.
124
+ * Called after compaction since the context has fundamentally changed.
125
+ */
126
+ resetActualTokenTracking(): void;
127
+ /**
128
+ * Placeholder text used when tool outputs are pruned.
129
+ * Shared constant to ensure consistency between preparation and estimation.
130
+ */
131
+ private static readonly PRUNED_TOOL_PLACEHOLDER;
132
+ /**
133
+ * Prepares conversation history for LLM consumption.
134
+ * This is the single source of truth for history transformation logic.
135
+ *
136
+ * Transformations applied:
137
+ * 1. filterCompacted - Remove pre-summary messages (messages before the most recent summary)
138
+ * 2. Transform pruned tool messages - Replace compactedAt messages with placeholder text
139
+ *
140
+ * Used by both:
141
+ * - getFormattedMessagesForLLM() - For actual LLM calls
142
+ * - getContextTokenEstimate() - For /context command estimation
143
+ *
144
+ * @returns Prepared history and statistics about the transformations
145
+ */
146
+ prepareHistory(): Promise<{
147
+ preparedHistory: InternalMessage[];
148
+ stats: {
149
+ /** Total messages in raw history */
150
+ originalCount: number;
151
+ /** Messages after filterCompacted (removed pre-summary) */
152
+ filteredCount: number;
153
+ /** Messages with compactedAt that were transformed to placeholders */
154
+ prunedToolCount: number;
155
+ };
156
+ }>;
75
157
  /**
76
158
  * Assembles and returns the current system prompt by invoking the SystemPromptManager.
77
159
  */
@@ -190,18 +272,123 @@ export declare class ContextManager<TMessage = unknown> {
190
272
  /**
191
273
  * Gets the conversation ready for LLM consumption with proper flow:
192
274
  * 1. Get system prompt
193
- * 2. Get history and filter (exclude pre-summary messages)
194
- * 3. Format messages
195
- * This method implements the correct ordering to avoid circular dependencies.
275
+ * 2. Prepare history (filter + transform pruned messages)
276
+ * 3. Format messages for LLM API
196
277
  *
197
278
  * @param contributorContext The DynamicContributorContext for system prompt contributors and formatting
198
279
  * @param llmContext The llmContext for the formatter to decide which messages to include based on the model's capabilities
199
- * @returns Object containing formatted messages and system prompt
280
+ * @returns Object containing formatted messages, system prompt, and prepared history
200
281
  */
201
- getFormattedMessagesWithCompression(contributorContext: DynamicContributorContext, llmContext: LLMContext): Promise<{
282
+ getFormattedMessagesForLLM(contributorContext: DynamicContributorContext, llmContext: LLMContext): Promise<{
202
283
  formattedMessages: TMessage[];
203
284
  systemPrompt: string;
285
+ preparedHistory: InternalMessage[];
286
+ }>;
287
+ /**
288
+ * Estimates context token usage for the /context command and compaction decisions.
289
+ * Uses the same prepareHistory() logic as getFormattedMessagesForLLM() to ensure consistency.
290
+ *
291
+ * When actuals are available from previous LLM calls:
292
+ * estimatedNextInput = lastInputTokens + lastOutputTokens + newMessagesEstimate
293
+ *
294
+ * This formula is more accurate because:
295
+ * - lastInputTokens: exactly what the API processed (ground truth)
296
+ * - lastOutputTokens: exactly what the LLM returned (ground truth)
297
+ * - newMessagesEstimate: only estimate the delta (tool results, new user messages)
298
+ *
299
+ * When no LLM call has been made yet (or after compaction), falls back to pure estimation.
300
+ *
301
+ * @param contributorContext Context for building the system prompt
302
+ * @param tools Tool definitions to include in the estimate
303
+ * @returns Token estimates with breakdown and comparison to actual (if available)
304
+ */
305
+ getContextTokenEstimate(contributorContext: DynamicContributorContext, tools: Record<string, {
306
+ name?: string;
307
+ description?: string;
308
+ parameters?: unknown;
309
+ }>): Promise<{
310
+ /** Total estimated tokens */
311
+ estimated: number;
312
+ /** Last actual token count from LLM API (null if no calls made yet) */
313
+ actual: number | null;
314
+ /** Breakdown by category */
315
+ breakdown: {
316
+ systemPrompt: number;
317
+ tools: {
318
+ total: number;
319
+ perTool: Array<{
320
+ name: string;
321
+ tokens: number;
322
+ }>;
323
+ };
324
+ messages: number;
325
+ };
326
+ /** Preparation stats */
327
+ stats: {
328
+ originalMessageCount: number;
329
+ filteredMessageCount: number;
330
+ prunedToolCount: number;
331
+ };
332
+ /** Calculation basis for debugging/display */
333
+ calculationBasis?: {
334
+ /** Whether we used the actual-based formula or pure estimation */
335
+ method: 'actuals' | 'estimate';
336
+ /** Last actual input tokens from API (if method is 'actuals') */
337
+ lastInputTokens?: number;
338
+ /** Last actual output tokens from API (if method is 'actuals') */
339
+ lastOutputTokens?: number;
340
+ /** Estimated tokens for new messages since last call (if method is 'actuals') */
341
+ newMessagesEstimate?: number;
342
+ };
204
343
  }>;
344
+ /**
345
+ * Estimates the next input token count using actual token data from the previous LLM call.
346
+ * This is a lightweight version for compaction pre-checks that only returns the total.
347
+ *
348
+ * ## Formula (when actuals are available):
349
+ * estimatedNextInput = lastInputTokens + lastOutputTokens + newMessagesEstimate
350
+ *
351
+ * ## Why this formula works:
352
+ *
353
+ * Consider two consecutive LLM calls:
354
+ *
355
+ * ```
356
+ * Call N:
357
+ * Input sent: system + tools + [user1] = lastInput tokens
358
+ * Output received: assistant response = lastOutput tokens
359
+ *
360
+ * Call N+1:
361
+ * Input will be: system + tools + [user1, assistant1, user2, ...]
362
+ * ≈ lastInput + assistant1_as_input + new_messages
363
+ * ≈ lastInput + lastOutput + newMessagesEstimate
364
+ * ```
365
+ *
366
+ * The assistant's response (lastOutput) becomes part of the next input as conversation
367
+ * history. Text tokenizes similarly whether sent as input or received as output.
368
+ *
369
+ * ## No double-counting:
370
+ *
371
+ * The assistant message is added to history DURING streaming (before this method runs),
372
+ * and recordLastCallMessageCount() captures the count INCLUDING that message.
373
+ * Therefore, newMessages = history.slice(lastMsgCount) EXCLUDES the assistant message,
374
+ * so lastOutput and newMessages don't overlap.
375
+ *
376
+ * ## Pruning caveat:
377
+ *
378
+ * If tool output pruning occurs between calls, lastInput may be stale (higher than
379
+ * actual). This causes OVERESTIMATION, which is SAFE - we'd trigger compaction
380
+ * earlier rather than risk context overflow.
381
+ *
382
+ * @param systemPrompt The system prompt string
383
+ * @param preparedHistory Message history AFTER filterCompacted and pruning
384
+ * @param tools Tool definitions
385
+ * @returns Estimated total input tokens for the next LLM call
386
+ */
387
+ getEstimatedNextInputTokens(systemPrompt: string, preparedHistory: readonly InternalMessage[], tools: Record<string, {
388
+ name?: string;
389
+ description?: string;
390
+ parameters?: unknown;
391
+ }>): Promise<number>;
205
392
  /**
206
393
  * Gets the system prompt formatted for the target LLM provider
207
394
  * Some providers handle system prompts differently
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/context/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/context/manager.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,13 @@ import { randomUUID } from "crypto";
3
3
  import { isSystemMessage, isUserMessage, isAssistantMessage, isToolMessage } from "./types.js";
4
4
  import { DextoLogComponent } from "../logger/v2/types.js";
5
5
  import { eventBus } from "../events/index.js";
6
- import { expandBlobReferences, isLikelyBase64String, filterCompacted } from "./utils.js";
6
+ import {
7
+ expandBlobReferences,
8
+ isLikelyBase64String,
9
+ filterCompacted,
10
+ estimateContextTokens,
11
+ estimateMessagesTokens
12
+ } from "./utils.js";
7
13
  import { ContextError } from "./errors.js";
8
14
  class ContextManager {
9
15
  /**
@@ -22,6 +28,23 @@ class ContextManager {
22
28
  * Maximum number of tokens allowed in the conversation (if specified)
23
29
  */
24
30
  maxInputTokens;
31
+ /**
32
+ * Last known actual input token count from the LLM API response.
33
+ * Updated after each LLM call. Used by /context for accurate reporting.
34
+ */
35
+ lastActualInputTokens = null;
36
+ /**
37
+ * Last known actual output token count from the LLM API response.
38
+ * Updated after each LLM call. Used in the context estimation formula:
39
+ * estimatedNextInput = lastInputTokens + lastOutputTokens + newMessagesEstimate
40
+ */
41
+ lastActualOutputTokens = null;
42
+ /**
43
+ * Message count at the time of the last LLM call.
44
+ * Used to identify which messages are "new" since the last call.
45
+ * Messages after this index are estimated with length/4 heuristic.
46
+ */
47
+ lastCallMessageCount = null;
25
48
  historyProvider;
26
49
  sessionId;
27
50
  /**
@@ -120,6 +143,119 @@ class ContextManager {
120
143
  getMaxInputTokens() {
121
144
  return this.maxInputTokens;
122
145
  }
146
+ /**
147
+ * Returns the last known actual input token count from the LLM API.
148
+ * Returns null if no LLM call has been made yet.
149
+ */
150
+ getLastActualInputTokens() {
151
+ return this.lastActualInputTokens;
152
+ }
153
+ /**
154
+ * Updates the last known actual input token count.
155
+ * Called after each LLM response with the actual usage from the API.
156
+ */
157
+ setLastActualInputTokens(tokens) {
158
+ this.lastActualInputTokens = tokens;
159
+ this.logger.debug(`Updated lastActualInputTokens: ${tokens}`);
160
+ }
161
+ /**
162
+ * Returns the last known actual output token count from the LLM API.
163
+ * Returns null if no LLM call has been made yet.
164
+ */
165
+ getLastActualOutputTokens() {
166
+ return this.lastActualOutputTokens;
167
+ }
168
+ /**
169
+ * Updates the last known actual output token count.
170
+ * Called after each LLM response with the actual usage from the API.
171
+ */
172
+ setLastActualOutputTokens(tokens) {
173
+ this.lastActualOutputTokens = tokens;
174
+ this.logger.debug(`Updated lastActualOutputTokens: ${tokens}`);
175
+ }
176
+ /**
177
+ * Returns the message count at the time of the last LLM call.
178
+ * Returns null if no LLM call has been made yet.
179
+ */
180
+ getLastCallMessageCount() {
181
+ return this.lastCallMessageCount;
182
+ }
183
+ /**
184
+ * Records the current message count after an LLM call completes.
185
+ * This marks the boundary for "new messages" calculation.
186
+ */
187
+ async recordLastCallMessageCount() {
188
+ const history = await this.historyProvider.getHistory();
189
+ this.lastCallMessageCount = history.length;
190
+ this.logger.debug(`Recorded lastCallMessageCount: ${this.lastCallMessageCount}`);
191
+ }
192
+ /**
193
+ * Resets the actual token tracking state.
194
+ * Called after compaction since the context has fundamentally changed.
195
+ */
196
+ resetActualTokenTracking() {
197
+ this.lastActualInputTokens = null;
198
+ this.lastActualOutputTokens = null;
199
+ this.lastCallMessageCount = null;
200
+ this.logger.debug("Reset actual token tracking state (after compaction)");
201
+ }
202
+ // ============= HISTORY PREPARATION =============
203
+ /**
204
+ * Placeholder text used when tool outputs are pruned.
205
+ * Shared constant to ensure consistency between preparation and estimation.
206
+ */
207
+ static PRUNED_TOOL_PLACEHOLDER = "[Old tool result content cleared]";
208
+ /**
209
+ * Prepares conversation history for LLM consumption.
210
+ * This is the single source of truth for history transformation logic.
211
+ *
212
+ * Transformations applied:
213
+ * 1. filterCompacted - Remove pre-summary messages (messages before the most recent summary)
214
+ * 2. Transform pruned tool messages - Replace compactedAt messages with placeholder text
215
+ *
216
+ * Used by both:
217
+ * - getFormattedMessagesForLLM() - For actual LLM calls
218
+ * - getContextTokenEstimate() - For /context command estimation
219
+ *
220
+ * @returns Prepared history and statistics about the transformations
221
+ */
222
+ async prepareHistory() {
223
+ const fullHistory = await this.historyProvider.getHistory();
224
+ const originalCount = fullHistory.length;
225
+ let history = filterCompacted(fullHistory);
226
+ const filteredCount = history.length;
227
+ if (filteredCount < originalCount) {
228
+ this.logger.debug(
229
+ `prepareHistory: filterCompacted reduced from ${originalCount} to ${filteredCount} messages`
230
+ );
231
+ }
232
+ let prunedToolCount = 0;
233
+ history = history.map((msg) => {
234
+ if (msg.role === "tool" && msg.compactedAt) {
235
+ prunedToolCount++;
236
+ return {
237
+ ...msg,
238
+ content: [
239
+ { type: "text", text: ContextManager.PRUNED_TOOL_PLACEHOLDER }
240
+ ]
241
+ };
242
+ }
243
+ return msg;
244
+ });
245
+ if (prunedToolCount > 0) {
246
+ this.logger.debug(
247
+ `prepareHistory: Transformed ${prunedToolCount} pruned tool messages to placeholders`
248
+ );
249
+ }
250
+ return {
251
+ preparedHistory: history,
252
+ stats: {
253
+ originalCount,
254
+ filteredCount,
255
+ prunedToolCount
256
+ }
257
+ };
258
+ }
123
259
  /**
124
260
  * Assembles and returns the current system prompt by invoking the SystemPromptManager.
125
261
  */
@@ -168,6 +304,7 @@ ${prompt}`);
168
304
  }
169
305
  };
170
306
  await this.addMessage(clearMarker);
307
+ this.resetActualTokenTracking();
171
308
  this.logger.debug(`Context cleared for session: ${this.sessionId}`);
172
309
  }
173
310
  /**
@@ -539,51 +676,166 @@ ${prompt}`);
539
676
  /**
540
677
  * Gets the conversation ready for LLM consumption with proper flow:
541
678
  * 1. Get system prompt
542
- * 2. Get history and filter (exclude pre-summary messages)
543
- * 3. Format messages
544
- * This method implements the correct ordering to avoid circular dependencies.
679
+ * 2. Prepare history (filter + transform pruned messages)
680
+ * 3. Format messages for LLM API
545
681
  *
546
682
  * @param contributorContext The DynamicContributorContext for system prompt contributors and formatting
547
683
  * @param llmContext The llmContext for the formatter to decide which messages to include based on the model's capabilities
548
- * @returns Object containing formatted messages and system prompt
684
+ * @returns Object containing formatted messages, system prompt, and prepared history
549
685
  */
550
- async getFormattedMessagesWithCompression(contributorContext, llmContext) {
686
+ async getFormattedMessagesForLLM(contributorContext, llmContext) {
551
687
  const systemPrompt = await this.getSystemPrompt(contributorContext);
552
- const fullHistory = await this.historyProvider.getHistory();
553
- let history = filterCompacted(fullHistory);
554
- if (history.length < fullHistory.length) {
555
- this.logger.debug(
556
- `filterCompacted: Reduced history from ${fullHistory.length} to ${history.length} messages (summary present)`
557
- );
558
- }
559
- const compactedCount = history.filter((m) => m.role === "tool" && m.compactedAt).length;
560
- if (compactedCount > 0) {
561
- history = history.map((msg) => {
562
- if (msg.role === "tool" && msg.compactedAt) {
563
- return {
564
- ...msg,
565
- content: [
566
- { type: "text", text: "[Old tool result content cleared]" }
567
- ]
568
- };
569
- }
570
- return msg;
571
- });
572
- this.logger.debug(
573
- `Transformed ${compactedCount} compacted tool messages to placeholders`
574
- );
575
- }
688
+ const { preparedHistory } = await this.prepareHistory();
576
689
  const formattedMessages = await this.getFormattedMessages(
577
690
  contributorContext,
578
691
  llmContext,
579
692
  systemPrompt,
580
- history
693
+ preparedHistory
581
694
  );
582
695
  return {
583
696
  formattedMessages,
584
- systemPrompt
697
+ systemPrompt,
698
+ preparedHistory
699
+ };
700
+ }
701
+ /**
702
+ * Estimates context token usage for the /context command and compaction decisions.
703
+ * Uses the same prepareHistory() logic as getFormattedMessagesForLLM() to ensure consistency.
704
+ *
705
+ * When actuals are available from previous LLM calls:
706
+ * estimatedNextInput = lastInputTokens + lastOutputTokens + newMessagesEstimate
707
+ *
708
+ * This formula is more accurate because:
709
+ * - lastInputTokens: exactly what the API processed (ground truth)
710
+ * - lastOutputTokens: exactly what the LLM returned (ground truth)
711
+ * - newMessagesEstimate: only estimate the delta (tool results, new user messages)
712
+ *
713
+ * When no LLM call has been made yet (or after compaction), falls back to pure estimation.
714
+ *
715
+ * @param contributorContext Context for building the system prompt
716
+ * @param tools Tool definitions to include in the estimate
717
+ * @returns Token estimates with breakdown and comparison to actual (if available)
718
+ */
719
+ async getContextTokenEstimate(contributorContext, tools) {
720
+ const systemPrompt = await this.getSystemPrompt(contributorContext);
721
+ const { preparedHistory, stats } = await this.prepareHistory();
722
+ const lastInput = this.lastActualInputTokens;
723
+ const lastOutput = this.lastActualOutputTokens;
724
+ const lastMsgCount = this.lastCallMessageCount;
725
+ const currentHistory = await this.historyProvider.getHistory();
726
+ const pureEstimate = estimateContextTokens(systemPrompt, preparedHistory, tools);
727
+ let total;
728
+ let calculationBasis;
729
+ if (lastInput !== null && lastOutput !== null && lastMsgCount !== null) {
730
+ const newMessages = currentHistory.slice(lastMsgCount);
731
+ const newMessagesEstimate = estimateMessagesTokens(newMessages);
732
+ total = lastInput + lastOutput + newMessagesEstimate;
733
+ calculationBasis = {
734
+ method: "actuals",
735
+ lastInputTokens: lastInput,
736
+ lastOutputTokens: lastOutput,
737
+ newMessagesEstimate
738
+ };
739
+ this.logger.info(
740
+ `Context estimate (actuals-based): lastInput=${lastInput}, lastOutput=${lastOutput}, newMsgs=${newMessagesEstimate} (${newMessages.length} messages), total=${total}`
741
+ );
742
+ } else {
743
+ total = pureEstimate.total;
744
+ calculationBasis = {
745
+ method: "estimate"
746
+ };
747
+ this.logger.debug(
748
+ `Context estimate (pure estimate): total=${total} (no actuals available yet)`
749
+ );
750
+ }
751
+ const systemPromptTokens = pureEstimate.breakdown.systemPrompt;
752
+ const toolsTokens = pureEstimate.breakdown.tools;
753
+ const messagesDisplay = Math.max(0, total - systemPromptTokens - toolsTokens.total);
754
+ if (lastInput !== null) {
755
+ const pureTotal = pureEstimate.total;
756
+ const diff = pureTotal - lastInput;
757
+ const diffPercent = lastInput > 0 ? (diff / lastInput * 100).toFixed(1) : "0.0";
758
+ this.logger.info(
759
+ `Context token calibration: pureEstimate=${pureTotal}, lastActual=${lastInput}, diff=${diff} (${diffPercent}%)`
760
+ );
761
+ }
762
+ return {
763
+ estimated: total,
764
+ actual: lastInput,
765
+ breakdown: {
766
+ systemPrompt: systemPromptTokens,
767
+ tools: toolsTokens,
768
+ messages: messagesDisplay
769
+ },
770
+ stats: {
771
+ originalMessageCount: stats.originalCount,
772
+ filteredMessageCount: stats.filteredCount,
773
+ prunedToolCount: stats.prunedToolCount
774
+ },
775
+ calculationBasis
585
776
  };
586
777
  }
778
+ /**
779
+ * Estimates the next input token count using actual token data from the previous LLM call.
780
+ * This is a lightweight version for compaction pre-checks that only returns the total.
781
+ *
782
+ * ## Formula (when actuals are available):
783
+ * estimatedNextInput = lastInputTokens + lastOutputTokens + newMessagesEstimate
784
+ *
785
+ * ## Why this formula works:
786
+ *
787
+ * Consider two consecutive LLM calls:
788
+ *
789
+ * ```
790
+ * Call N:
791
+ * Input sent: system + tools + [user1] = lastInput tokens
792
+ * Output received: assistant response = lastOutput tokens
793
+ *
794
+ * Call N+1:
795
+ * Input will be: system + tools + [user1, assistant1, user2, ...]
796
+ * ≈ lastInput + assistant1_as_input + new_messages
797
+ * ≈ lastInput + lastOutput + newMessagesEstimate
798
+ * ```
799
+ *
800
+ * The assistant's response (lastOutput) becomes part of the next input as conversation
801
+ * history. Text tokenizes similarly whether sent as input or received as output.
802
+ *
803
+ * ## No double-counting:
804
+ *
805
+ * The assistant message is added to history DURING streaming (before this method runs),
806
+ * and recordLastCallMessageCount() captures the count INCLUDING that message.
807
+ * Therefore, newMessages = history.slice(lastMsgCount) EXCLUDES the assistant message,
808
+ * so lastOutput and newMessages don't overlap.
809
+ *
810
+ * ## Pruning caveat:
811
+ *
812
+ * If tool output pruning occurs between calls, lastInput may be stale (higher than
813
+ * actual). This causes OVERESTIMATION, which is SAFE - we'd trigger compaction
814
+ * earlier rather than risk context overflow.
815
+ *
816
+ * @param systemPrompt The system prompt string
817
+ * @param preparedHistory Message history AFTER filterCompacted and pruning
818
+ * @param tools Tool definitions
819
+ * @returns Estimated total input tokens for the next LLM call
820
+ */
821
+ async getEstimatedNextInputTokens(systemPrompt, preparedHistory, tools) {
822
+ const lastInput = this.lastActualInputTokens;
823
+ const lastOutput = this.lastActualOutputTokens;
824
+ const lastMsgCount = this.lastCallMessageCount;
825
+ const currentHistory = await this.historyProvider.getHistory();
826
+ if (lastInput !== null && lastOutput !== null && lastMsgCount !== null) {
827
+ const newMessages = currentHistory.slice(lastMsgCount);
828
+ const newMessagesEstimate = estimateMessagesTokens(newMessages);
829
+ const total = lastInput + lastOutput + newMessagesEstimate;
830
+ this.logger.debug(
831
+ `Estimated next input (actuals-based): ${lastInput} + ${lastOutput} + ${newMessagesEstimate} = ${total}`
832
+ );
833
+ return total;
834
+ }
835
+ const pureEstimate = estimateContextTokens(systemPrompt, preparedHistory, tools);
836
+ this.logger.debug(`Estimated next input (pure estimate): ${pureEstimate.total}`);
837
+ return pureEstimate.total;
838
+ }
587
839
  /**
588
840
  * Gets the system prompt formatted for the target LLM provider
589
841
  * Some providers handle system prompts differently
@@ -600,6 +852,7 @@ ${prompt}`);
600
852
  */
601
853
  async resetConversation() {
602
854
  await this.historyProvider.clearHistory();
855
+ this.resetActualTokenTracking();
603
856
  this.logger.debug(
604
857
  `ContextManager: Conversation history cleared for session ${this.sessionId}`
605
858
  );
@@ -186,6 +186,12 @@ export interface AssistantMessage extends MessageBase {
186
186
  * Present when the provider supports reasoning and returns a final reasoning trace.
187
187
  */
188
188
  reasoning?: string;
189
+ /**
190
+ * Provider-specific metadata for reasoning, used for round-tripping.
191
+ * Contains opaque tokens (e.g., OpenAI itemId, Gemini thought signatures)
192
+ * that must be passed back to the provider on subsequent requests.
193
+ */
194
+ reasoningMetadata?: Record<string, unknown>;
189
195
  /** Token usage accounting for this response */
190
196
  tokenUsage?: TokenUsage;
191
197
  /** Model identifier that generated this response */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context/types.ts"],"names":[],"mappings":"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"}