@evalops/maestro 0.10.30 → 0.10.31

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.
@@ -33,31 +33,12 @@
33
33
  * @module agent/compaction
34
34
  */
35
35
  import type { PromptProjectDocManifest } from "../config/index.js";
36
- import type { SessionEntry } from "../session/types.js";
37
36
  import { readTool } from "../tools/read.js";
38
37
  import { type CompactionHookContext, type CompactionHookService } from "./compaction-hooks.js";
39
- import type { Api, AppMessage, AssistantMessage, Usage } from "./types.js";
40
- /**
41
- * Configuration for compaction behavior.
42
- */
43
- export interface CompactionSettings {
44
- /** Whether auto-compaction is enabled */
45
- enabled: boolean;
46
- /**
47
- * Tokens to reserve for summary generation and safety margin.
48
- * Default: 16384 (~13k for summary + ~3k safety margin)
49
- */
50
- reserveTokens: number;
51
- /**
52
- * Approximate number of recent tokens to preserve verbatim.
53
- * Default: 20000 (recent context preserved without summarization)
54
- */
55
- keepRecentTokens: number;
56
- }
57
- /**
58
- * Default compaction settings based on research into Claude Code, Codex, and OpenCode.
59
- */
60
- export declare const DEFAULT_COMPACTION_SETTINGS: CompactionSettings;
38
+ import type { CompactionSettings } from "./compaction/cut-points.js";
39
+ export { DEFAULT_COMPACTION_SETTINGS, adjustBoundaryForToolResults, calculateContextTokens, calculateUsagePercent, findCutPoint, findCutPointWithSplitInfo, findTurnStartIndex, getLastAssistantUsage, getLastAssistantUsageFromEntries, shouldCompact, } from "./compaction/cut-points.js";
40
+ export type { CompactionSettings, CutPointResult, } from "./compaction/cut-points.js";
41
+ import type { Api, AppMessage, AssistantMessage } from "./types.js";
61
42
  /**
62
43
  * Internal user prompt appended after compaction so the model resumes from the
63
44
  * summarized context on the next turn.
@@ -78,127 +59,7 @@ export interface CompactionResult {
78
59
  /** Messages to keep verbatim */
79
60
  keptMessages: AppMessage[];
80
61
  }
81
- /**
82
- * Calculate total context tokens from usage metadata.
83
- *
84
- * This includes all token types that contribute to context window consumption:
85
- * - input: Tokens from user/system messages
86
- * - output: Tokens generated by the model
87
- * - cacheRead: Tokens read from prompt cache
88
- * - cacheWrite: Tokens written to prompt cache
89
- *
90
- * @param usage - Usage metadata from an assistant message
91
- * @returns Total context token count
92
- */
93
- export declare function calculateContextTokens(usage: Usage): number;
94
62
  type ReadRestoreExecutor = typeof readTool.execute;
95
- /**
96
- * Find the last non-aborted assistant message usage from a message array.
97
- *
98
- * Used to determine current context window consumption. Walks backwards
99
- * to find the most recent valid usage metadata.
100
- *
101
- * @param messages - Array of application messages
102
- * @returns Usage metadata from the last assistant message, or null
103
- */
104
- export declare function getLastAssistantUsage(messages: AppMessage[]): Usage | null;
105
- /**
106
- * Find the last non-aborted assistant message usage from session entries.
107
- *
108
- * @param entries - Array of session entries
109
- * @returns Usage metadata from the last assistant message, or null
110
- */
111
- export declare function getLastAssistantUsageFromEntries(entries: SessionEntry[]): Usage | null;
112
- /**
113
- * Determine if compaction should trigger based on current context usage.
114
- *
115
- * Compaction triggers when context usage exceeds `contextWindow - reserveTokens`,
116
- * leaving enough room for summary generation plus a safety margin.
117
- *
118
- * @param contextTokens - Current context window usage in tokens
119
- * @param contextWindow - Model's total context window size
120
- * @param settings - Compaction configuration
121
- * @returns true if compaction should trigger
122
- */
123
- export declare function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean;
124
- /**
125
- * Calculate context usage percentage.
126
- *
127
- * @param contextTokens - Current context window usage in tokens
128
- * @param contextWindow - Model's total context window size
129
- * @returns Percentage of context window used (0-100)
130
- */
131
- export declare function calculateUsagePercent(contextTokens: number, contextWindow: number): number;
132
- /**
133
- * Result of finding a cut point, including split turn information.
134
- */
135
- export interface CutPointResult {
136
- /** Index of first message to keep (messages before this are summarized) */
137
- firstKeptIndex: number;
138
- /** Index of user message that starts the turn being split, or -1 if not splitting */
139
- turnStartIndex: number;
140
- /** Whether this cut splits a turn (cut point is not a user message) */
141
- isSplitTurn: boolean;
142
- }
143
- /**
144
- * Find the user message that starts the turn containing the given index.
145
- *
146
- * @param messages - Array of messages
147
- * @param entryIndex - Index of the message to find the turn start for
148
- * @param startIndex - First index to consider (inclusive)
149
- * @returns Index of the user message that starts this turn, or -1 if not found
150
- */
151
- export declare function findTurnStartIndex(messages: AppMessage[], entryIndex: number, startIndex: number): number;
152
- /**
153
- * Find the optimal cut point in messages that preserves `keepRecentTokens`.
154
- *
155
- * The algorithm:
156
- * 1. Find all turn boundaries (user message indices)
157
- * 2. Walk backwards collecting assistant token usage
158
- * 3. Find where cumulative token difference exceeds keepRecentTokens
159
- * 4. Return the nearest turn boundary at or before that point
160
- *
161
- * This ensures:
162
- * - Recent context is preserved based on actual token usage, not message count
163
- * - Turn integrity is maintained (user/assistant/toolResult groups stay together)
164
- * - More context is kept when messages are small, less when large
165
- *
166
- * @param messages - Array of messages to analyze
167
- * @param startIndex - First index to consider for cutting
168
- * @param endIndex - Last index to consider (exclusive)
169
- * @param keepRecentTokens - Approximate tokens to preserve
170
- * @returns Index of first message to keep (messages before this are summarized)
171
- */
172
- export declare function findCutPoint(messages: AppMessage[], startIndex: number, endIndex: number, keepRecentTokens: number): number;
173
- /**
174
- * Find the optimal cut point with split turn information.
175
- *
176
- * Similar to findCutPoint but:
177
- * - Can cut at assistant messages (not just user messages)
178
- * - Returns information about whether we're splitting a turn
179
- *
180
- * This allows for more aggressive compaction when turns are very large,
181
- * while providing the context needed to summarize the split turn prefix.
182
- *
183
- * @param messages - Array of messages to analyze
184
- * @param startIndex - First index to consider for cutting
185
- * @param endIndex - Last index to consider (exclusive)
186
- * @param keepRecentTokens - Approximate tokens to preserve
187
- * @returns CutPointResult with index and split turn information
188
- */
189
- export declare function findCutPointWithSplitInfo(messages: AppMessage[], startIndex: number, endIndex: number, keepRecentTokens: number): CutPointResult;
190
- /**
191
- * Adjust a cut boundary to preserve tool call/result integrity.
192
- *
193
- * Tool results must stay paired with their originating assistant message.
194
- * If a toolResult would be kept but its toolCall would be cut, this
195
- * moves the boundary back to include the toolCall.
196
- *
197
- * @param messages - Array of messages
198
- * @param boundary - Initial cut point
199
- * @returns Adjusted boundary that preserves tool integrity
200
- */
201
- export declare function adjustBoundaryForToolResults(messages: AppMessage[], boundary: number): number;
202
63
  /**
203
64
  * Default prompt for generating compaction summaries.
204
65
  *
@@ -383,5 +244,4 @@ export declare function performCompaction(params: {
383
244
  renderSummaryText?: (message: AssistantMessage) => string;
384
245
  readRestoreExecute?: ReadRestoreExecutor;
385
246
  }): Promise<PerformCompactionResult>;
386
- export {};
387
247
  //# sourceMappingURL=compaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/agent/compaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/agent/compaction.ts"],"names":[],"mappings":"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"}
@@ -41,20 +41,17 @@ import { createLogger } from "../utils/logger.js";
41
41
  import { expandUserPath } from "../utils/path-validation.js";
42
42
  import { runPostCompactionCleanup } from "./compaction-cleanup.js";
43
43
  import { createCompactionHookService, } from "./compaction-hooks.js";
44
+ import { DEFAULT_COMPACTION_SETTINGS, adjustBoundaryForToolResults, calculateContextTokens, findCutPoint, findTurnBoundaries, getLastAssistantUsage, } from "./compaction/cut-points.js";
45
+ export { DEFAULT_COMPACTION_SETTINGS, adjustBoundaryForToolResults, calculateContextTokens, calculateUsagePercent, findCutPoint, findCutPointWithSplitInfo, findTurnStartIndex, getLastAssistantUsage, getLastAssistantUsageFromEntries, shouldCompact, } from "./compaction/cut-points.js";
44
46
  import { BACKGROUND_TASKS_COMPACTION_CUSTOM_TYPE, HEADLESS_CLIENT_REQUESTS_COMPACTION_CUSTOM_TYPE, MCP_SERVERS_COMPACTION_CUSTOM_TYPE, PLAN_FILE_COMPACTION_CUSTOM_TYPE, PLAN_MODE_COMPACTION_CUSTOM_TYPE, } from "./compaction-restoration.js";
45
47
  import { isContextOverflow as isCompactionOverflowMessage, isOverflowErrorMessage, parseOverflowDetails, } from "./context-overflow.js";
46
48
  import { convertAppMessageToLlm, createHookMessage, } from "./custom-messages.js";
47
49
  import { getPlanFilePathForCompactionRestore } from "./plan-mode.js";
48
50
  import { SESSION_START_INITIAL_USER_METADATA_KIND } from "./session-start-metadata.js";
49
51
  const logger = createLogger("agent:compaction");
50
- /**
51
- * Default compaction settings based on research into Claude Code, Codex, and OpenCode.
52
- */
53
- export const DEFAULT_COMPACTION_SETTINGS = {
54
- enabled: true,
55
- reserveTokens: 16384,
56
- keepRecentTokens: 20000,
57
- };
52
+ // ============================================================================
53
+ // Types
54
+ // ============================================================================
58
55
  /**
59
56
  * Internal user prompt appended after compaction so the model resumes from the
60
57
  * summarized context on the next turn.
@@ -74,38 +71,6 @@ const SKILL_RESTORE_TRUNCATION_MARKER = "\n\n[... restored skill truncated for c
74
71
  // ============================================================================
75
72
  // Token Calculation
76
73
  // ============================================================================
77
- /**
78
- * Calculate total context tokens from usage metadata.
79
- *
80
- * This includes all token types that contribute to context window consumption:
81
- * - input: Tokens from user/system messages
82
- * - output: Tokens generated by the model
83
- * - cacheRead: Tokens read from prompt cache
84
- * - cacheWrite: Tokens written to prompt cache
85
- *
86
- * @param usage - Usage metadata from an assistant message
87
- * @returns Total context token count
88
- */
89
- export function calculateContextTokens(usage) {
90
- return usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
91
- }
92
- /**
93
- * Extract usage metadata from a message if it's a non-aborted assistant message.
94
- *
95
- * @param msg - Any application message
96
- * @returns Usage metadata or null if not available/applicable
97
- */
98
- function getAssistantUsage(msg) {
99
- if (msg.role === "assistant") {
100
- const assistantMsg = msg;
101
- if (assistantMsg.stopReason !== "aborted" &&
102
- assistantMsg.stopReason !== "error" &&
103
- assistantMsg.usage) {
104
- return assistantMsg.usage;
105
- }
106
- }
107
- return null;
108
- }
109
74
  function shouldSkipAssistantCompactionMessage(message) {
110
75
  return (message.role === "assistant" &&
111
76
  (message.stopReason === "aborted" || message.stopReason === "error"));
@@ -952,306 +917,6 @@ function truncateSummaryInputForOverflowRetry(messages, overflowErrorMessage) {
952
917
  ? [preamble, markerMessage, ...truncatedBody]
953
918
  : [markerMessage, ...truncatedBody];
954
919
  }
955
- /**
956
- * Find the last non-aborted assistant message usage from a message array.
957
- *
958
- * Used to determine current context window consumption. Walks backwards
959
- * to find the most recent valid usage metadata.
960
- *
961
- * @param messages - Array of application messages
962
- * @returns Usage metadata from the last assistant message, or null
963
- */
964
- export function getLastAssistantUsage(messages) {
965
- for (let i = messages.length - 1; i >= 0; i--) {
966
- const usage = getAssistantUsage(messages[i]);
967
- if (usage)
968
- return usage;
969
- }
970
- return null;
971
- }
972
- /**
973
- * Find the last non-aborted assistant message usage from session entries.
974
- *
975
- * @param entries - Array of session entries
976
- * @returns Usage metadata from the last assistant message, or null
977
- */
978
- export function getLastAssistantUsageFromEntries(entries) {
979
- for (let i = entries.length - 1; i >= 0; i--) {
980
- const entry = entries[i];
981
- if (!entry)
982
- continue;
983
- if (entry.type === "message") {
984
- const usage = getAssistantUsage(entry.message);
985
- if (usage)
986
- return usage;
987
- }
988
- }
989
- return null;
990
- }
991
- // ============================================================================
992
- // Compaction Trigger Detection
993
- // ============================================================================
994
- /**
995
- * Determine if compaction should trigger based on current context usage.
996
- *
997
- * Compaction triggers when context usage exceeds `contextWindow - reserveTokens`,
998
- * leaving enough room for summary generation plus a safety margin.
999
- *
1000
- * @param contextTokens - Current context window usage in tokens
1001
- * @param contextWindow - Model's total context window size
1002
- * @param settings - Compaction configuration
1003
- * @returns true if compaction should trigger
1004
- */
1005
- export function shouldCompact(contextTokens, contextWindow, settings) {
1006
- if (!settings.enabled)
1007
- return false;
1008
- return contextTokens > contextWindow - settings.reserveTokens;
1009
- }
1010
- /**
1011
- * Calculate context usage percentage.
1012
- *
1013
- * @param contextTokens - Current context window usage in tokens
1014
- * @param contextWindow - Model's total context window size
1015
- * @returns Percentage of context window used (0-100)
1016
- */
1017
- export function calculateUsagePercent(contextTokens, contextWindow) {
1018
- if (contextWindow <= 0)
1019
- return 0;
1020
- return (contextTokens / contextWindow) * 100;
1021
- }
1022
- /**
1023
- * Find indices of user messages (turn boundaries) in a message array.
1024
- *
1025
- * Turn boundaries are important because we never want to split a turn
1026
- * (user message + assistant response + tool results). Cutting at a user
1027
- * message ensures turn integrity.
1028
- *
1029
- * @param messages - Array of messages to scan
1030
- * @param startIndex - First index to consider (inclusive)
1031
- * @param endIndex - Last index to consider (exclusive)
1032
- * @returns Array of indices where user messages appear
1033
- */
1034
- function findTurnBoundaries(messages, startIndex, endIndex) {
1035
- const boundaries = [];
1036
- for (let i = startIndex; i < endIndex; i++) {
1037
- if (messages[i].role === "user") {
1038
- boundaries.push(i);
1039
- }
1040
- }
1041
- return boundaries;
1042
- }
1043
- /**
1044
- * Find the user message that starts the turn containing the given index.
1045
- *
1046
- * @param messages - Array of messages
1047
- * @param entryIndex - Index of the message to find the turn start for
1048
- * @param startIndex - First index to consider (inclusive)
1049
- * @returns Index of the user message that starts this turn, or -1 if not found
1050
- */
1051
- export function findTurnStartIndex(messages, entryIndex, startIndex) {
1052
- for (let i = entryIndex; i >= startIndex; i--) {
1053
- if (messages[i].role === "user") {
1054
- return i;
1055
- }
1056
- }
1057
- return -1;
1058
- }
1059
- /**
1060
- * Find the optimal cut point in messages that preserves `keepRecentTokens`.
1061
- *
1062
- * The algorithm:
1063
- * 1. Find all turn boundaries (user message indices)
1064
- * 2. Walk backwards collecting assistant token usage
1065
- * 3. Find where cumulative token difference exceeds keepRecentTokens
1066
- * 4. Return the nearest turn boundary at or before that point
1067
- *
1068
- * This ensures:
1069
- * - Recent context is preserved based on actual token usage, not message count
1070
- * - Turn integrity is maintained (user/assistant/toolResult groups stay together)
1071
- * - More context is kept when messages are small, less when large
1072
- *
1073
- * @param messages - Array of messages to analyze
1074
- * @param startIndex - First index to consider for cutting
1075
- * @param endIndex - Last index to consider (exclusive)
1076
- * @param keepRecentTokens - Approximate tokens to preserve
1077
- * @returns Index of first message to keep (messages before this are summarized)
1078
- */
1079
- export function findCutPoint(messages, startIndex, endIndex, keepRecentTokens) {
1080
- const boundaries = findTurnBoundaries(messages, startIndex, endIndex);
1081
- if (boundaries.length === 0) {
1082
- return startIndex; // No user messages, keep everything in range
1083
- }
1084
- // Collect assistant usages walking backwards from endIndex
1085
- const assistantUsages = [];
1086
- for (let i = endIndex - 1; i >= startIndex; i--) {
1087
- const usage = getAssistantUsage(messages[i]);
1088
- if (usage) {
1089
- assistantUsages.push({
1090
- index: i,
1091
- tokens: calculateContextTokens(usage),
1092
- });
1093
- }
1094
- }
1095
- if (assistantUsages.length === 0) {
1096
- // No usage info, keep last turn only
1097
- return boundaries[boundaries.length - 1];
1098
- }
1099
- // Walk through and find where cumulative token difference exceeds keepRecentTokens
1100
- const newestTokens = assistantUsages[0].tokens;
1101
- let cutIndex = startIndex; // Default: keep everything in range
1102
- for (let i = 1; i < assistantUsages.length; i++) {
1103
- const tokenDiff = newestTokens - assistantUsages[i].tokens;
1104
- if (tokenDiff >= keepRecentTokens) {
1105
- // Find the turn boundary at or before the assistant we want to keep
1106
- const lastKeptAssistantIndex = assistantUsages[i - 1].index;
1107
- for (let b = boundaries.length - 1; b >= 0; b--) {
1108
- const boundary = boundaries[b];
1109
- if (boundary !== undefined && boundary <= lastKeptAssistantIndex) {
1110
- cutIndex = boundary;
1111
- break;
1112
- }
1113
- }
1114
- break;
1115
- }
1116
- }
1117
- return cutIndex;
1118
- }
1119
- /**
1120
- * Find valid cut points (user or assistant messages, not tool results).
1121
- * Tool results must stay with their preceding tool call.
1122
- *
1123
- * @param messages - Array of messages to scan
1124
- * @param startIndex - First index to consider (inclusive)
1125
- * @param endIndex - Last index to consider (exclusive)
1126
- * @returns Array of indices where valid cut points exist
1127
- */
1128
- function findValidCutPoints(messages, startIndex, endIndex) {
1129
- const cutPoints = [];
1130
- for (let i = startIndex; i < endIndex; i++) {
1131
- const role = messages[i].role;
1132
- // user and assistant are valid cut points
1133
- // toolResult must stay with its preceding tool call
1134
- if (role === "user" || role === "assistant") {
1135
- cutPoints.push(i);
1136
- }
1137
- }
1138
- return cutPoints;
1139
- }
1140
- /**
1141
- * Find the optimal cut point with split turn information.
1142
- *
1143
- * Similar to findCutPoint but:
1144
- * - Can cut at assistant messages (not just user messages)
1145
- * - Returns information about whether we're splitting a turn
1146
- *
1147
- * This allows for more aggressive compaction when turns are very large,
1148
- * while providing the context needed to summarize the split turn prefix.
1149
- *
1150
- * @param messages - Array of messages to analyze
1151
- * @param startIndex - First index to consider for cutting
1152
- * @param endIndex - Last index to consider (exclusive)
1153
- * @param keepRecentTokens - Approximate tokens to preserve
1154
- * @returns CutPointResult with index and split turn information
1155
- */
1156
- export function findCutPointWithSplitInfo(messages, startIndex, endIndex, keepRecentTokens) {
1157
- const cutPoints = findValidCutPoints(messages, startIndex, endIndex);
1158
- if (cutPoints.length === 0) {
1159
- return {
1160
- firstKeptIndex: startIndex,
1161
- turnStartIndex: -1,
1162
- isSplitTurn: false,
1163
- };
1164
- }
1165
- // Estimate message sizes for token-based cutting
1166
- let accumulatedTokens = 0;
1167
- let cutIndex = startIndex;
1168
- for (let i = endIndex - 1; i >= startIndex; i--) {
1169
- const usage = getAssistantUsage(messages[i]);
1170
- if (usage) {
1171
- accumulatedTokens = calculateContextTokens(usage);
1172
- // Using cumulative tokens from the last assistant message
1173
- if (accumulatedTokens >= keepRecentTokens) {
1174
- // Find the closest valid cut point at or after this index
1175
- for (const cp of cutPoints) {
1176
- if (cp >= i) {
1177
- cutIndex = cp;
1178
- break;
1179
- }
1180
- }
1181
- break;
1182
- }
1183
- }
1184
- }
1185
- // If no cut point found via tokens, use last assistant's token-based cut
1186
- if (cutIndex === startIndex) {
1187
- // Fall back to original algorithm
1188
- cutIndex = findCutPoint(messages, startIndex, endIndex, keepRecentTokens);
1189
- }
1190
- // Determine if this is a split turn
1191
- const isUserMessage = messages[cutIndex]?.role === "user";
1192
- const turnStartIndex = isUserMessage
1193
- ? -1
1194
- : findTurnStartIndex(messages, cutIndex, startIndex);
1195
- return {
1196
- firstKeptIndex: cutIndex,
1197
- turnStartIndex,
1198
- isSplitTurn: !isUserMessage && turnStartIndex !== -1,
1199
- };
1200
- }
1201
- /**
1202
- * Adjust a cut boundary to preserve tool call/result integrity.
1203
- *
1204
- * Tool results must stay paired with their originating assistant message.
1205
- * If a toolResult would be kept but its toolCall would be cut, this
1206
- * moves the boundary back to include the toolCall.
1207
- *
1208
- * @param messages - Array of messages
1209
- * @param boundary - Initial cut point
1210
- * @returns Adjusted boundary that preserves tool integrity
1211
- */
1212
- export function adjustBoundaryForToolResults(messages, boundary) {
1213
- let adjusted = boundary;
1214
- const seenToolCalls = new Set();
1215
- const missingToolCalls = new Set();
1216
- // Process assistant message to collect tool calls
1217
- const processAssistantMessage = (message) => {
1218
- if (message.role !== "assistant")
1219
- return;
1220
- const content = message.content;
1221
- if (!content)
1222
- return;
1223
- for (const part of content) {
1224
- if (part?.type === "toolCall") {
1225
- seenToolCalls.add(part.id);
1226
- if (missingToolCalls.has(part.id)) {
1227
- missingToolCalls.delete(part.id);
1228
- }
1229
- }
1230
- }
1231
- };
1232
- // Process tool result to check for missing tool calls
1233
- const processToolResultMessage = (message) => {
1234
- if (message.role !== "toolResult")
1235
- return;
1236
- const toolCallId = message.toolCallId;
1237
- if (!seenToolCalls.has(toolCallId)) {
1238
- missingToolCalls.add(toolCallId);
1239
- }
1240
- };
1241
- // First pass: process messages we're keeping
1242
- for (const message of messages.slice(adjusted)) {
1243
- processAssistantMessage(message);
1244
- processToolResultMessage(message);
1245
- }
1246
- // Walk backwards to find missing tool calls
1247
- while (missingToolCalls.size > 0 && adjusted > 0) {
1248
- adjusted -= 1;
1249
- const candidate = messages[adjusted];
1250
- processAssistantMessage(candidate);
1251
- processToolResultMessage(candidate);
1252
- }
1253
- return adjusted;
1254
- }
1255
920
  // ============================================================================
1256
921
  // Summarization
1257
922
  // ============================================================================