feat(kernel): emit ContextTruncatedEvent on workflow context overflow
DefaultContextPackBuilder already enforced the token budget, but SessionOrchestrator discarded the entriesDropped signal at both build sites — overflow was enforced yet unobserved. Emit ContextTruncatedEvent when entriesDropped>0, matching the router path (open-threads 6.2 contract).
This commit is contained in:
+25
@@ -23,6 +23,7 @@ import com.correx.core.context.model.EntryRole
|
||||
import com.correx.core.events.events.ApprovalDecisionResolvedEvent
|
||||
import com.correx.core.events.events.ApprovalRequestedEvent
|
||||
import com.correx.core.events.events.ArtifactCreatedEvent
|
||||
import com.correx.core.events.events.ContextTruncatedEvent
|
||||
import com.correx.core.events.events.ToolCallAssessedEvent
|
||||
import com.correx.core.events.events.ArtifactValidatedEvent
|
||||
import com.correx.core.events.events.ArtifactValidatingEvent
|
||||
@@ -267,6 +268,7 @@ abstract class SessionOrchestrator(
|
||||
entries = systemPrompt + schemaEntries + promptEntries + steeringEntries,
|
||||
budget = TokenBudget(limit = stageConfig.tokenBudget),
|
||||
)
|
||||
emitContextTruncationIfNeeded(sessionId, stageId, contextPack)
|
||||
|
||||
var currentContext = contextPack
|
||||
var inferenceResult = runInference(
|
||||
@@ -310,6 +312,7 @@ abstract class SessionOrchestrator(
|
||||
entries = allEntries,
|
||||
budget = TokenBudget(limit = stageConfig.tokenBudget),
|
||||
)
|
||||
emitContextTruncationIfNeeded(sessionId, stageId, currentContext)
|
||||
inferenceResult = runInference(
|
||||
sessionId, stageId, currentContext, stageConfig, config.stageTimeoutMs, responseFormat,
|
||||
)
|
||||
@@ -937,6 +940,28 @@ abstract class SessionOrchestrator(
|
||||
|
||||
// --- event emission ---
|
||||
|
||||
// Surface context-budget overflow as an event on the workflow path. The builder already
|
||||
// enforces the budget (drops oldest/compressible entries, pinning steering + event history);
|
||||
// this records that it happened so replay and operators can see it, mirroring the router path.
|
||||
private suspend fun emitContextTruncationIfNeeded(
|
||||
sessionId: SessionId,
|
||||
stageId: StageId,
|
||||
contextPack: ContextPack,
|
||||
) {
|
||||
val meta = contextPack.compressionMetadata
|
||||
if (meta.entriesDropped <= 0) return
|
||||
emit(
|
||||
sessionId,
|
||||
ContextTruncatedEvent(
|
||||
sessionId = sessionId,
|
||||
turnId = stageId.value,
|
||||
entriesDropped = meta.entriesDropped,
|
||||
truncatedLayers = meta.truncatedLayers.map { it.name },
|
||||
timestampMs = Clock.System.now().toEpochMilliseconds(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internal suspend fun emit(sessionId: SessionId, payload: EventPayload) {
|
||||
log.debug("[session {}] emitting event, payload: {}", sessionId.value.take(7), payload)
|
||||
eventStore.append(
|
||||
|
||||
Reference in New Issue
Block a user