Skip to content
Closed
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
5 changes: 4 additions & 1 deletion src/cortex-compact/src/compactor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ impl Compactor {
}];
new_items.extend(items.into_iter().skip(preserved_start));

let tokens_after = current_tokens - tokens_in_compacted + summary_tokens;
// Use saturating arithmetic to prevent underflow if tokens_in_compacted > current_tokens
let tokens_after = current_tokens
.saturating_sub(tokens_in_compacted)
.saturating_add(summary_tokens);

let result =
CompactionResult::success(summary, current_tokens, tokens_after, items_removed);
Expand Down
16 changes: 12 additions & 4 deletions src/cortex-engine/src/git_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,17 @@ impl GitInfo {
let status = git_command(&root, &["status", "--porcelain"]).unwrap_or_default();
let is_dirty = !status.is_empty();

// Count changes
let changes = status.lines().filter(|l| !l.starts_with("??")).count() as u32;
let untracked = status.lines().filter(|l| l.starts_with("??")).count() as u32;
// Count changes (saturating to u32::MAX to prevent truncation)
let changes = status
.lines()
.filter(|l| !l.starts_with("??"))
.count()
.min(u32::MAX as usize) as u32;
let untracked = status
.lines()
.filter(|l| l.starts_with("??"))
.count()
.min(u32::MAX as usize) as u32;

// Get tags
let tags = git_command(&root, &["tag", "--points-at", "HEAD"])
Expand Down Expand Up @@ -385,7 +393,7 @@ impl GitStash {
let message = parts.get(2).unwrap_or(&"").to_string();

stashes.push(GitStash {
index: i as u32,
index: i.min(u32::MAX as usize) as u32,
message,
branch,
});
Expand Down
19 changes: 16 additions & 3 deletions src/cortex-engine/src/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,25 @@ pub struct StreamTokenUsage {
pub total_tokens: u32,
}

/// Safely convert an i64 token count to u32 with saturation.
/// Negative values clamp to 0, values > u32::MAX clamp to u32::MAX.
#[inline]
fn saturating_i64_to_u32(value: i64) -> u32 {
if value <= 0 {
0
} else if value > u32::MAX as i64 {
u32::MAX
} else {
value as u32
}
}

impl From<crate::client::TokenUsage> for StreamTokenUsage {
fn from(usage: crate::client::TokenUsage) -> Self {
Self {
prompt_tokens: usage.input_tokens as u32,
completion_tokens: usage.output_tokens as u32,
total_tokens: usage.total_tokens as u32,
prompt_tokens: saturating_i64_to_u32(usage.input_tokens),
completion_tokens: saturating_i64_to_u32(usage.output_tokens),
total_tokens: saturating_i64_to_u32(usage.total_tokens),
}
}
}
Expand Down