( messages: Message[], querySource: QuerySource | undefined, )
| 420 | * the same predicate without coupling to the tool-result clearing action. |
| 421 | */ |
| 422 | export function evaluateTimeBasedTrigger( |
| 423 | messages: Message[], |
| 424 | querySource: QuerySource | undefined, |
| 425 | ): { gapMinutes: number; config: TimeBasedMCConfig } | null { |
| 426 | const config = getTimeBasedMCConfig() |
| 427 | // Require an explicit main-thread querySource. isMainThreadSource treats |
| 428 | // undefined as main-thread (for cached-MC backward-compat), but several |
| 429 | // callers (/context, /compact, analyzeContext) invoke microcompactMessages |
| 430 | // without a source for analysis-only purposes — they should not trigger. |
| 431 | if (!config.enabled || !querySource || !isMainThreadSource(querySource)) { |
| 432 | return null |
| 433 | } |
| 434 | const lastAssistant = messages.findLast(m => m.type === 'assistant') |
| 435 | if (!lastAssistant) { |
| 436 | return null |
| 437 | } |
| 438 | const gapMinutes = |
| 439 | (Date.now() - new Date(lastAssistant.timestamp).getTime()) / 60_000 |
| 440 | if (!Number.isFinite(gapMinutes) || gapMinutes < config.gapThresholdMinutes) { |
| 441 | return null |
| 442 | } |
| 443 | return { gapMinutes, config } |
| 444 | } |
| 445 | |
| 446 | function maybeTimeBasedMicrocompact( |
| 447 | messages: Message[], |
no test coverage detected