Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/pr-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Run policy tests
run: bun run check:policy

Expand Down
5 changes: 3 additions & 2 deletions src/constants/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ export function getCLISyspromptPrefix(options?: {

/**
* Check if attribution header is enabled.
* Enabled by default, can be disabled via env var or GrowthBook killswitch.
* Disabled by default to preserve prompt cache stability.
* Can be enabled via env var CLAUDE_CODE_ATTRIBUTION_HEADER or GrowthBook feature flag.
*/
function isAttributionHeaderEnabled(): boolean {
if (isEnvDefinedFalsy(process.env.CLAUDE_CODE_ATTRIBUTION_HEADER)) {
return false
}
return getFeatureValue_CACHED_MAY_BE_STALE('tengu_attribution_header', true)
return getFeatureValue_CACHED_MAY_BE_STALE('tengu_attribution_header', false)
}

/**
Expand Down
59 changes: 58 additions & 1 deletion src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import type { CanUseToolFn } from './hooks/useCanUseTool.js'
import { FallbackTriggeredError } from './services/api/withRetry.js'
import {
calculateTokenWarningState,
COMPACT_PRECHECK_FOLD_RATIO,
estimateTurnStartUsage,
getEffectiveContextWindowSize,
isAutoCompactEnabled,
shouldPreFold,
type AutoCompactTrackingState,
type CacheMetrics,
} from './services/compact/autoCompact.js'
import { buildPostCompactMessages } from './services/compact/compact.js'
/* eslint-disable @typescript-eslint/no-require-imports */
Expand Down Expand Up @@ -452,7 +457,47 @@ async function* queryLoop(
)

queryCheckpoint('query_autocompact_start')
const { compactionResult, consecutiveFailures } = await deps.autocompact(

// Turn-start pre-estimation: check whether accumulated context from the
// last turn has pushed us into dangerous territory BEFORE the next API
// call. When the 90% threshold is crossed and we haven't already folded
// this turn, force a pre-fold via the existing autocompact pipeline.
let forcePreFold = false
if (feature('TURN_START_PRE_ESTIMATION')) {
const effectiveWindow = getEffectiveContextWindowSize(
toolUseContext.options.mainLoopModel,
)
const { ratio, estimateTokens } = estimateTurnStartUsage(
messagesForQuery,
effectiveWindow,
)
if (
shouldPreFold(tracking, estimateTokens, effectiveWindow)
) {
forcePreFold = true
logForDebugging(
`turnStartPreEstimate: context at ${(ratio * 100).toFixed(1)}% ` +
`(~${estimateTokens.toLocaleString()} tokens) — forcing pre-fold before API call`,
{ level: 'warn' },
)
logEvent('tengu_turn_start_prefold_triggered', {
estimatedTokens: estimateTokens,
ratio: Math.round(ratio * 100),
})
} else if (ratio >= COMPACT_PRECHECK_FOLD_RATIO) {
// Above threshold but suppressed by alreadyFoldedThisTurn
logForDebugging(
`turnStartPreEstimate: context at ${(ratio * 100).toFixed(1)}% ` +
`but pre-fold suppressed (already folded this turn)`,
)
}
}

const {
compactionResult,
consecutiveFailures,
cacheMetrics,
} = await deps.autocompact(
messagesForQuery,
toolUseContext,
{
Expand All @@ -465,9 +510,20 @@ async function* queryLoop(
querySource,
tracking,
snipTokensFreed,
forcePreFold,
)
queryCheckpoint('query_autocompact_end')

if (cacheMetrics) {
logForDebugging(
`cacheMetrics.compaction: hit=${cacheMetrics.cacheHitTokens.toLocaleString()} ` +
`miss=${cacheMetrics.cacheMissTokens.toLocaleString()} ` +
`ratio=${(cacheMetrics.cacheHitRatio * 100).toFixed(1)}%`,
)
}

queryCheckpoint('query_autocompact_end')

if (compactionResult) {
const {
preCompactTokenCount,
Expand All @@ -491,6 +547,7 @@ async function* queryLoop(
compactionUsage?.cache_read_input_tokens ?? 0,
compactionCacheCreationTokens:
compactionUsage?.cache_creation_input_tokens ?? 0,
compactionCacheHitRatio: cacheMetrics?.cacheHitRatio ?? 0,
compactionTotalTokens: compactionUsage
? compactionUsage.input_tokens +
(compactionUsage.cache_creation_input_tokens ?? 0) +
Expand Down
Loading
Loading