M0: compilable skeleton — Kigi 0.1.0 fork surgery

Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.

Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
  kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
  ptyctl, ptyctl-cli, third_party/ unchanged; proto package
  xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
  KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
  (templates re-encrypted)

Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
  trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
  module & dc_log, heap-profile uploader, auth-diagnostics uploader,
  session-analytics halves of feedback; local zero-egress observability
  preserved in new kigi-log crate (unified log, --debug firehose,
  subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
  direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
  relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
  ~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
  kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
  session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
  shell util

Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
  https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
  https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
  Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted

Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
  workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
  all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
  exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
  insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean

Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
  (new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
  fast-worktree); RSS measurement tests serialized via serial_test

Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
  notices sustained; kigi-tools ported-code notices extended; README,
  CONTRIBUTING, SECURITY, AGENTS.md rewritten

Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
This commit is contained in:
ZacharyZhang-NY committed 2026-07-17 05:31:01 -04:00
commit d6c20fc13f
2612 files changed
+1353757

No files matched your search

@@ -0,0 +1,291 @@
use std::collections::HashSet;
use std::path::Path;
use std::time::{Duration, SystemTime};
use rusqlite::{Connection, params};
use super::super::{
ApprovedRoot, ForeignSessionSummary, ForeignSessionTool, MAX_SESSION_AGE,
MAX_SESSIONS_PER_TOOL, RecentCandidate, is_within, millis_bounds, normalize_title,
open_sqlite_transaction, system_time_from_millis,
};
use super::{existing_rollout_path, source_from_persisted, title};
const MAX_DB_CANDIDATES: usize = 200;
pub(super) const MAX_RECENT_DB_CANDIDATES: usize = 8;
const MIN_EPOCH_MILLIS: i64 = 1_577_836_800_000;
const MAX_ID_BYTES: usize = 64;
const MAX_PATH_BYTES: usize = 16 * 1024;
const MAX_TEXT_BYTES: usize = 64 * 1024;
const MAX_BRANCH_BYTES: usize = 4 * 1024;
struct DbCandidate {
id: String,
rollout_path: String,
updated_at: i64,
source: String,
stored_cwd: String,
title: String,
first_user_message: String,
branch: Option<String>,
}
pub(super) enum RecentDatabaseResult {
Unusable,
Incomplete,
Usable(Option<RecentCandidate>),
}
pub(super) fn scan_database(
root: &ApprovedRoot,
db_path: &Path,
cwd: &Path,
now: SystemTime,
) -> Option<Vec<ForeignSessionSummary>> {
let bounds = millis_bounds(now, MAX_SESSION_AGE)?;
let cwd_string = cwd.to_str()?.to_owned();
if cwd_string.len() > MAX_PATH_BYTES {
return None;
}
let database = open_sqlite_transaction(root, db_path)?;
let columns = table_columns(&database, "threads")?;
let sql = scan_sql(&columns)?;
let rows = query_candidates(&database, &sql, &cwd_string, bounds)?;
let mut sessions = Vec::new();
for row in rows {
let Some(candidate) =
qualify_candidate(root, cwd, now, MAX_SESSION_AGE, &row, source_from_persisted)
else {
continue;
};
let Some(title) = title(&row.title, &row.first_user_message) else {
continue;
};
sessions.push(ForeignSessionSummary {
tool: ForeignSessionTool::Codex,
source: candidate.source,
native_id: candidate.native_id,
title,
cwd: Path::new(&row.stored_cwd).to_path_buf(),
updated_at: candidate.updated_at,
branch: row.branch.as_deref().and_then(normalize_title),
});
if sessions.len() == MAX_SESSIONS_PER_TOOL {
break;
}
}
Some(sessions)
}
pub(super) fn most_recent_database(
root: &ApprovedRoot,
db_path: &Path,
cwd: &Path,
now: SystemTime,
within: Duration,
) -> RecentDatabaseResult {
let Some(bounds) = millis_bounds(now, within) else {
return RecentDatabaseResult::Unusable;
};
let Some(cwd_string) = cwd.to_str().map(str::to_owned) else {
return RecentDatabaseResult::Unusable;
};
if cwd_string.len() > MAX_PATH_BYTES {
return RecentDatabaseResult::Unusable;
}
let Some(database) = open_sqlite_transaction(root, db_path) else {
return RecentDatabaseResult::Unusable;
};
let Some(columns) = table_columns(&database, "threads") else {
return RecentDatabaseResult::Unusable;
};
let Some(sql) = recent_scan_sql(&columns) else {
return RecentDatabaseResult::Unusable;
};
let Ok(mut rows) = query_recent_candidates(&database, &sql, &cwd_string, bounds) else {
return RecentDatabaseResult::Incomplete;
};
let truncated = rows.len() > MAX_RECENT_DB_CANDIDATES;
rows.truncate(MAX_RECENT_DB_CANDIDATES);
let candidate = rows
.into_iter()
.find_map(|row| qualify_candidate(root, cwd, now, within, &row, super::source_from_str));
if candidate.is_none() && truncated {
RecentDatabaseResult::Incomplete
} else {
RecentDatabaseResult::Usable(candidate)
}
}
fn query_candidates(
database: &Connection,
sql: &str,
cwd: &str,
(oldest_millis, newest_millis): (i64, i64),
) -> Option<Vec<DbCandidate>> {
let mut statement = database.prepare(sql).ok()?;
let rows = statement
.query_map(
params![cwd, oldest_millis, newest_millis],
decode_candidate_row,
)
.ok()?;
Some(rows.flatten().collect())
}
fn query_recent_candidates(
database: &Connection,
sql: &str,
cwd: &str,
(oldest_millis, newest_millis): (i64, i64),
) -> Result<Vec<DbCandidate>, ()> {
let mut statement = database.prepare(sql).map_err(|_| ())?;
let rows = statement
.query_map(
params![cwd, oldest_millis, newest_millis],
decode_candidate_row,
)
.map_err(|_| ())?;
rows.collect::<rusqlite::Result<Vec<_>>>().map_err(|_| ())
}
fn decode_candidate_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<DbCandidate> {
Ok(DbCandidate {
id: row.get(0)?,
rollout_path: row.get(1)?,
updated_at: row.get(2)?,
source: row.get(3)?,
stored_cwd: row.get(4)?,
title: row.get(5)?,
first_user_message: row.get(6)?,
branch: row.get(7)?,
})
}
fn qualify_candidate(
root: &ApprovedRoot,
cwd: &Path,
now: SystemTime,
within: Duration,
row: &DbCandidate,
parse_source: impl Fn(&str) -> Option<super::super::ForeignSessionSource>,
) -> Option<RecentCandidate> {
if uuid::Uuid::try_parse(&row.id).is_err() || Path::new(&row.stored_cwd) != cwd {
return None;
}
let source = parse_source(&row.source)?;
let updated_at = normalize_updated_at(row.updated_at)?;
if !is_within(updated_at, now, within)
|| existing_rollout_path(root, &row.rollout_path, &row.id).is_none()
{
return None;
}
Some(RecentCandidate {
tool: ForeignSessionTool::Codex,
source,
native_id: row.id.clone(),
updated_at,
})
}
pub(super) fn normalize_updated_at(value: i64) -> Option<SystemTime> {
let millis = if value < MIN_EPOCH_MILLIS {
value.saturating_mul(1_000)
} else {
value
};
system_time_from_millis(millis)
}
pub(super) fn scan_sql(columns: &HashSet<String>) -> Option<String> {
scan_sql_with_limit(
columns,
MAX_DB_CANDIDATES,
"('cli', 'vscode', '{\"custom\":\"atlas\"}', '{\"custom\":\"chatgpt\"}')",
)
}
pub(super) fn recent_scan_sql(columns: &HashSet<String>) -> Option<String> {
scan_sql_with_limit(columns, MAX_RECENT_DB_CANDIDATES + 1, "('cli', 'vscode')")
}
fn scan_sql_with_limit(
columns: &HashSet<String>,
limit: usize,
allowed_sources: &str,
) -> Option<String> {
for required in ["id", "rollout_path", "source", "cwd", "archived"] {
if !columns.contains(required) {
return None;
}
}
let updated_column = if columns.contains("updated_at_ms") {
"updated_at_ms"
} else if columns.contains("updated_at") {
"updated_at"
} else {
return None;
};
let title_column = if columns.contains("title") {
"title"
} else {
"''"
};
let first_user_message = if columns.contains("first_user_message") {
"first_user_message"
} else {
"''"
};
let git_branch = if columns.contains("git_branch") {
"git_branch"
} else {
"NULL"
};
let title_projection = format!(
"CASE WHEN typeof({title_column}) = 'text' \
AND octet_length({title_column}) <= {MAX_TEXT_BYTES} \
THEN {title_column} ELSE '' END"
);
let first_projection = format!(
"CASE WHEN typeof({first_user_message}) = 'text' \
AND octet_length({first_user_message}) <= {MAX_TEXT_BYTES} \
THEN {first_user_message} ELSE '' END"
);
let branch_projection = format!(
"CASE WHEN typeof({git_branch}) = 'text' \
AND octet_length({git_branch}) <= {MAX_BRANCH_BYTES} \
THEN {git_branch} ELSE NULL END"
);
Some(format!(
"SELECT id, rollout_path, {updated_column}, source, cwd, \
{title_projection}, {first_projection}, {branch_projection} \
FROM threads \
WHERE typeof(id) = 'text' \
AND typeof(rollout_path) = 'text' \
AND typeof({updated_column}) = 'integer' \
AND typeof(archived) = 'integer' \
AND archived = 0 AND cwd = ?1 \
AND source IN {allowed_sources} \
AND octet_length(id) <= {MAX_ID_BYTES} \
AND octet_length(rollout_path) <= {MAX_PATH_BYTES} \
AND CASE \
WHEN {updated_column} < {MIN_EPOCH_MILLIS} THEN {updated_column} * 1000 \
ELSE {updated_column} \
END BETWEEN ?2 AND ?3 \
ORDER BY CASE \
WHEN {updated_column} < {MIN_EPOCH_MILLIS} THEN {updated_column} * 1000 \
ELSE {updated_column} \
END DESC, id ASC LIMIT {limit}"
))
}
fn table_columns(connection: &Connection, table: &str) -> Option<HashSet<String>> {
let mut statement = connection
.prepare(&format!("PRAGMA table_info({table})"))
.ok()?;
let rows = statement
.query_map([], |row| row.get::<_, String>(1))
.ok()?;
let columns = rows.flatten().collect::<HashSet<_>>();
(!columns.is_empty()).then_some(columns)
}
@@ -0,0 +1,359 @@
use std::collections::HashSet;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use chrono::{DateTime, Datelike, Days, Local, TimeDelta, Utc};
use serde_json::Value;
use super::super::{
ApprovedRoot, ForeignSessionSource, ForeignSessionSummary, ForeignSessionTool, MAX_SESSION_AGE,
MAX_SESSIONS_PER_TOOL, RecentCandidate, RecentProbe, is_within, normalize_title,
retain_top_k_by,
};
use super::{rollout_id, source_from_value, title};
const DAYS_IN_WINDOW: usize = 31;
const MAX_DATE_DIRS: usize = 32;
const MAX_METADATA_READS: usize = 128;
const MAX_RECENT_METADATA_READS: usize = 16;
pub(super) const MAX_RECENT_DIRECTORY_ENTRIES: usize = 64;
const MAX_HEAD_RECORDS: usize = 10;
pub(super) const MAX_HEAD_BYTES: usize = 64 * 1024;
// Bound compressed work separately from the decoded head and decoder window.
pub(super) const MAX_COMPRESSED_HEAD_BYTES: usize = 256 * 1024;
pub(super) const MAX_ZSTD_WINDOW_LOG: u32 = 23;
struct Candidate {
root: ApprovedRoot,
path: PathBuf,
id: String,
modified: SystemTime,
}
#[derive(Default)]
struct HeadMetadata {
id: Option<uuid::Uuid>,
cwd: Option<String>,
source: Option<ForeignSessionSource>,
branch: Option<String>,
first_user_message: Option<String>,
}
pub(super) fn scan_rollouts(
codex_root: &ApprovedRoot,
cwd: &Path,
now: SystemTime,
) -> Vec<ForeignSessionSummary> {
let Some(root) = codex_root.subroot(Path::new("sessions")) else {
return Vec::new();
};
let candidates = collect_candidates(&root, now, MAX_SESSION_AGE, MAX_METADATA_READS);
let mut accepted_ids = HashSet::new();
let mut sessions = Vec::new();
for candidate in candidates {
if accepted_ids.contains(&candidate.id) {
continue;
}
let Some(session) = read_candidate(candidate, cwd) else {
continue;
};
accepted_ids.insert(session.native_id.clone());
sessions.push(session);
if sessions.len() == MAX_SESSIONS_PER_TOOL {
break;
}
}
sessions
}
pub(super) fn most_recent_rollout(
codex_root: &ApprovedRoot,
cwd: &Path,
now: SystemTime,
within: Duration,
) -> RecentProbe<RecentCandidate> {
let sessions_path = codex_root.join("sessions");
match std::fs::symlink_metadata(&sessions_path) {
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return RecentProbe::Complete(None);
}
Err(_) => return RecentProbe::Incomplete,
Ok(_) => {}
}
let Some(root) = codex_root.subroot(&sessions_path) else {
return RecentProbe::Incomplete;
};
let Some((candidates, truncated)) = collect_recent_candidates(&root, now, within) else {
return RecentProbe::Incomplete;
};
let candidate = candidates.into_iter().find_map(|candidate| {
let (_, source, _) = qualify_candidate(&candidate, cwd)?;
if !matches!(
source,
ForeignSessionSource::CodexCli | ForeignSessionSource::CodexVsCode
) {
return None;
}
Some(RecentCandidate {
tool: ForeignSessionTool::Codex,
source,
native_id: candidate.id,
updated_at: candidate.modified,
})
});
if candidate.is_none() && truncated {
RecentProbe::Incomplete
} else {
RecentProbe::Complete(candidate)
}
}
fn collect_recent_candidates(
root: &ApprovedRoot,
now: SystemTime,
within: Duration,
) -> Option<(Vec<Candidate>, bool)> {
let mut candidates = Vec::with_capacity(MAX_RECENT_METADATA_READS);
let mut remaining_entries = MAX_RECENT_DIRECTORY_ENTRIES;
let mut qualifying = 0;
let local_offset = DateTime::<Local>::from(now).offset().local_minus_utc();
for date_dir in recent_date_dirs(root.path(), now, local_offset) {
let Some(date_root) = root.subroot(&date_dir) else {
continue;
};
let outcome = date_root.for_each_entry_bounded(remaining_entries, |name| {
let path = date_root.join(&name);
let Some(id) = rollout_id(&path) else {
return;
};
let Some((path, metadata)) = date_root.resolve_regular_file(&path) else {
return;
};
let Ok(modified) = metadata.modified() else {
return;
};
if is_within(modified, now, within) {
qualifying += 1;
retain_top_k_by(
&mut candidates,
Candidate {
root: date_root.clone(),
path,
id,
modified,
},
MAX_RECENT_METADATA_READS,
candidate_order,
);
}
});
remaining_entries = remaining_entries.saturating_sub(outcome.visited);
if !outcome.complete {
return None;
}
}
Some((candidates, qualifying > MAX_RECENT_METADATA_READS))
}
fn collect_candidates(
root: &ApprovedRoot,
now: SystemTime,
within: Duration,
limit: usize,
) -> Vec<Candidate> {
let mut candidates = Vec::with_capacity(limit);
let local_offset = DateTime::<Local>::from(now).offset().local_minus_utc();
for date_dir in recent_date_dirs(root.path(), now, local_offset) {
let Some(date_root) = root.subroot(&date_dir) else {
continue;
};
let mut date_candidates = Vec::with_capacity(limit);
// Enumerate every direct rollout entry in the fixed date window so
// filesystem order cannot decide which files receive the head-read budget.
let complete = date_root.for_each_entry(|name| {
let path = date_root.join(&name);
let Some(id) = rollout_id(&path) else {
return;
};
let Some((path, metadata)) = date_root.resolve_regular_file(&path) else {
return;
};
let Ok(modified) = metadata.modified() else {
return;
};
if is_within(modified, now, within) {
retain_top_k_by(
&mut date_candidates,
Candidate {
root: date_root.clone(),
path,
id,
modified,
},
limit,
candidate_order,
);
}
});
if !complete {
continue;
}
for candidate in date_candidates {
retain_top_k_by(&mut candidates, candidate, limit, candidate_order);
}
}
candidates
}
fn candidate_order(a: &Candidate, b: &Candidate) -> std::cmp::Ordering {
b.modified
.cmp(&a.modified)
.then_with(|| a.id.cmp(&b.id))
.then_with(|| a.path.cmp(&b.path))
}
pub(super) fn recent_date_dirs(
root: &Path,
now: SystemTime,
local_offset_seconds: i32,
) -> Vec<PathBuf> {
let utc = DateTime::<Utc>::from(now);
let local = utc + TimeDelta::seconds(i64::from(local_offset_seconds));
let mut dates = [utc.date_naive(), local.date_naive()]
.into_iter()
.flat_map(|today| {
(0..DAYS_IN_WINDOW)
.filter_map(move |days| today.checked_sub_days(Days::new(days as u64)))
})
.collect::<HashSet<_>>()
.into_iter()
.collect::<Vec<_>>();
dates.sort_by(|a, b| b.cmp(a));
dates.truncate(MAX_DATE_DIRS);
dates
.into_iter()
.map(|date| {
root.join(format!("{:04}", date.year()))
.join(format!("{:02}", date.month()))
.join(format!("{:02}", date.day()))
})
.collect()
}
fn read_candidate(candidate: Candidate, requested_cwd: &Path) -> Option<ForeignSessionSummary> {
let (
HeadMetadata {
branch,
first_user_message,
..
},
source,
stored_cwd,
) = qualify_candidate(&candidate, requested_cwd)?;
let title = title("", first_user_message.as_deref().unwrap_or(""))?;
Some(ForeignSessionSummary {
tool: ForeignSessionTool::Codex,
source,
native_id: candidate.id,
title,
cwd: PathBuf::from(stored_cwd),
updated_at: candidate.modified,
branch,
})
}
fn qualify_candidate(
candidate: &Candidate,
requested_cwd: &Path,
) -> Option<(HeadMetadata, ForeignSessionSource, String)> {
let metadata = read_head(&candidate.root, &candidate.path)?;
if metadata.id? != uuid::Uuid::try_parse(&candidate.id).ok()? {
return None;
}
let stored_cwd = metadata.cwd.clone()?;
if Path::new(&stored_cwd) != requested_cwd {
return None;
}
let source = metadata.source?;
Some((metadata, source, stored_cwd))
}
fn read_head(root: &ApprovedRoot, path: &Path) -> Option<HeadMetadata> {
let file = root.open_regular_file(path)?.file;
if path.extension().and_then(|extension| extension.to_str()) == Some("zst") {
let compressed = file.take(MAX_COMPRESSED_HEAD_BYTES as u64);
let mut decoder = zstd::Decoder::new(compressed).ok()?;
decoder.window_log_max(MAX_ZSTD_WINDOW_LOG).ok()?;
let decoder = decoder.single_frame();
parse_head(BufReader::new(decoder.take(MAX_HEAD_BYTES as u64)))
} else {
parse_head(BufReader::new(file.take(MAX_HEAD_BYTES as u64)))
}
}
fn parse_head(mut reader: impl BufRead) -> Option<HeadMetadata> {
let mut line = String::new();
let mut total = 0;
let mut metadata = HeadMetadata::default();
let mut saw_session_meta = false;
for _ in 0..MAX_HEAD_RECORDS {
line.clear();
let read = reader.read_line(&mut line).ok()?;
if read == 0 {
break;
}
total += read;
if total > MAX_HEAD_BYTES {
break;
}
let Ok(record) = serde_json::from_str::<Value>(&line) else {
continue;
};
let record_type = record.get("type").and_then(Value::as_str);
let payload = record.get("payload").unwrap_or(&Value::Null);
if record_type == Some("session_meta") && !saw_session_meta {
saw_session_meta = true;
metadata.id = payload
.get("id")
.and_then(Value::as_str)
.and_then(|id| uuid::Uuid::try_parse(id).ok());
metadata.cwd = payload
.get("cwd")
.and_then(Value::as_str)
.map(str::to_owned);
metadata.source = payload.get("source").and_then(source_from_value);
metadata.branch = payload
.pointer("/git/branch")
.or_else(|| payload.get("git_branch"))
.and_then(Value::as_str)
.and_then(normalize_title);
}
if metadata.first_user_message.is_none() {
metadata.first_user_message = user_message(payload).and_then(normalize_title);
}
}
Some(metadata)
}
fn user_message(payload: &Value) -> Option<&str> {
if payload.get("type").and_then(Value::as_str) == Some("user_message") {
return payload.get("message").and_then(Value::as_str);
}
if payload.get("type").and_then(Value::as_str) != Some("message")
|| payload.get("role").and_then(Value::as_str) != Some("user")
{
return None;
}
let text = payload
.get("content")?
.as_array()?
.iter()
.find_map(|item| match item.get("type").and_then(Value::as_str) {
Some("input_text" | "text") => item.get("text").and_then(Value::as_str),
_ => None,
})?;
let trimmed = text.trim_start();
(!trimmed.starts_with("<environment_context>") && !trimmed.starts_with("<user_instructions>"))
.then_some(text)
}
@@ -0,0 +1,194 @@
use std::path::{Component, Path, PathBuf};
use std::time::{Duration, SystemTime};
use serde_json::Value;
mod db;
mod files;
use super::{
ApprovedRoot, ForeignSessionSource, ForeignSessionSummary, RecentCandidate, RecentProbe,
approved_root_for_recent, finish_tool_scan, normalize_title,
};
// Codex is currently on single-digit state generations. Probe a generous,
// deterministic supported range without enumerating unrelated CODEX_HOME files.
const MAX_STATE_DB_GENERATION: u32 = 128;
pub(super) fn scan(cwd: &Path, now: SystemTime) -> Vec<ForeignSessionSummary> {
let Some(codex_home) = std::env::var_os("CODEX_HOME")
.map(PathBuf::from)
.or_else(|| dirs::home_dir().map(|home| home.join(".codex")))
else {
return Vec::new();
};
scan_in_home(&codex_home, cwd, now)
}
pub(super) fn most_recent(
cwd: &Path,
now: SystemTime,
within: Duration,
) -> RecentProbe<RecentCandidate> {
let Some(codex_home) = std::env::var_os("CODEX_HOME")
.map(PathBuf::from)
.or_else(|| dirs::home_dir().map(|home| home.join(".codex")))
else {
return RecentProbe::Complete(None);
};
most_recent_in_home(&codex_home, cwd, now, within)
}
fn scan_in_home(codex_home: &Path, cwd: &Path, now: SystemTime) -> Vec<ForeignSessionSummary> {
let Some(root) = ApprovedRoot::new(codex_home) else {
return Vec::new();
};
let sessions = state_databases(&root)
.find_map(|path| {
db::scan_database(&root, &path, cwd, now).filter(|sessions| !sessions.is_empty())
})
.unwrap_or_else(|| files::scan_rollouts(&root, cwd, now));
finish_tool_scan(sessions)
}
fn most_recent_in_home(
codex_home: &Path,
cwd: &Path,
now: SystemTime,
within: Duration,
) -> RecentProbe<RecentCandidate> {
let root = match approved_root_for_recent(codex_home) {
Ok(Some(root)) => root,
Ok(None) => return RecentProbe::Complete(None),
Err(()) => return RecentProbe::Incomplete,
};
let Some(path) = highest_named_state_database(&root) else {
return files::most_recent_rollout(&root, cwd, now, within);
};
match db::most_recent_database(&root, &path, cwd, now, within) {
db::RecentDatabaseResult::Usable(candidate) => RecentProbe::Complete(candidate),
db::RecentDatabaseResult::Incomplete => RecentProbe::Incomplete,
db::RecentDatabaseResult::Unusable => files::most_recent_rollout(&root, cwd, now, within),
}
}
fn highest_named_state_database(root: &ApprovedRoot) -> Option<PathBuf> {
(0..=MAX_STATE_DB_GENERATION).rev().find_map(|generation| {
let path = root.join(format!("state_{generation}.sqlite"));
match std::fs::symlink_metadata(&path) {
Ok(_) => Some(path),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
Err(_) => Some(path),
}
})
}
fn state_databases(root: &ApprovedRoot) -> impl Iterator<Item = PathBuf> + '_ {
(0..=MAX_STATE_DB_GENERATION)
.rev()
.filter_map(move |generation| {
let path = root.join(format!("state_{generation}.sqlite"));
root.resolve_regular_file(&path).map(|(path, _)| path)
})
}
fn source_from_str(source: &str) -> Option<ForeignSessionSource> {
match source {
"cli" => Some(ForeignSessionSource::CodexCli),
"vscode" => Some(ForeignSessionSource::CodexVsCode),
_ => None,
}
}
fn source_from_persisted(source: &str) -> Option<ForeignSessionSource> {
source_from_str(source).or_else(|| {
let value = serde_json::from_str::<Value>(source).ok()?;
source_from_value(&value)
})
}
fn source_from_value(source: &Value) -> Option<ForeignSessionSource> {
source
.as_str()
.and_then(source_from_str)
.or_else(|| match source.get("custom")?.as_str()? {
"atlas" => Some(ForeignSessionSource::CodexAtlas),
"chatgpt" => Some(ForeignSessionSource::CodexChatGpt),
_ => None,
})
}
fn existing_rollout_path(root: &ApprovedRoot, value: &str, expected_id: &str) -> Option<PathBuf> {
let path = PathBuf::from(value);
if path.components().any(|part| part == Component::ParentDir) {
return None;
}
let path = if path.is_absolute() {
path
} else {
root.join(path)
};
let mut compressed = path.clone().into_os_string();
compressed.push(".zst");
let approved_prefixes = ["sessions", "archived_sessions"]
.into_iter()
.filter_map(|name| dunce::canonicalize(root.join(name)).ok())
.filter(|path| path.starts_with(root.path()))
.collect::<Vec<_>>();
[path, PathBuf::from(compressed)]
.into_iter()
.filter_map(|candidate| root.open_regular_file(&candidate).map(|opened| opened.path))
.find(|candidate| {
rollout_id(candidate).as_deref() == Some(expected_id)
&& approved_prefixes
.iter()
.any(|prefix| candidate.starts_with(prefix))
})
}
fn rollout_id(path: &Path) -> Option<String> {
let name = path.file_name()?.to_str()?;
let stem = name
.strip_suffix(".jsonl.zst")
.or_else(|| name.strip_suffix(".jsonl"))?;
let value = stem.strip_prefix("rollout-")?;
let id_start = value.len().checked_sub(36)?;
if id_start == 0 || value.as_bytes().get(id_start - 1) != Some(&b'-') {
return None;
}
let timestamp = &value[..id_start - 1];
if timestamp.len() != 19 {
return None;
}
chrono::NaiveDateTime::parse_from_str(timestamp, "%Y-%m-%dT%H-%M-%S").ok()?;
let id = &value[id_start..];
uuid::Uuid::try_parse(id).ok()?;
Some(id.to_owned())
}
fn title(primary: &str, fallback: &str) -> Option<String> {
normalize_title(primary).or_else(|| normalize_title(fallback))
}
#[cfg(test)]
mod fixed_path_tests {
use super::*;
#[test]
fn fixed_rollout_qualification_does_not_require_enumeration() {
let (_tempdir, root) = crate::foreign_sessions::canonical_tempdir();
let sessions = root.join("sessions/2027/01/15");
std::fs::create_dir_all(&sessions).unwrap();
let id = uuid::Uuid::from_u128(9_000);
let rollout = sessions.join(format!("rollout-2027-01-15T12-00-00-{id}.jsonl"));
std::fs::write(&rollout, "").unwrap();
let approved = ApprovedRoot::new(&root).unwrap();
assert_eq!(
existing_rollout_path(&approved, &rollout.to_string_lossy(), &id.to_string()),
Some(dunce::canonicalize(&rollout).unwrap())
);
}
}
#[cfg(all(test, unix))]
mod tests;
@@ -0,0 +1,889 @@
use super::*;
#[test]
fn codex_query_uses_cwd_index_and_length_metadata_opcode() {
let root = TempDir::new().unwrap();
let db_path = root.path().join("state.sqlite");
create_db(&db_path, &[]);
let connection = Connection::open(&db_path).unwrap();
let columns = [
"id",
"rollout_path",
"updated_at_ms",
"source",
"cwd",
"title",
"first_user_message",
"archived",
"git_branch",
]
.into_iter()
.map(str::to_owned)
.collect::<std::collections::HashSet<_>>();
let sql = super::super::db::scan_sql(&columns).unwrap();
let mut plan = connection
.prepare(&format!("EXPLAIN QUERY PLAN {sql}"))
.unwrap();
let details = plan
.query_map(params!["/repo", 0_i64, i64::MAX], |row| {
row.get::<_, String>(3)
})
.unwrap()
.flatten()
.collect::<Vec<_>>();
assert!(details.iter().any(|detail| {
detail.contains("SEARCH threads USING INDEX threads_archived_cwd_updated")
&& detail.contains("archived=? AND cwd=?")
}));
assert!(
details
.iter()
.any(|detail| detail.contains("USE TEMP B-TREE FOR ORDER BY"))
);
let mut bytecode = connection.prepare(&format!("EXPLAIN {sql}")).unwrap();
let columns = bytecode
.query_map(params!["/repo", 0_i64, i64::MAX], |row| {
Ok((
row.get::<_, String>(1)?,
row.get::<_, i64>(3)?,
row.get::<_, i64>(6)?,
))
})
.unwrap()
.flatten()
.collect::<Vec<_>>();
for column in [0_i64, 1, 5, 6, 8] {
assert!(columns.iter().any(|(opcode, p2, p5)| {
opcode == "Column" && *p2 == column && (*p5 & 0xc0) == 0xc0
}));
}
}
#[test]
fn recent_database_probe_is_windowed_bounded_and_keeps_source_filters() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let rollout_dir = root.join("sessions/2027/01/15");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&rollout_dir).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_000_000);
let winner = uuid::Uuid::from_u128(90);
let winner_rollout = rollout_path(&rollout_dir, winner);
fs::write(&winner_rollout, "").unwrap();
let missing = rollout_path(&rollout_dir, uuid::Uuid::from_u128(91));
let db_path = root.join("state_9.sqlite");
create_db(
&db_path,
&[
DbRow {
id: uuid::Uuid::from_u128(93),
rollout_path: &winner_rollout,
updated_at_ms: millis_from_system_time(now + Duration::from_secs(1)).unwrap(),
source: r#"{"custom":"atlas"}"#,
cwd: &cwd,
title: "excluded custom source",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(92),
rollout_path: &winner_rollout,
updated_at_ms: millis_from_system_time(now).unwrap(),
source: r#"{"subagent":"review"}"#,
cwd: &cwd,
title: "excluded",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(91),
rollout_path: &missing,
updated_at_ms: millis_from_system_time(now - Duration::from_secs(1)).unwrap(),
source: "cli",
cwd: &cwd,
title: "missing rollout",
first_user_message: "",
archived: false,
},
DbRow {
id: winner,
rollout_path: &winner_rollout,
updated_at_ms: millis_from_system_time(now - Duration::from_secs(2)).unwrap(),
source: "vscode",
cwd: &cwd,
title: "",
first_user_message: "",
archived: false,
},
],
);
let found = most_recent_in_home(&root, &cwd, now, Duration::from_secs(600)).unwrap();
assert_eq!(found.native_id, winner.to_string());
assert_eq!(found.source, ForeignSessionSource::CodexVsCode);
let columns = [
"id",
"rollout_path",
"updated_at_ms",
"source",
"cwd",
"title",
"first_user_message",
"archived",
"git_branch",
]
.into_iter()
.map(str::to_owned)
.collect::<std::collections::HashSet<_>>();
let sql = super::super::db::recent_scan_sql(&columns).unwrap();
assert!(sql.contains("source IN ('cli', 'vscode')"));
assert!(!sql.contains("custom"));
assert!(sql.contains(&format!(
"LIMIT {}",
super::super::db::MAX_RECENT_DB_CANDIDATES + 1
)));
}
#[test]
fn recent_database_sentinel_marks_invalid_window_incomplete() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
let sessions = root.path().join("sessions");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&sessions).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_010_000);
let valid_id = uuid::Uuid::from_u128(190);
let valid_rollout = rollout_path(&sessions, valid_id);
fs::write(&valid_rollout, "").unwrap();
let db_path = root.path().join("state_10.sqlite");
create_db(&db_path, &[]);
let connection = Connection::open(&db_path).unwrap();
for index in 0..super::super::db::MAX_RECENT_DB_CANDIDATES {
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, 'cli', ?4, 'invalid', '', 0, NULL)",
params![
uuid::Uuid::from_u128(10_000 + index as u128).to_string(),
sessions
.join(format!("missing-{index}.jsonl"))
.display()
.to_string(),
millis_from_system_time(now - Duration::from_secs(index as u64)).unwrap(),
cwd.display().to_string(),
],
)
.unwrap();
}
assert_eq!(
most_recent_in_home(root.path(), &cwd, now, Duration::from_secs(600)),
RecentProbe::Complete(None),
);
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, 'cli', ?4, 'ninth valid', '', 0, NULL)",
params![
valid_id.to_string(),
valid_rollout.display().to_string(),
millis_from_system_time(now - Duration::from_secs(20)).unwrap(),
cwd.display().to_string(),
],
)
.unwrap();
drop(connection);
assert_eq!(
most_recent_in_home(root.path(), &cwd, now, Duration::from_secs(600)),
RecentProbe::Incomplete,
);
}
#[test]
fn recent_database_row_decode_error_is_incomplete() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let sessions = root.join("sessions");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&sessions).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_015_000);
let older_id = uuid::Uuid::from_u128(191);
let older_rollout = rollout_path(&sessions, older_id);
fs::write(&older_rollout, "").unwrap();
let db_path = root.join("state_10.sqlite");
create_db(
&db_path,
&[DbRow {
id: older_id,
rollout_path: &older_rollout,
updated_at_ms: millis_from_system_time(now - Duration::from_secs(1)).unwrap(),
source: "cli",
cwd: &cwd,
title: "older valid",
first_user_message: "",
archived: false,
}],
);
let broken_id = uuid::Uuid::from_u128(192);
let broken_rollout = rollout_path(&sessions, broken_id);
fs::write(&broken_rollout, "").unwrap();
let connection = Connection::open(&db_path).unwrap();
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, 'cli', ?4, CAST(x'80' AS TEXT), '', 0, NULL)",
params![
broken_id.to_string(),
broken_rollout.display().to_string(),
millis_from_system_time(now).unwrap(),
cwd.display().to_string(),
],
)
.unwrap();
drop(connection);
assert_eq!(
most_recent_in_home(&root, &cwd, now, Duration::from_secs(600)),
RecentProbe::Incomplete,
);
let full = scan_in_home(&root, &cwd, now);
assert_eq!(full.len(), 1);
assert_eq!(full[0].native_id, older_id.to_string());
}
#[test]
fn recent_database_tri_state_uses_only_current_generation() {
let now = UNIX_EPOCH + Duration::from_secs(1_800_020_000);
let readable = TempDir::new().unwrap();
let readable_cwd = readable.path().join("repo");
fs::create_dir_all(&readable_cwd).unwrap();
let older_id = uuid::Uuid::from_u128(200);
let older_rollout =
write_recent_rollout(readable.path(), &readable_cwd, now, older_id, json!("cli"));
create_db(
&readable.path().join("state_9.sqlite"),
&[DbRow {
id: older_id,
rollout_path: &older_rollout,
updated_at_ms: millis_from_system_time(now).unwrap(),
source: "cli",
cwd: &readable_cwd,
title: "obsolete generation",
first_user_message: "",
archived: false,
}],
);
create_db(&readable.path().join("state_10.sqlite"), &[]);
assert!(
most_recent_in_home(
readable.path(),
&readable_cwd,
now,
Duration::from_secs(600)
)
.is_none(),
"a usable empty current index must suppress obsolete DB and rollout fallback"
);
let unreadable = TempDir::new().unwrap();
let unreadable_cwd = unreadable.path().join("repo");
fs::create_dir_all(&unreadable_cwd).unwrap();
let fallback_id = uuid::Uuid::from_u128(201);
write_recent_rollout(
unreadable.path(),
&unreadable_cwd,
now,
fallback_id,
json!("vscode"),
);
fs::write(unreadable.path().join("state_10.sqlite"), "not sqlite").unwrap();
assert_eq!(
most_recent_in_home(
unreadable.path(),
&unreadable_cwd,
now,
Duration::from_secs(600)
)
.unwrap()
.native_id,
fallback_id.to_string()
);
let absent = TempDir::new().unwrap();
let absent_cwd = absent.path().join("repo");
fs::create_dir_all(&absent_cwd).unwrap();
let absent_id = uuid::Uuid::from_u128(202);
write_recent_rollout(absent.path(), &absent_cwd, now, absent_id, json!("cli"));
assert_eq!(
most_recent_in_home(absent.path(), &absent_cwd, now, Duration::from_secs(600))
.unwrap()
.native_id,
absent_id.to_string()
);
}
#[cfg(unix)]
#[test]
fn recent_database_unsafe_highest_uses_fallback_not_older_database() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_025_000);
let older_id = uuid::Uuid::from_u128(210);
let older_rollout = write_recent_rollout(root.path(), &cwd, now, older_id, json!("cli"));
touch(&older_rollout, now - Duration::from_secs(60 * 60));
let older_db = root.path().join("state_9.sqlite");
create_db(
&older_db,
&[DbRow {
id: older_id,
rollout_path: &older_rollout,
updated_at_ms: millis_from_system_time(now).unwrap(),
source: "cli",
cwd: &cwd,
title: "must not use older DB",
first_user_message: "",
archived: false,
}],
);
std::os::unix::fs::symlink(&older_db, root.path().join("state_10.sqlite")).unwrap();
let fallback_id = uuid::Uuid::from_u128(211);
write_recent_rollout(root.path(), &cwd, now, fallback_id, json!("vscode"));
assert_eq!(
most_recent_in_home(root.path(), &cwd, now, Duration::from_secs(600))
.unwrap()
.native_id,
fallback_id.to_string(),
);
}
#[test]
fn highest_database_filters_sources_paths_cwd_and_millis() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_000_000);
let updated = now - Duration::from_secs(2);
let updated_ms = millis_from_system_time(updated).unwrap();
let rollout_dir = root.join("sessions/2026/01/01");
fs::create_dir_all(&rollout_dir).unwrap();
let old_rollout = rollout_path(&rollout_dir, uuid::Uuid::from_u128(1));
fs::write(&old_rollout, "").unwrap();
create_db(
&root.join("state_5.sqlite"),
&[DbRow {
id: uuid::Uuid::from_u128(1),
rollout_path: &old_rollout,
updated_at_ms: updated_ms,
source: "cli",
cwd: &cwd,
title: "old generation",
first_user_message: "",
archived: false,
}],
);
let rollout = rollout_path(&rollout_dir, uuid::Uuid::from_u128(2));
fs::write(&rollout, "").unwrap();
let compressed_plain = rollout_path(&rollout_dir, uuid::Uuid::from_u128(3));
fs::write(format!("{}.zst", compressed_plain.display()), "").unwrap();
let chatgpt_rollout = rollout_path(&rollout_dir, uuid::Uuid::from_u128(9));
fs::write(&chatgpt_rollout, "").unwrap();
let missing = rollout_path(&rollout_dir, uuid::Uuid::from_u128(5));
let stale_ms = millis_from_system_time(
now - super::super::super::MAX_SESSION_AGE - Duration::from_secs(1),
)
.unwrap();
create_db(
&root.join("state_9.sqlite"),
&[
DbRow {
id: uuid::Uuid::from_u128(2),
rollout_path: &rollout,
updated_at_ms: updated_ms,
source: "vscode",
cwd: &cwd,
title: "",
first_user_message: "fallback title",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(3),
rollout_path: &compressed_plain,
updated_at_ms: updated_ms - 1,
source: r#"{"custom":"atlas"}"#,
cwd: &cwd,
title: "compressed",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(9),
rollout_path: &chatgpt_rollout,
updated_at_ms: updated_ms - 2,
source: r#"{"custom":"chatgpt"}"#,
cwd: &cwd,
title: "chatgpt",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(4),
rollout_path: &rollout,
updated_at_ms: updated_ms,
source: r#"{"subagent":"review"}"#,
cwd: &cwd,
title: "subagent",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(5),
rollout_path: &missing,
updated_at_ms: updated_ms,
source: "cli",
cwd: &cwd,
title: "missing",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(6),
rollout_path: &rollout,
updated_at_ms: stale_ms,
source: "cli",
cwd: &cwd,
title: "stale",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(7),
rollout_path: &rollout,
updated_at_ms: updated_ms,
source: "cli",
cwd: Path::new("/other"),
title: "wrong cwd",
first_user_message: "",
archived: false,
},
DbRow {
id: uuid::Uuid::from_u128(8),
rollout_path: &rollout,
updated_at_ms: updated_ms,
source: "cli",
cwd: &cwd,
title: "archived",
first_user_message: "",
archived: true,
},
],
);
let sessions = scan_in_home(&root, &cwd, now);
assert_eq!(
sessions
.iter()
.map(|session| session.title.as_str())
.collect::<Vec<_>>(),
vec!["fallback title", "compressed", "chatgpt"]
);
assert_eq!(sessions[0].updated_at, updated);
assert_eq!(sessions[0].source, ForeignSessionSource::CodexVsCode);
assert_eq!(sessions[1].source, ForeignSessionSource::CodexAtlas);
assert_eq!(sessions[2].source, ForeignSessionSource::CodexChatGpt);
}
#[test]
fn empty_newer_database_uses_older_nonempty_generation() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let rollout_dir = root.join("sessions/2027/01/15");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&rollout_dir).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_050_000);
let id = uuid::Uuid::from_u128(40);
let rollout = rollout_path(&rollout_dir, id);
fs::write(&rollout, "").unwrap();
create_db(
&root.join("state_9.sqlite"),
&[DbRow {
id,
rollout_path: &rollout,
updated_at_ms: millis_from_system_time(now).unwrap(),
source: "cli",
cwd: &cwd,
title: "older nonempty generation",
first_user_message: "",
archived: false,
}],
);
create_db(&root.join("state_10.sqlite"), &[]);
let sessions = scan_in_home(&root, &cwd, now);
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].native_id, id.to_string());
assert_eq!(sessions[0].title, "older nonempty generation");
}
#[test]
fn empty_databases_fall_back_to_rollout_files() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
let now = UNIX_EPOCH + Duration::from_secs(1_800_100_000);
let day = session_day(root.path(), now);
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&day).unwrap();
let id = uuid::Uuid::from_u128(41);
let rollout = rollout_path(&day, id);
let contents = [
json!({
"type": "session_meta",
"payload": {
"id": id,
"cwd": cwd.display().to_string(),
"source": "cli"
}
}),
json!({
"type": "event_msg",
"payload": {
"type": "user_message",
"message": "rollout fallback"
}
}),
]
.into_iter()
.map(|record| record.to_string())
.collect::<Vec<_>>()
.join("\n");
fs::write(&rollout, contents).unwrap();
touch(&rollout, now);
create_db(&root.path().join("state_10.sqlite"), &[]);
let sessions = scan_in_home(root.path(), &cwd, now);
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].native_id, id.to_string());
assert_eq!(sessions[0].title, "rollout fallback");
}
#[test]
fn database_window_qualifies_past_invalid_rows() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let sessions = root.join("sessions");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&sessions).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_200_000);
let valid_id = uuid::Uuid::from_u128(500);
let valid_path = rollout_path(&sessions, valid_id);
fs::write(&valid_path, "").unwrap();
let db_path = root.join("state_10.sqlite");
create_db(
&db_path,
&[DbRow {
id: valid_id,
rollout_path: &valid_path,
updated_at_ms: millis_from_system_time(now - Duration::from_secs(100)).unwrap(),
source: "cli",
cwd: &cwd,
title: "valid after window",
first_user_message: "",
archived: false,
}],
);
let connection = Connection::open(&db_path).unwrap();
for i in 0..60_u128 {
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, 'cli', ?4, 'invalid', '', 0, NULL)",
params![
uuid::Uuid::from_u128(1_000 + i).to_string(),
sessions
.join(format!("missing-{i}.jsonl"))
.display()
.to_string(),
millis_from_system_time(now - Duration::from_secs(i as u64)).unwrap(),
cwd.display().to_string(),
],
)
.unwrap();
}
for i in 0..220_u128 {
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, ?4, ?5, 'subagent', '', 0, NULL)",
params![
uuid::Uuid::from_u128(2_000 + i).to_string(),
valid_path.display().to_string(),
millis_from_system_time(now - Duration::from_secs(i as u64)).unwrap(),
r#"{"subagent":"review"}"#,
cwd.display().to_string(),
],
)
.unwrap();
}
drop(connection);
let found = scan_in_home(&root, &cwd, now);
assert_eq!(found.len(), 1);
assert_eq!(found[0].title, "valid after window");
}
#[test]
fn database_window_normalizes_units_and_filters_future_rows_before_limit() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let sessions = root.join("sessions");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&sessions).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_250_000);
let valid_id = uuid::Uuid::from_u128(2_500);
let valid_path = rollout_path(&sessions, valid_id);
fs::write(&valid_path, "").unwrap();
let db_path = root.join("state_10.sqlite");
create_db(
&db_path,
&[DbRow {
id: valid_id,
rollout_path: &valid_path,
updated_at_ms: i64::try_from(
(now - Duration::from_secs(1))
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
)
.unwrap(),
source: "cli",
cwd: &cwd,
title: "newer seconds row",
first_user_message: "",
archived: false,
}],
);
let connection = Connection::open(&db_path).unwrap();
for (prefix, offset) in [
("stale", -(10 * 24 * 60 * 60_i64)),
("future", 24 * 60 * 60),
] {
for i in 0..205_u128 {
let timestamp = if offset < 0 {
now - Duration::from_secs((-offset) as u64 + i as u64)
} else {
now + Duration::from_secs(offset as u64 + i as u64)
};
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, 'cli', ?4, ?5, '', 0, NULL)",
params![
uuid::Uuid::from_u128(3_000 + i + if offset > 0 { 1_000 } else { 0 })
.to_string(),
sessions
.join(format!("missing-{prefix}-{i}.jsonl"))
.display()
.to_string(),
millis_from_system_time(timestamp).unwrap(),
cwd.display().to_string(),
prefix,
],
)
.unwrap();
}
}
drop(connection);
let found = scan_in_home(&root, &cwd, now);
assert_eq!(found.len(), 1);
assert_eq!(found[0].title, "newer seconds row");
}
#[test]
fn each_required_sql_predicate_protects_the_candidate_window() {
for case in 0..5_u128 {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let sessions = root.join("sessions");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&sessions).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_260_000 + case as u64);
let valid_id = uuid::Uuid::from_u128(6_000 + case);
let valid_path = rollout_path(&sessions, valid_id);
fs::write(&valid_path, "").unwrap();
let db_path = root.join("state_10.sqlite");
create_db(
&db_path,
&[DbRow {
id: valid_id,
rollout_path: &valid_path,
updated_at_ms: millis_from_system_time(now - Duration::from_secs(1)).unwrap(),
source: "cli",
cwd: &cwd,
title: "valid required row",
first_user_message: "",
archived: false,
}],
);
let connection = Connection::open(&db_path).unwrap();
for i in 0..201_u128 {
let id_text = uuid::Uuid::from_u128(7_000 + i).to_string();
let mut id = SqlValue::Text(id_text.clone());
let rollout_text = sessions
.join(format!("missing-required-{case}-{i}.jsonl"))
.display()
.to_string();
let mut rollout = SqlValue::Text(rollout_text.clone());
let mut updated = SqlValue::Integer(millis_from_system_time(now).unwrap());
let source = SqlValue::Text("cli".to_owned());
let stored_cwd = SqlValue::Text(cwd.display().to_string());
match case {
0 => id = SqlValue::Blob(id_text.into_bytes()),
1 => id = SqlValue::Text(oversized_text(64, i % 2 == 1)),
2 => rollout = SqlValue::Blob(rollout_text.into_bytes()),
3 => rollout = SqlValue::Text(oversized_text(16 * 1024, i % 2 == 1)),
_ => updated = SqlValue::Real(millis_from_system_time(now).unwrap() as f64 + 0.5),
}
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, ?4, ?5, 'invalid', '', 0, NULL)",
params![id, rollout, updated, source, stored_cwd],
)
.unwrap();
}
drop(connection);
let found = scan_in_home(&root, &cwd, now);
assert_eq!(found.len(), 1, "required predicate case {case}");
assert_eq!(found[0].native_id, valid_id.to_string());
}
}
#[test]
fn optional_codex_metadata_degrades_without_dropping_rows() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
let sessions = root.join("sessions");
fs::create_dir_all(&cwd).unwrap();
fs::create_dir_all(&sessions).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_270_000);
let db_path = root.join("state_10.sqlite");
create_db(&db_path, &[]);
let connection = Connection::open(&db_path).unwrap();
let expected = [
"fallback oversized title",
"fallback wrong title type",
"title with oversized fallback",
"title with wrong fallback type",
"title with oversized branch",
"title with wrong branch type",
];
for (index, expected_title) in expected.iter().enumerate() {
let id = uuid::Uuid::from_u128(8_000 + index as u128);
let path = rollout_path(&sessions, id);
fs::write(&path, "").unwrap();
let mut title = SqlValue::Text((*expected_title).to_owned());
let mut first = SqlValue::Text((*expected_title).to_owned());
let mut branch = SqlValue::Text("main".to_owned());
match index {
0 => title = SqlValue::Text(oversized_text(64 * 1024, false)),
1 => title = SqlValue::Blob(b"wrong title".to_vec()),
2 => first = SqlValue::Text(oversized_text(64 * 1024, true)),
3 => first = SqlValue::Blob(b"wrong fallback".to_vec()),
4 => branch = SqlValue::Text(oversized_text(4 * 1024, false)),
_ => branch = SqlValue::Blob(b"wrong branch".to_vec()),
}
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, 'cli', ?4, ?5, ?6, 0, ?7)",
params![
id.to_string(),
path.display().to_string(),
millis_from_system_time(now - Duration::from_secs(index as u64)).unwrap(),
cwd.display().to_string(),
title,
first,
branch,
],
)
.unwrap();
}
drop(connection);
let found = scan_in_home(&root, &cwd, now);
assert_eq!(found.len(), expected.len());
for (index, expected_title) in expected.iter().enumerate() {
let id = uuid::Uuid::from_u128(8_000 + index as u128).to_string();
let session = found
.iter()
.find(|session| session.native_id == id)
.unwrap();
assert_eq!(session.title, *expected_title);
if index >= 4 {
assert_eq!(session.branch, None);
}
}
}
#[test]
fn state_database_probes_have_a_supported_generation_ceiling() {
let (_tempdir, root) = canonical_tempdir();
for i in 0..100 {
fs::write(root.join(format!("unrelated-{i:03}")), "").unwrap();
}
fs::write(root.join("state_2.sqlite"), "").unwrap();
let boundary = root.join(format!("state_{MAX_STATE_DB_GENERATION}.sqlite"));
let beyond = root.join(format!("state_{}.sqlite", MAX_STATE_DB_GENERATION + 1));
fs::write(&boundary, "").unwrap();
fs::write(&beyond, "").unwrap();
let approved_root = ApprovedRoot::new(&root).unwrap();
assert_eq!(
state_databases(&approved_root).collect::<Vec<_>>(),
vec![boundary.clone(), root.join("state_2.sqlite")]
);
fs::remove_file(boundary).unwrap();
assert_eq!(
state_databases(&approved_root).collect::<Vec<_>>(),
vec![root.join("state_2.sqlite")]
);
}
#[cfg(unix)]
#[test]
fn state_database_probe_rejects_symlink_escape() {
let root = TempDir::new().unwrap();
let codex_home = root.path().join("codex");
fs::create_dir_all(&codex_home).unwrap();
let outside = root.path().join("outside.sqlite");
fs::write(&outside, "").unwrap();
std::os::unix::fs::symlink(
&outside,
codex_home.join(format!("state_{MAX_STATE_DB_GENERATION}.sqlite")),
)
.unwrap();
let approved_root = ApprovedRoot::new(&codex_home).unwrap();
assert!(state_databases(&approved_root).next().is_none());
}
#[test]
fn normalizes_legacy_seconds_and_millis_then_uses_shared_recency() {
let now = UNIX_EPOCH + Duration::from_secs(1_800_000_000);
let expected = now - Duration::from_secs(10);
let seconds = 1_799_999_990_i64;
let millis = millis_from_system_time(expected).unwrap();
assert_eq!(
super::super::db::normalize_updated_at(seconds),
Some(expected)
);
assert_eq!(
super::super::db::normalize_updated_at(millis),
Some(expected)
);
let future = now + Duration::from_secs(24 * 60 * 60);
assert_eq!(
super::super::db::normalize_updated_at(millis_from_system_time(future).unwrap()),
Some(future)
);
assert!(!super::super::super::is_within(
future,
now,
super::super::super::MAX_SESSION_AGE,
));
}
@@ -0,0 +1,445 @@
use super::*;
fn rollout_records(id: uuid::Uuid, cwd: &Path, source: serde_json::Value, title: &str) -> String {
[
json!({
"type": "session_meta",
"payload": {
"id": id,
"cwd": cwd.display().to_string(),
"source": source
}
}),
json!({
"type": "event_msg",
"payload": {"type": "user_message", "message": title}
}),
]
.into_iter()
.map(|record| record.to_string())
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn recent_fallback_skips_excluded_sources_and_wrong_cwds() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_090_000);
let day = session_day(root.path(), now);
fs::create_dir_all(&day).unwrap();
let excluded = uuid::Uuid::from_u128(10);
let custom = uuid::Uuid::from_u128(13);
let wrong_cwd = uuid::Uuid::from_u128(11);
let winner = uuid::Uuid::from_u128(12);
for (id, stored_cwd, source, age) in [
(
excluded,
cwd.as_path(),
json!({"subagent":"review"}),
Duration::ZERO,
),
(
custom,
cwd.as_path(),
json!({"custom":"atlas"}),
Duration::from_millis(500),
),
(
wrong_cwd,
Path::new("/other"),
json!("cli"),
Duration::from_secs(1),
),
(
winner,
cwd.as_path(),
json!("vscode"),
Duration::from_secs(2),
),
] {
let path = day.join(format!("rollout-2027-01-15T12-00-00-{id}.jsonl"));
fs::write(&path, rollout_records(id, stored_cwd, source, "")).unwrap();
touch(&path, now - age);
}
let found = most_recent_in_home(root.path(), &cwd, now, Duration::from_secs(600)).unwrap();
assert_eq!(found.native_id, winner.to_string());
assert_eq!(found.source, ForeignSessionSource::CodexVsCode);
}
#[test]
fn recent_fallback_fails_closed_at_directory_entry_cap() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_095_000);
let day = session_day(root.path(), now);
fs::create_dir_all(&day).unwrap();
write_recent_rollout(
root.path(),
&cwd,
now,
uuid::Uuid::from_u128(15),
json!("cli"),
);
for index in 0..super::super::files::MAX_RECENT_DIRECTORY_ENTRIES {
fs::write(day.join(format!("junk-{index:03}")), "").unwrap();
}
assert_eq!(
most_recent_in_home(root.path(), &cwd, now, Duration::from_secs(600)),
RecentProbe::Incomplete,
);
}
#[test]
fn recent_fallback_includes_old_creation_directory_with_fresh_mtime() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_097_000);
let created_at = now - Duration::from_secs(10 * 24 * 60 * 60);
let day = session_day(root.path(), created_at);
fs::create_dir_all(&day).unwrap();
let id = uuid::Uuid::from_u128(16);
let path = rollout_path(&day, id);
fs::write(&path, rollout_records(id, &cwd, json!("cli"), "")).unwrap();
touch(&path, now);
assert_eq!(
most_recent_in_home(root.path(), &cwd, now, Duration::from_secs(600))
.unwrap()
.native_id,
id.to_string(),
);
}
#[test]
fn falls_back_to_bounded_plain_and_compressed_heads() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_100_000);
let day = session_day(root.path(), now);
let valid_day = session_day(root.path(), now - Duration::from_secs(24 * 60 * 60));
fs::create_dir_all(&day).unwrap();
fs::create_dir_all(&valid_day).unwrap();
let plain_id = uuid::Uuid::from_u128(20);
let plain = valid_day.join(format!("rollout-1970-05-08T12-00-00-{plain_id}.jsonl"));
fs::write(
&plain,
format!(
"{{malformed\n{}",
rollout_records(plain_id, &cwd, json!("cli"), "filesystem title")
),
)
.unwrap();
touch(&plain, now);
let compressed_id = uuid::Uuid::from_u128(21);
let compressed = valid_day.join(format!(
"rollout-1970-05-08T12-00-01-{compressed_id}.jsonl.zst"
));
let compressed_head = rollout_records(compressed_id, &cwd, json!("vscode"), "compressed title");
fs::write(
&compressed,
zstd::encode_all(compressed_head.as_bytes(), 1).unwrap(),
)
.unwrap();
touch(&compressed, now - Duration::from_secs(1));
let malformed_id = uuid::Uuid::from_u128(22);
let malformed = valid_day.join(format!(
"rollout-1970-05-08T12-00-02-{malformed_id}.jsonl.zst"
));
fs::write(&malformed, b"not a zstd frame").unwrap();
touch(&malformed, now - Duration::from_secs(2));
let concatenated_id = uuid::Uuid::from_u128(23);
let concatenated_path = valid_day.join(format!(
"rollout-1970-05-08T12-00-03-{concatenated_id}.jsonl.zst"
));
let mut concatenated = zstd::encode_all(
json!({"type":"event_msg","payload":{"type":"user_message","message":"frame one"}})
.to_string()
.as_bytes(),
1,
)
.unwrap();
concatenated.extend(
zstd::encode_all(
rollout_records(
concatenated_id,
&cwd,
json!("cli"),
"must not read frame two",
)
.as_bytes(),
1,
)
.unwrap(),
);
fs::write(&concatenated_path, concatenated).unwrap();
touch(&concatenated_path, now - Duration::from_secs(3));
let skippable_id = uuid::Uuid::from_u128(24);
let skippable_path = valid_day.join(format!(
"rollout-1970-05-08T12-00-04-{skippable_id}.jsonl.zst"
));
let payload_len = super::super::files::MAX_COMPRESSED_HEAD_BYTES + 1024;
let mut skippable = Vec::with_capacity(payload_len + 8);
skippable.extend(0x184D_2A50_u32.to_le_bytes());
skippable.extend(u32::try_from(payload_len).unwrap().to_le_bytes());
skippable.resize(8 + payload_len, 0);
skippable.extend(
zstd::encode_all(
rollout_records(skippable_id, &cwd, json!("cli"), "beyond compressed cap").as_bytes(),
1,
)
.unwrap(),
);
fs::write(&skippable_path, &skippable).unwrap();
assert!(skippable.len() > super::super::files::MAX_COMPRESSED_HEAD_BYTES);
touch(&skippable_path, now - Duration::from_secs(4));
let window_id = uuid::Uuid::from_u128(25);
let window_path = valid_day.join(format!("rollout-1970-05-08T12-00-05-{window_id}.jsonl.zst"));
let window_head = rollout_records(window_id, &cwd, json!("cli"), "oversized window");
let mut encoder = zstd::Encoder::new(Vec::new(), 1).unwrap();
encoder
.window_log(super::super::files::MAX_ZSTD_WINDOW_LOG + 1)
.unwrap();
encoder.include_contentsize(false).unwrap();
encoder.write_all(window_head.as_bytes()).unwrap();
let window_frame = encoder.finish().unwrap();
assert_eq!(
zstd::decode_all(window_frame.as_slice()).unwrap(),
window_head.as_bytes()
);
let mut limited = zstd::Decoder::new(window_frame.as_slice()).unwrap();
limited
.window_log_max(super::super::files::MAX_ZSTD_WINDOW_LOG)
.unwrap();
let mut limited_output = Vec::new();
assert!(limited.read_to_end(&mut limited_output).is_err());
fs::write(&window_path, window_frame).unwrap();
touch(&window_path, now - Duration::from_secs(5));
let output_id = uuid::Uuid::from_u128(26);
let output_path = valid_day.join(format!("rollout-1970-05-08T12-00-06-{output_id}.jsonl.zst"));
let output_head = format!(
"{}\n{}",
"x".repeat(super::super::files::MAX_HEAD_BYTES + 1),
rollout_records(output_id, &cwd, json!("cli"), "beyond output cap")
);
let output_frame = zstd::encode_all(output_head.as_bytes(), 1).unwrap();
assert!(
String::from_utf8(zstd::decode_all(output_frame.as_slice()).unwrap())
.unwrap()
.contains(&output_id.to_string())
);
fs::write(&output_path, output_frame).unwrap();
touch(&output_path, now - Duration::from_secs(6));
for i in 0..520_u128 {
let id = uuid::Uuid::from_u128(100 + i);
let path = day.join(format!("rollout-1970-05-08T12-01-00-{id}.jsonl"));
fs::write(
&path,
rollout_records(id, &cwd, json!({"subagent":"review"}), "excluded source"),
)
.unwrap();
touch(&path, now - Duration::from_secs(i as u64 + 1));
}
let sessions = scan_in_home(root.path(), &cwd, now);
assert_eq!(sessions.len(), 2);
assert_eq!(sessions[0].native_id, plain_id.to_string());
assert_eq!(sessions[0].title, "filesystem title");
assert_eq!(sessions[1].native_id, compressed_id.to_string());
assert_eq!(sessions[1].title, "compressed title");
}
#[test]
fn fallback_requires_complete_matching_first_session_meta() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_150_000);
let day = session_day(root.path(), now);
fs::create_dir_all(&day).unwrap();
for (id, first_payload) in [
(
uuid::Uuid::from_u128(30),
json!({"id":uuid::Uuid::from_u128(30),"source":"cli"}),
),
(
uuid::Uuid::from_u128(31),
json!({"id":uuid::Uuid::from_u128(31),"cwd":cwd.display().to_string()}),
),
(
uuid::Uuid::from_u128(32),
json!({"cwd":cwd.display().to_string(),"source":"cli"}),
),
(
uuid::Uuid::from_u128(33),
json!({"id":uuid::Uuid::from_u128(999),"cwd":cwd.display().to_string(),"source":"cli"}),
),
] {
let path = day.join(format!("rollout-2027-01-15T12-00-00-{id}.jsonl"));
fs::write(
&path,
[
json!({"type":"session_meta","payload":first_payload}).to_string(),
rollout_records(id, &cwd, json!("cli"), "fork copy"),
]
.join("\n"),
)
.unwrap();
touch(&path, now);
}
assert!(scan_in_home(root.path(), &cwd, now).is_empty());
}
#[test]
fn rollout_paths_must_remain_under_approved_roots() {
let (_tempdir, root) = canonical_tempdir();
let sessions = root.join("sessions");
fs::create_dir_all(&sessions).unwrap();
let approved_id = uuid::Uuid::from_u128(600);
let compressed_id = uuid::Uuid::from_u128(601);
let approved = rollout_path(&sessions, approved_id);
let compressed_plain = rollout_path(&sessions, compressed_id);
let compressed = PathBuf::from(format!("{}.zst", compressed_plain.display()));
let outside = rollout_path(&root, uuid::Uuid::from_u128(602));
let wrong_extension = sessions.join("not-a-rollout.txt");
let directory = rollout_path(&sessions, uuid::Uuid::from_u128(603));
fs::write(&approved, "").unwrap();
fs::write(&compressed, "").unwrap();
fs::write(&outside, "").unwrap();
fs::write(&wrong_extension, "").unwrap();
fs::create_dir_all(&directory).unwrap();
let approved_root = ApprovedRoot::new(&root).unwrap();
assert_eq!(
existing_rollout_path(
&approved_root,
&approved.display().to_string(),
&approved_id.to_string()
),
Some(dunce::canonicalize(&approved).unwrap())
);
assert_eq!(
existing_rollout_path(
&approved_root,
&compressed_plain.display().to_string(),
&compressed_id.to_string()
),
Some(dunce::canonicalize(&compressed).unwrap())
);
assert_eq!(
existing_rollout_path(
&approved_root,
&outside.display().to_string(),
&uuid::Uuid::from_u128(602).to_string()
),
None
);
assert_eq!(
existing_rollout_path(
&approved_root,
&wrong_extension.display().to_string(),
&uuid::Uuid::from_u128(604).to_string()
),
None
);
assert_eq!(
existing_rollout_path(
&approved_root,
&directory.display().to_string(),
&uuid::Uuid::from_u128(603).to_string()
),
None
);
let traversal = format!(
"sessions/../{}",
outside.file_name().unwrap().to_string_lossy()
);
assert_eq!(
existing_rollout_path(
&approved_root,
&traversal,
&uuid::Uuid::from_u128(602).to_string()
),
None
);
let adversarial = sessions.join(format!(
"rollout-2027-01-15T12-00-00-extra-{}.jsonl",
uuid::Uuid::from_u128(604)
));
fs::write(&adversarial, "").unwrap();
assert_eq!(rollout_id(&adversarial), None);
#[cfg(unix)]
{
let link = rollout_path(&sessions, uuid::Uuid::from_u128(602));
std::os::unix::fs::symlink(&outside, &link).unwrap();
assert_eq!(
existing_rollout_path(
&approved_root,
&link.display().to_string(),
&uuid::Uuid::from_u128(602).to_string()
),
None
);
}
}
#[cfg(unix)]
#[test]
fn fallback_rejects_sessions_parent_symlink_escape() {
let root = TempDir::new().unwrap();
let outside = TempDir::new().unwrap();
let codex_home = root.path().join("codex");
let cwd = codex_home.join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(1_800_300_000);
let outside_day = session_day(outside.path(), now);
fs::create_dir_all(&outside_day).unwrap();
let id = uuid::Uuid::from_u128(5_000);
let rollout = rollout_path(&outside_day, id);
fs::write(
&rollout,
rollout_records(id, &cwd, json!("cli"), "outside sessions root"),
)
.unwrap();
touch(&rollout, now);
std::os::unix::fs::symlink(outside.path().join("sessions"), codex_home.join("sessions"))
.unwrap();
assert!(scan_in_home(&codex_home, &cwd, now).is_empty());
}
#[test]
fn date_dirs_include_utc_and_offset_boundary_days() {
let root = Path::new("/sessions");
let now = system_time_from_millis(
chrono::DateTime::parse_from_rfc3339("2026-01-01T00:30:00Z")
.unwrap()
.timestamp_millis(),
)
.unwrap();
let negative = super::super::files::recent_date_dirs(root, now, -2 * 60 * 60);
assert_eq!(negative.len(), 32);
assert!(negative.contains(&root.join("2026/01/01")));
assert!(negative.contains(&root.join("2025/12/31")));
let late = system_time_from_millis(
chrono::DateTime::parse_from_rfc3339("2026-01-01T23:30:00Z")
.unwrap()
.timestamp_millis(),
)
.unwrap();
let positive = super::super::files::recent_date_dirs(root, late, 2 * 60 * 60);
assert_eq!(positive[0], root.join("2026/01/02"));
assert!(positive.contains(&root.join("2026/01/01")));
}
@@ -0,0 +1,119 @@
use std::fs;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use filetime::FileTime;
use rusqlite::{Connection, params, types::Value as SqlValue};
use serde_json::json;
use tempfile::TempDir;
use super::*;
use crate::foreign_sessions::{
canonical_tempdir, millis_from_system_time, system_time_from_millis,
};
mod db;
mod files;
struct DbRow<'a> {
id: uuid::Uuid,
rollout_path: &'a Path,
updated_at_ms: i64,
source: &'a str,
cwd: &'a Path,
title: &'a str,
first_user_message: &'a str,
archived: bool,
}
fn create_db(path: &Path, rows: &[DbRow<'_>]) {
let connection = Connection::open(path).unwrap();
connection
.execute_batch(
"CREATE TABLE threads (
id TEXT,
rollout_path TEXT,
updated_at_ms INTEGER,
source TEXT,
cwd TEXT,
title TEXT,
first_user_message TEXT,
archived INTEGER,
git_branch TEXT
);
CREATE INDEX threads_archived_cwd_updated \
ON threads(archived, cwd, updated_at_ms DESC);",
)
.unwrap();
for row in rows {
connection
.execute(
"INSERT INTO threads VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, NULL)",
params![
row.id.to_string(),
row.rollout_path.display().to_string(),
row.updated_at_ms,
row.source,
row.cwd.display().to_string(),
row.title,
row.first_user_message,
row.archived,
],
)
.unwrap();
}
}
fn touch(path: &Path, time: SystemTime) {
filetime::set_file_mtime(path, FileTime::from_system_time(time)).unwrap();
}
fn session_day(root: &Path, now: SystemTime) -> PathBuf {
use chrono::{DateTime, Datelike, Utc};
let date = DateTime::<Utc>::from(now).date_naive();
root.join("sessions")
.join(format!("{:04}", date.year()))
.join(format!("{:02}", date.month()))
.join(format!("{:02}", date.day()))
}
fn rollout_path(dir: &Path, id: uuid::Uuid) -> PathBuf {
dir.join(format!("rollout-2027-01-15T12-00-00-{id}.jsonl"))
}
fn write_recent_rollout(
root: &Path,
cwd: &Path,
now: SystemTime,
id: uuid::Uuid,
source: serde_json::Value,
) -> PathBuf {
let day = session_day(root, now);
fs::create_dir_all(&day).unwrap();
let path = rollout_path(&day, id);
fs::write(
&path,
json!({
"type": "session_meta",
"payload": {
"id": id,
"cwd": cwd.display().to_string(),
"source": source,
}
})
.to_string(),
)
.unwrap();
touch(&path, now);
path
}
fn oversized_text(limit: usize, utf8: bool) -> String {
if utf8 {
"é".repeat(limit / 2 + 1)
} else {
"x".repeat(limit + 1)
}
}