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
15 changes: 12 additions & 3 deletions crates/tokscale-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4290,9 +4290,18 @@ mod tests {
None,
);

assert_eq!(messages.len(), 4);
assert_eq!(messages.iter().map(|m| m.tokens.input).sum::<i64>(), 150);
assert_eq!(messages.iter().map(|m| m.tokens.output).sum::<i64>(), 15);
// Parent contributes its two turns. The two forks each replay the
// parent history (skipped) and then emit one own turn that lands on
// the identical cumulative total (140/14). Sibling forks sharing a
// cumulative total is the signature of a replayed row, so the
// fork-parent-scoped dedup key collapses them into one. Real fork
// fan-out replays the same upstream totals into 10-100+ siblings;
// two distinct turns reaching a byte-identical cumulative vector by
// chance does not happen in practice because the cumulative encodes
// each fork's divergent context size.
assert_eq!(messages.len(), 3);
assert_eq!(messages.iter().map(|m| m.tokens.input).sum::<i64>(), 140);
assert_eq!(messages.iter().map(|m| m.tokens.output).sum::<i64>(), 14);
}

match original_home {
Expand Down
4 changes: 3 additions & 1 deletion crates/tokscale-core/src/message_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::time::UNIX_EPOCH;

const CACHE_SCHEMA_VERSION: u32 = 17;
// 18: codex token_count dedup key scoped to the fork parent. Cached
// messages store their dedup_key, so old entries must be reparsed.
const CACHE_SCHEMA_VERSION: u32 = 18;
const CACHE_FILENAME: &str = "source-message-cache.bin";
const CACHE_LOCK_FILENAME: &str = "source-message-cache.lock";
const MAX_CACHE_FILE_BYTES: u64 = 256 * 1024 * 1024;
Expand Down
15 changes: 14 additions & 1 deletion crates/tokscale-core/src/sessions/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,23 @@ fn parse_codex_reader<R: BufRead>(
state.pending_turn_start = false;
}
if parsed_timestamp.is_some() || total_usage.is_some() {
// Fork/subagent children replay the same upstream
// token_count history into many sibling files. Those
// replays carry identical cumulative totals but a
// distinct per-file session id, so a session-scoped key
// never collapses them and the totals get counted once
// per sibling. Scope the key to the fork parent instead
// so sibling replays share one key. Unrelated sessions
// keep their own id and never merge.
let dedup_scope_id = state
.session_forked_from_id
.as_deref()
.or(state.session_id_from_meta.as_deref())
.unwrap_or(session_id);
Comment thread
RedesignedRobot marked this conversation as resolved.
set_codex_dedup_key(
&mut message,
model.as_deref().unwrap_or("unknown"),
state.session_id_from_meta.as_deref().unwrap_or(session_id),
dedup_scope_id,
total_usage,
);
}
Expand Down