@dexto/core 1.5.3 → 1.5.5

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 (143) hide show
  1. package/dist/agent/DextoAgent.cjs +190 -1
  2. package/dist/agent/DextoAgent.d.ts +71 -0
  3. package/dist/agent/DextoAgent.d.ts.map +1 -1
  4. package/dist/agent/DextoAgent.js +181 -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 +168 -26
  20. package/dist/context/compaction/strategies/reactive-overflow.d.ts +22 -0
  21. package/dist/context/compaction/strategies/reactive-overflow.d.ts.map +1 -1
  22. package/dist/context/compaction/strategies/reactive-overflow.js +168 -26
  23. package/dist/context/compaction/types.d.ts +13 -1
  24. package/dist/context/compaction/types.d.ts.map +1 -1
  25. package/dist/context/manager.cjs +278 -31
  26. package/dist/context/manager.d.ts +192 -5
  27. package/dist/context/manager.d.ts.map +1 -1
  28. package/dist/context/manager.js +285 -32
  29. package/dist/context/types.d.ts +6 -0
  30. package/dist/context/types.d.ts.map +1 -1
  31. package/dist/context/utils.cjs +77 -11
  32. package/dist/context/utils.d.ts +86 -8
  33. package/dist/context/utils.d.ts.map +1 -1
  34. package/dist/context/utils.js +71 -11
  35. package/dist/errors/types.cjs +0 -2
  36. package/dist/errors/types.d.ts +1 -5
  37. package/dist/errors/types.d.ts.map +1 -1
  38. package/dist/errors/types.js +0 -2
  39. package/dist/events/index.cjs +2 -0
  40. package/dist/events/index.d.ts +21 -6
  41. package/dist/events/index.d.ts.map +1 -1
  42. package/dist/events/index.js +2 -0
  43. package/dist/llm/executor/stream-processor.cjs +104 -28
  44. package/dist/llm/executor/stream-processor.d.ts +7 -0
  45. package/dist/llm/executor/stream-processor.d.ts.map +1 -1
  46. package/dist/llm/executor/stream-processor.js +104 -28
  47. package/dist/llm/executor/turn-executor.cjs +147 -30
  48. package/dist/llm/executor/turn-executor.d.ts +28 -10
  49. package/dist/llm/executor/turn-executor.d.ts.map +1 -1
  50. package/dist/llm/executor/turn-executor.js +147 -30
  51. package/dist/llm/formatters/vercel.cjs +36 -28
  52. package/dist/llm/formatters/vercel.d.ts.map +1 -1
  53. package/dist/llm/formatters/vercel.js +36 -28
  54. package/dist/llm/services/factory.cjs +3 -2
  55. package/dist/llm/services/factory.d.ts +3 -1
  56. package/dist/llm/services/factory.d.ts.map +1 -1
  57. package/dist/llm/services/factory.js +3 -2
  58. package/dist/llm/services/vercel.cjs +31 -6
  59. package/dist/llm/services/vercel.d.ts +18 -3
  60. package/dist/llm/services/vercel.d.ts.map +1 -1
  61. package/dist/llm/services/vercel.js +31 -6
  62. package/dist/session/chat-session.cjs +29 -13
  63. package/dist/session/chat-session.d.ts +6 -4
  64. package/dist/session/chat-session.d.ts.map +1 -1
  65. package/dist/session/chat-session.js +29 -13
  66. package/dist/session/session-manager.cjs +11 -0
  67. package/dist/session/session-manager.d.ts +7 -0
  68. package/dist/session/session-manager.d.ts.map +1 -1
  69. package/dist/session/session-manager.js +11 -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/telemetry/telemetry.cjs +12 -5
  89. package/dist/telemetry/telemetry.d.ts.map +1 -1
  90. package/dist/telemetry/telemetry.js +12 -5
  91. package/dist/utils/index.cjs +3 -1
  92. package/dist/utils/index.d.ts +1 -0
  93. package/dist/utils/index.d.ts.map +1 -1
  94. package/dist/utils/index.js +1 -0
  95. package/package.json +15 -5
  96. package/dist/filesystem/error-codes.cjs +0 -53
  97. package/dist/filesystem/error-codes.d.ts +0 -31
  98. package/dist/filesystem/error-codes.d.ts.map +0 -1
  99. package/dist/filesystem/error-codes.js +0 -30
  100. package/dist/filesystem/errors.cjs +0 -303
  101. package/dist/filesystem/errors.d.ts +0 -109
  102. package/dist/filesystem/errors.d.ts.map +0 -1
  103. package/dist/filesystem/errors.js +0 -280
  104. package/dist/filesystem/filesystem-service.cjs +0 -534
  105. package/dist/filesystem/filesystem-service.d.ts +0 -97
  106. package/dist/filesystem/filesystem-service.d.ts.map +0 -1
  107. package/dist/filesystem/filesystem-service.js +0 -501
  108. package/dist/filesystem/index.cjs +0 -37
  109. package/dist/filesystem/index.d.ts +0 -11
  110. package/dist/filesystem/index.d.ts.map +0 -1
  111. package/dist/filesystem/index.js +0 -11
  112. package/dist/filesystem/path-validator.cjs +0 -250
  113. package/dist/filesystem/path-validator.d.ts +0 -103
  114. package/dist/filesystem/path-validator.d.ts.map +0 -1
  115. package/dist/filesystem/path-validator.js +0 -217
  116. package/dist/filesystem/types.cjs +0 -16
  117. package/dist/filesystem/types.d.ts +0 -175
  118. package/dist/filesystem/types.d.ts.map +0 -1
  119. package/dist/filesystem/types.js +0 -0
  120. package/dist/process/command-validator.cjs +0 -554
  121. package/dist/process/command-validator.d.ts +0 -49
  122. package/dist/process/command-validator.d.ts.map +0 -1
  123. package/dist/process/command-validator.js +0 -531
  124. package/dist/process/error-codes.cjs +0 -47
  125. package/dist/process/error-codes.d.ts +0 -25
  126. package/dist/process/error-codes.d.ts.map +0 -1
  127. package/dist/process/error-codes.js +0 -24
  128. package/dist/process/errors.cjs +0 -244
  129. package/dist/process/errors.d.ts +0 -87
  130. package/dist/process/errors.d.ts.map +0 -1
  131. package/dist/process/errors.js +0 -221
  132. package/dist/process/index.cjs +0 -37
  133. package/dist/process/index.d.ts +0 -11
  134. package/dist/process/index.d.ts.map +0 -1
  135. package/dist/process/index.js +0 -11
  136. package/dist/process/process-service.cjs +0 -497
  137. package/dist/process/process-service.d.ts +0 -69
  138. package/dist/process/process-service.d.ts.map +0 -1
  139. package/dist/process/process-service.js +0 -464
  140. package/dist/process/types.cjs +0 -16
  141. package/dist/process/types.d.ts +0 -107
  142. package/dist/process/types.d.ts.map +0 -1
  143. package/dist/process/types.js +0 -0
@@ -27,8 +27,10 @@ class StreamProcessor {
27
27
  actualTokens = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
28
28
  finishReason = "unknown";
29
29
  reasoningText = "";
30
+ reasoningMetadata;
30
31
  accumulatedText = "";
31
32
  logger;
33
+ hasStepUsage = false;
32
34
  /**
33
35
  * Track pending tool calls (added to context but no result yet).
34
36
  * On cancel/abort, we add synthetic "cancelled" results to maintain tool_use/tool_result pairing.
@@ -59,6 +61,9 @@ class StreamProcessor {
59
61
  break;
60
62
  case "reasoning-delta":
61
63
  this.reasoningText += event.text;
64
+ if (event.providerMetadata) {
65
+ this.reasoningMetadata = event.providerMetadata;
66
+ }
62
67
  if (this.streaming) {
63
68
  this.eventBus.emit("llm:chunk", {
64
69
  chunkType: "reasoning",
@@ -136,41 +141,63 @@ class StreamProcessor {
136
141
  }
137
142
  case "finish-step":
138
143
  if (event.usage) {
139
- const anthropicMeta = event.providerMetadata?.["anthropic"];
140
- const bedrockMeta = event.providerMetadata?.["bedrock"];
141
- const cacheWriteTokens = anthropicMeta?.["cacheCreationInputTokens"] ?? bedrockMeta?.usage?.["cacheWriteInputTokens"] ?? 0;
142
- const cacheReadTokens = anthropicMeta?.["cacheReadInputTokens"] ?? bedrockMeta?.usage?.["cacheReadInputTokens"] ?? event.usage.cachedInputTokens ?? 0;
144
+ const providerMetadata = this.getProviderMetadata(event);
145
+ const stepUsage = this.normalizeUsage(event.usage, providerMetadata);
143
146
  this.actualTokens = {
144
- inputTokens: (this.actualTokens.inputTokens ?? 0) + (event.usage.inputTokens ?? 0),
145
- outputTokens: (this.actualTokens.outputTokens ?? 0) + (event.usage.outputTokens ?? 0),
146
- totalTokens: (this.actualTokens.totalTokens ?? 0) + (event.usage.totalTokens ?? 0),
147
- ...event.usage.reasoningTokens !== void 0 && {
148
- reasoningTokens: (this.actualTokens.reasoningTokens ?? 0) + event.usage.reasoningTokens
147
+ inputTokens: (this.actualTokens.inputTokens ?? 0) + (stepUsage.inputTokens ?? 0),
148
+ outputTokens: (this.actualTokens.outputTokens ?? 0) + (stepUsage.outputTokens ?? 0),
149
+ totalTokens: (this.actualTokens.totalTokens ?? 0) + (stepUsage.totalTokens ?? 0),
150
+ ...stepUsage.reasoningTokens !== void 0 && {
151
+ reasoningTokens: (this.actualTokens.reasoningTokens ?? 0) + stepUsage.reasoningTokens
149
152
  },
150
153
  // Cache tokens
151
- cacheReadTokens: (this.actualTokens.cacheReadTokens ?? 0) + cacheReadTokens,
152
- cacheWriteTokens: (this.actualTokens.cacheWriteTokens ?? 0) + cacheWriteTokens
154
+ cacheReadTokens: (this.actualTokens.cacheReadTokens ?? 0) + (stepUsage.cacheReadTokens ?? 0),
155
+ cacheWriteTokens: (this.actualTokens.cacheWriteTokens ?? 0) + (stepUsage.cacheWriteTokens ?? 0)
153
156
  };
157
+ this.hasStepUsage = true;
154
158
  }
155
159
  break;
156
160
  case "finish": {
157
161
  this.finishReason = event.finishReason;
158
- const cacheReadTokens = this.actualTokens.cacheReadTokens ?? event.totalUsage.cachedInputTokens ?? 0;
159
- const cacheWriteTokens = this.actualTokens.cacheWriteTokens ?? 0;
160
- const providerExcludesCached = this.config.provider === "anthropic" || this.config.provider === "bedrock";
161
- const adjustedInputTokens = providerExcludesCached ? event.totalUsage.inputTokens ?? 0 : (event.totalUsage.inputTokens ?? 0) - cacheReadTokens;
162
- const usage = {
163
- inputTokens: adjustedInputTokens,
164
- outputTokens: event.totalUsage.outputTokens ?? 0,
165
- totalTokens: event.totalUsage.totalTokens ?? 0,
166
- // Capture reasoning tokens if available (from Claude extended thinking, etc.)
167
- ...event.totalUsage.reasoningTokens !== void 0 && {
168
- reasoningTokens: event.totalUsage.reasoningTokens
169
- },
170
- // Cache tokens from accumulated finish-step events or totalUsage fallback
171
- cacheReadTokens,
172
- cacheWriteTokens
173
- };
162
+ const providerMetadata = this.getProviderMetadata(event);
163
+ const fallbackUsage = this.normalizeUsage(
164
+ event.totalUsage,
165
+ providerMetadata
166
+ );
167
+ const usage = this.hasStepUsage ? { ...this.actualTokens } : fallbackUsage;
168
+ if (this.hasStepUsage) {
169
+ const fallbackInput = fallbackUsage.inputTokens ?? 0;
170
+ if ((usage.inputTokens ?? 0) === 0 && fallbackInput > 0) {
171
+ this.logger.debug(
172
+ "Backfilling inputTokens from fallback usage (step reported 0)",
173
+ { stepValue: usage.inputTokens, fallbackValue: fallbackInput }
174
+ );
175
+ usage.inputTokens = fallbackInput;
176
+ }
177
+ const fallbackOutput = fallbackUsage.outputTokens ?? 0;
178
+ if ((usage.outputTokens ?? 0) === 0 && fallbackOutput > 0) {
179
+ this.logger.debug(
180
+ "Backfilling outputTokens from fallback usage (step reported 0)",
181
+ { stepValue: usage.outputTokens, fallbackValue: fallbackOutput }
182
+ );
183
+ usage.outputTokens = fallbackOutput;
184
+ }
185
+ const fallbackCacheRead = fallbackUsage.cacheReadTokens ?? 0;
186
+ if ((usage.cacheReadTokens ?? 0) === 0 && fallbackCacheRead > 0) {
187
+ usage.cacheReadTokens = fallbackCacheRead;
188
+ }
189
+ const fallbackCacheWrite = fallbackUsage.cacheWriteTokens ?? 0;
190
+ if ((usage.cacheWriteTokens ?? 0) === 0 && fallbackCacheWrite > 0) {
191
+ usage.cacheWriteTokens = fallbackCacheWrite;
192
+ }
193
+ const fallbackTotalTokens = fallbackUsage.totalTokens ?? 0;
194
+ if ((usage.totalTokens ?? 0) === 0 && fallbackTotalTokens > 0) {
195
+ usage.totalTokens = fallbackTotalTokens;
196
+ }
197
+ if (usage.reasoningTokens === void 0 && fallbackUsage.reasoningTokens !== void 0) {
198
+ usage.reasoningTokens = fallbackUsage.reasoningTokens;
199
+ }
200
+ }
174
201
  this.actualTokens = usage;
175
202
  this.logger.info("LLM response complete", {
176
203
  finishReason: event.finishReason,
@@ -188,7 +215,12 @@ class StreamProcessor {
188
215
  await this.contextManager.updateAssistantMessage(
189
216
  this.assistantMessageId,
190
217
  {
191
- tokenUsage: usage
218
+ tokenUsage: usage,
219
+ // Persist reasoning text and metadata for round-tripping
220
+ ...this.reasoningText && { reasoning: this.reasoningText },
221
+ ...this.reasoningMetadata && {
222
+ reasoningMetadata: this.reasoningMetadata
223
+ }
192
224
  }
193
225
  );
194
226
  }
@@ -200,6 +232,9 @@ class StreamProcessor {
200
232
  provider: this.config.provider,
201
233
  model: this.config.model,
202
234
  tokenUsage: usage,
235
+ ...this.config.estimatedInputTokens !== void 0 && {
236
+ estimatedInputTokens: this.config.estimatedInputTokens
237
+ },
203
238
  finishReason: this.finishReason
204
239
  });
205
240
  }
@@ -261,6 +296,9 @@ class StreamProcessor {
261
296
  provider: this.config.provider,
262
297
  model: this.config.model,
263
298
  tokenUsage: this.actualTokens,
299
+ ...this.config.estimatedInputTokens !== void 0 && {
300
+ estimatedInputTokens: this.config.estimatedInputTokens
301
+ },
264
302
  finishReason: "cancelled"
265
303
  });
266
304
  return {
@@ -282,6 +320,9 @@ class StreamProcessor {
282
320
  provider: this.config.provider,
283
321
  model: this.config.model,
284
322
  tokenUsage: this.actualTokens,
323
+ ...this.config.estimatedInputTokens !== void 0 && {
324
+ estimatedInputTokens: this.config.estimatedInputTokens
325
+ },
285
326
  finishReason: "cancelled"
286
327
  });
287
328
  return {
@@ -304,6 +345,41 @@ class StreamProcessor {
304
345
  usage: this.actualTokens
305
346
  };
306
347
  }
348
+ getCacheTokensFromProviderMetadata(providerMetadata) {
349
+ const anthropicMeta = providerMetadata?.["anthropic"];
350
+ const bedrockMeta = providerMetadata?.["bedrock"];
351
+ const cacheWriteTokens = anthropicMeta?.["cacheCreationInputTokens"] ?? bedrockMeta?.usage?.["cacheWriteInputTokens"] ?? 0;
352
+ const cacheReadTokens = anthropicMeta?.["cacheReadInputTokens"] ?? bedrockMeta?.usage?.["cacheReadInputTokens"] ?? 0;
353
+ return { cacheReadTokens, cacheWriteTokens };
354
+ }
355
+ normalizeUsage(usage, providerMetadata) {
356
+ const inputTokensRaw = usage?.inputTokens ?? 0;
357
+ const outputTokens = usage?.outputTokens ?? 0;
358
+ const totalTokens = usage?.totalTokens ?? 0;
359
+ const reasoningTokens = usage?.reasoningTokens;
360
+ const cachedInputTokens = usage?.cachedInputTokens;
361
+ const inputTokenDetails = usage?.inputTokenDetails;
362
+ const providerCache = this.getCacheTokensFromProviderMetadata(providerMetadata);
363
+ const cacheReadTokens = inputTokenDetails?.cacheReadTokens ?? cachedInputTokens ?? providerCache.cacheReadTokens ?? 0;
364
+ const cacheWriteTokens = inputTokenDetails?.cacheWriteTokens ?? providerCache.cacheWriteTokens ?? 0;
365
+ const needsCacheWriteAdjustment = inputTokenDetails === void 0 && cachedInputTokens !== void 0 && providerCache.cacheWriteTokens > 0;
366
+ const noCacheTokens = inputTokenDetails?.noCacheTokens ?? (cachedInputTokens !== void 0 ? inputTokensRaw - cachedInputTokens - (needsCacheWriteAdjustment ? providerCache.cacheWriteTokens : 0) : inputTokensRaw);
367
+ return {
368
+ inputTokens: Math.max(0, noCacheTokens),
369
+ outputTokens,
370
+ totalTokens,
371
+ ...reasoningTokens !== void 0 && { reasoningTokens },
372
+ cacheReadTokens,
373
+ cacheWriteTokens
374
+ };
375
+ }
376
+ getProviderMetadata(event) {
377
+ const metadata = "providerMetadata" in event ? event.providerMetadata : void 0;
378
+ if (!metadata || typeof metadata !== "object") {
379
+ return void 0;
380
+ }
381
+ return metadata;
382
+ }
307
383
  async createAssistantMessage() {
308
384
  await this.contextManager.addAssistantMessage("", [], {});
309
385
  return this.getLastMessageId();
@@ -91,7 +91,7 @@ var import_reactive_overflow = require("../../context/compaction/strategies/reac
91
91
  const toolSupportCache = /* @__PURE__ */ new Map();
92
92
  const LOCAL_PROVIDERS = ["ollama", "local"];
93
93
  class TurnExecutor {
94
- constructor(model, toolManager, contextManager, eventBus, resourceManager, sessionId, config, llmContext, logger, messageQueue, modelLimits, externalSignal, compactionStrategy) {
94
+ constructor(model, toolManager, contextManager, eventBus, resourceManager, sessionId, config, llmContext, logger, messageQueue, modelLimits, externalSignal, compactionStrategy, compactionThresholdPercent = 1) {
95
95
  this.model = model;
96
96
  this.toolManager = toolManager;
97
97
  this.contextManager = contextManager;
@@ -103,6 +103,7 @@ class TurnExecutor {
103
103
  this.messageQueue = messageQueue;
104
104
  this.modelLimits = modelLimits;
105
105
  this.externalSignal = externalSignal;
106
+ this.compactionThresholdPercent = compactionThresholdPercent;
106
107
  this.logger = logger.createChild(import_types4.DextoLogComponent.EXECUTOR);
107
108
  this.stepAbortController = new AbortController();
108
109
  if (compactionStrategy !== void 0) {
@@ -125,11 +126,13 @@ class TurnExecutor {
125
126
  approvalMetadata = /* @__PURE__ */ new Map();
126
127
  /**
127
128
  * Get StreamProcessor config from TurnExecutor state.
129
+ * @param estimatedInputTokens Optional estimated input tokens for analytics
128
130
  */
129
- getStreamProcessorConfig() {
131
+ getStreamProcessorConfig(estimatedInputTokens) {
130
132
  return {
131
133
  provider: this.llmContext.provider,
132
- model: this.llmContext.model
134
+ model: this.llmContext.model,
135
+ ...estimatedInputTokens !== void 0 && { estimatedInputTokens }
133
136
  };
134
137
  }
135
138
  /**
@@ -185,13 +188,41 @@ class TurnExecutor {
185
188
  if (coalesced) {
186
189
  await this.injectQueuedMessages(coalesced);
187
190
  }
188
- if (lastStepTokens && this.checkAndHandleOverflow(lastStepTokens)) {
189
- await this.compress(lastStepTokens.inputTokens ?? 0);
190
- }
191
- const prepared = await this.contextManager.getFormattedMessagesWithCompression(
191
+ await this.pruneOldToolOutputs();
192
+ let prepared = await this.contextManager.getFormattedMessagesForLLM(
192
193
  contributorContext,
193
194
  this.llmContext
194
195
  );
196
+ const toolDefinitions = supportsTools ? await this.toolManager.getAllTools() : {};
197
+ let estimatedTokens = await this.contextManager.getEstimatedNextInputTokens(
198
+ prepared.systemPrompt,
199
+ prepared.preparedHistory,
200
+ toolDefinitions
201
+ );
202
+ if (this.shouldCompact(estimatedTokens)) {
203
+ this.logger.debug(
204
+ `Pre-check: estimated ${estimatedTokens} tokens exceeds threshold, compacting`
205
+ );
206
+ const didCompact = await this.compactContext(
207
+ estimatedTokens,
208
+ contributorContext,
209
+ toolDefinitions
210
+ );
211
+ if (didCompact) {
212
+ prepared = await this.contextManager.getFormattedMessagesForLLM(
213
+ contributorContext,
214
+ this.llmContext
215
+ );
216
+ estimatedTokens = await this.contextManager.getEstimatedNextInputTokens(
217
+ prepared.systemPrompt,
218
+ prepared.preparedHistory,
219
+ toolDefinitions
220
+ );
221
+ this.logger.debug(
222
+ `Post-compaction: recomputed estimate is ${estimatedTokens} tokens`
223
+ );
224
+ }
225
+ }
195
226
  this.logger.debug(`Step ${stepCount}: Starting`);
196
227
  const tools = supportsTools ? await this.createTools() : {};
197
228
  const streamProcessor = new import_stream_processor.StreamProcessor(
@@ -199,7 +230,7 @@ class TurnExecutor {
199
230
  this.eventBus,
200
231
  this.resourceManager,
201
232
  this.stepAbortController.signal,
202
- this.getStreamProcessorConfig(),
233
+ this.getStreamProcessorConfig(estimatedTokens),
203
234
  this.logger,
204
235
  streaming,
205
236
  this.approvalMetadata
@@ -238,6 +269,35 @@ class TurnExecutor {
238
269
  this.logger.debug(
239
270
  `Step ${stepCount}: Finished with reason="${result.finishReason}", tokens=${JSON.stringify(result.usage)}`
240
271
  );
272
+ if (result.finishReason === "cancelled") {
273
+ this.logger.info(
274
+ `Context estimation (cancelled): keeping last known actuals, partial response (${result.text.length} chars) will be estimated`
275
+ );
276
+ } else if (result.usage?.inputTokens !== void 0) {
277
+ const contextInputTokens2 = this.getContextInputTokens(result.usage);
278
+ const actualInputTokens = contextInputTokens2 ?? result.usage.inputTokens;
279
+ const diff = estimatedTokens - actualInputTokens;
280
+ const diffPercent = actualInputTokens > 0 ? (diff / actualInputTokens * 100).toFixed(1) : "0.0";
281
+ this.logger.info(
282
+ `Context estimation accuracy: estimated=${estimatedTokens}, actual=${actualInputTokens}, error=${diff} (${diffPercent}%)`
283
+ );
284
+ this.contextManager.setLastActualInputTokens(actualInputTokens);
285
+ if (result.usage?.outputTokens !== void 0) {
286
+ this.contextManager.setLastActualOutputTokens(result.usage.outputTokens);
287
+ }
288
+ await this.contextManager.recordLastCallMessageCount();
289
+ }
290
+ const contextInputTokens = result.usage ? this.getContextInputTokens(result.usage) : null;
291
+ if (contextInputTokens && this.shouldCompactFromActual(contextInputTokens)) {
292
+ this.logger.debug(
293
+ `Post-response: actual ${contextInputTokens} tokens exceeds threshold, compacting`
294
+ );
295
+ await this.compactContext(
296
+ contextInputTokens,
297
+ contributorContext,
298
+ toolDefinitions
299
+ );
300
+ }
241
301
  if (result.finishReason !== "tool-calls") {
242
302
  const queuedOnTerminate = this.messageQueue.dequeueAll();
243
303
  if (queuedOnTerminate) {
@@ -261,7 +321,6 @@ class TurnExecutor {
261
321
  lastFinishReason = "max-steps";
262
322
  break;
263
323
  }
264
- await this.pruneOldToolOutputs();
265
324
  }
266
325
  } catch (error) {
267
326
  const mappedError = this.mapProviderError(error);
@@ -589,7 +648,7 @@ class TurnExecutor {
589
648
  /**
590
649
  * Prunes old tool outputs by marking them with compactedAt timestamp.
591
650
  * Does NOT modify content - transformation happens at format time in
592
- * ContextManager.getFormattedMessagesWithCompression().
651
+ * ContextManager.prepareHistory().
593
652
  *
594
653
  * Algorithm:
595
654
  * 1. Go backwards through history (most recent first)
@@ -658,54 +717,108 @@ class TurnExecutor {
658
717
  this.messageQueue.clear();
659
718
  }
660
719
  /**
661
- * Check if context has overflowed based on actual token usage from API.
720
+ * Check if context should be compacted based on estimated token count.
721
+ * Uses the threshold percentage from compaction config to trigger earlier (e.g., at 90%).
722
+ *
723
+ * @param estimatedTokens Estimated token count from the current context
724
+ * @returns true if compaction is needed before making the LLM call
662
725
  */
663
- checkAndHandleOverflow(tokens) {
726
+ shouldCompact(estimatedTokens) {
664
727
  if (!this.modelLimits || !this.compactionStrategy) {
665
728
  return false;
666
729
  }
667
- return (0, import_overflow.isOverflow)(tokens, this.modelLimits);
730
+ return (0, import_overflow.isOverflow)(
731
+ { inputTokens: estimatedTokens },
732
+ this.modelLimits,
733
+ this.compactionThresholdPercent
734
+ );
668
735
  }
669
736
  /**
670
- * Compress context using ReactiveOverflowStrategy.
737
+ * Check if context should be compacted based on actual token count from API response.
738
+ * This is a post-response check using real token counts rather than estimates.
671
739
  *
672
- * Generates a summary of older messages and adds it to history.
673
- * The actual token reduction happens at read-time via filterCompacted()
674
- * in getFormattedMessagesWithCompression().
740
+ * @param actualTokens Actual input token count from the API response
741
+ * @returns true if compaction is needed
742
+ */
743
+ shouldCompactFromActual(actualTokens) {
744
+ if (!this.modelLimits || !this.compactionStrategy) {
745
+ return false;
746
+ }
747
+ return (0, import_overflow.isOverflow)(
748
+ { inputTokens: actualTokens },
749
+ this.modelLimits,
750
+ this.compactionThresholdPercent
751
+ );
752
+ }
753
+ /**
754
+ * Compact context by generating a summary and adding it to the same session.
755
+ *
756
+ * The summary message is added to the conversation history with `isSummary: true` metadata.
757
+ * When the context is loaded via getFormattedMessagesForLLM(), filterCompacted() will
758
+ * exclude all messages before the summary, effectively compacting the context.
675
759
  *
676
- * @param originalTokens The actual input token count from API that triggered overflow
760
+ * @param originalTokens The estimated input token count that triggered overflow
761
+ * @param contributorContext Context for system prompt contributors (needed for accurate token estimation)
762
+ * @param tools Tool definitions (needed for accurate token estimation)
763
+ * @returns true if compaction occurred, false if skipped
677
764
  */
678
- async compress(originalTokens) {
765
+ async compactContext(originalTokens, contributorContext, tools) {
679
766
  if (!this.compactionStrategy) {
680
- return;
767
+ return false;
681
768
  }
682
769
  this.logger.info(
683
- `Context overflow detected (${originalTokens} tokens), running compression`
770
+ `Context overflow detected (${originalTokens} tokens), checking if compression is possible`
684
771
  );
685
772
  const history = await this.contextManager.getHistory();
773
+ const { filterCompacted } = await import("../../context/utils.js");
774
+ const originalFiltered = filterCompacted(history);
775
+ const originalMessages = originalFiltered.length;
776
+ if (history.length < 4) {
777
+ this.logger.debug("Compaction skipped: history too short to summarize");
778
+ return false;
779
+ }
780
+ this.eventBus.emit("context:compacting", {
781
+ estimatedTokens: originalTokens
782
+ });
686
783
  const summaryMessages = await this.compactionStrategy.compact(history);
687
784
  if (summaryMessages.length === 0) {
688
- this.logger.debug("Compaction returned no summary (history too short)");
689
- return;
785
+ this.logger.debug(
786
+ "Compaction skipped: strategy returned no summary (likely already compacted or nothing to summarize)"
787
+ );
788
+ this.eventBus.emit("context:compacted", {
789
+ originalTokens,
790
+ compactedTokens: originalTokens,
791
+ // No change
792
+ originalMessages,
793
+ compactedMessages: originalMessages,
794
+ // No change
795
+ strategy: this.compactionStrategy.name,
796
+ reason: "overflow"
797
+ });
798
+ return false;
690
799
  }
691
800
  for (const summary of summaryMessages) {
692
801
  await this.contextManager.addMessage(summary);
693
802
  }
694
- const { filterCompacted, estimateMessagesTokens } = await import("../../context/utils.js");
695
- const updatedHistory = await this.contextManager.getHistory();
696
- const filteredHistory = filterCompacted(updatedHistory);
697
- const compactedTokens = estimateMessagesTokens(filteredHistory);
803
+ this.contextManager.resetActualTokenTracking();
804
+ const afterEstimate = await this.contextManager.getContextTokenEstimate(
805
+ contributorContext,
806
+ tools
807
+ );
808
+ const compactedTokens = afterEstimate.estimated;
809
+ const compactedMessages = afterEstimate.stats.filteredMessageCount;
698
810
  this.eventBus.emit("context:compacted", {
699
811
  originalTokens,
700
812
  compactedTokens,
701
- originalMessages: history.length,
702
- compactedMessages: filteredHistory.length,
813
+ originalMessages,
814
+ compactedMessages,
703
815
  strategy: this.compactionStrategy.name,
704
816
  reason: "overflow"
705
817
  });
706
818
  this.logger.info(
707
- `Compaction complete: ${originalTokens} \u2192 ~${compactedTokens} tokens (${history.length} \u2192 ${filteredHistory.length} messages after filtering)`
819
+ `Compaction complete: ${originalTokens} \u2192 ~${compactedTokens} tokens (${originalMessages} \u2192 ${compactedMessages} messages after filtering)`
708
820
  );
821
+ return true;
709
822
  }
710
823
  /**
711
824
  * Set telemetry span attributes for token usage.
@@ -728,6 +841,10 @@ class TurnExecutor {
728
841
  activeSpan.setAttribute("gen_ai.usage.reasoning_tokens", usage.reasoningTokens);
729
842
  }
730
843
  }
844
+ getContextInputTokens(usage) {
845
+ if (usage.inputTokens === void 0) return null;
846
+ return usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
847
+ }
731
848
  /**
732
849
  * Map provider errors to DextoRuntimeError.
733
850
  */
@@ -34,6 +34,7 @@ export declare class TurnExecutor {
34
34
  private messageQueue;
35
35
  private modelLimits?;
36
36
  private externalSignal?;
37
+ private compactionThresholdPercent;
37
38
  private logger;
38
39
  /**
39
40
  * Per-step abort controller. Created fresh for each iteration of the loop.
@@ -52,9 +53,10 @@ export declare class TurnExecutor {
52
53
  temperature?: number | undefined;
53
54
  baseURL?: string | undefined;
54
55
  reasoningEffort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | undefined;
55
- }, llmContext: LLMContext, logger: IDextoLogger, messageQueue: MessageQueueService, modelLimits?: ModelLimits | undefined, externalSignal?: AbortSignal | undefined, compactionStrategy?: ICompactionStrategy | null);
56
+ }, llmContext: LLMContext, logger: IDextoLogger, messageQueue: MessageQueueService, modelLimits?: ModelLimits | undefined, externalSignal?: AbortSignal | undefined, compactionStrategy?: ICompactionStrategy | null, compactionThresholdPercent?: number);
56
57
  /**
57
58
  * Get StreamProcessor config from TurnExecutor state.
59
+ * @param estimatedInputTokens Optional estimated input tokens for analytics
58
60
  */
59
61
  private getStreamProcessorConfig;
60
62
  /**
@@ -119,7 +121,7 @@ export declare class TurnExecutor {
119
121
  /**
120
122
  * Prunes old tool outputs by marking them with compactedAt timestamp.
121
123
  * Does NOT modify content - transformation happens at format time in
122
- * ContextManager.getFormattedMessagesWithCompression().
124
+ * ContextManager.prepareHistory().
123
125
  *
124
126
  * Algorithm:
125
127
  * 1. Go backwards through history (most recent first)
@@ -143,23 +145,39 @@ export declare class TurnExecutor {
143
145
  */
144
146
  private cleanup;
145
147
  /**
146
- * Check if context has overflowed based on actual token usage from API.
148
+ * Check if context should be compacted based on estimated token count.
149
+ * Uses the threshold percentage from compaction config to trigger earlier (e.g., at 90%).
150
+ *
151
+ * @param estimatedTokens Estimated token count from the current context
152
+ * @returns true if compaction is needed before making the LLM call
153
+ */
154
+ private shouldCompact;
155
+ /**
156
+ * Check if context should be compacted based on actual token count from API response.
157
+ * This is a post-response check using real token counts rather than estimates.
158
+ *
159
+ * @param actualTokens Actual input token count from the API response
160
+ * @returns true if compaction is needed
147
161
  */
148
- private checkAndHandleOverflow;
162
+ private shouldCompactFromActual;
149
163
  /**
150
- * Compress context using ReactiveOverflowStrategy.
164
+ * Compact context by generating a summary and adding it to the same session.
151
165
  *
152
- * Generates a summary of older messages and adds it to history.
153
- * The actual token reduction happens at read-time via filterCompacted()
154
- * in getFormattedMessagesWithCompression().
166
+ * The summary message is added to the conversation history with `isSummary: true` metadata.
167
+ * When the context is loaded via getFormattedMessagesForLLM(), filterCompacted() will
168
+ * exclude all messages before the summary, effectively compacting the context.
155
169
  *
156
- * @param originalTokens The actual input token count from API that triggered overflow
170
+ * @param originalTokens The estimated input token count that triggered overflow
171
+ * @param contributorContext Context for system prompt contributors (needed for accurate token estimation)
172
+ * @param tools Tool definitions (needed for accurate token estimation)
173
+ * @returns true if compaction occurred, false if skipped
157
174
  */
158
- private compress;
175
+ private compactContext;
159
176
  /**
160
177
  * Set telemetry span attributes for token usage.
161
178
  */
162
179
  private setTelemetryAttributes;
180
+ private getContextInputTokens;
163
181
  /**
164
182
  * Map provider errors to DextoRuntimeError.
165
183
  */
@@ -1 +1 @@
1
- {"version":3,"file":"turn-executor.d.ts","sourceRoot":"","sources":["../../../src/llm/executor/turn-executor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"turn-executor.d.ts","sourceRoot":"","sources":["../../../src/llm/executor/turn-executor.ts"],"names":[],"mappings":"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"}