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:
2026-07-17 05:31:01 -04:00
commit d6c20fc13f
2612 changed files with 1353757 additions and 0 deletions
@@ -0,0 +1,295 @@
use std::ffi::OsString;
use std::fs::{File, Metadata};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use kigi_sqlite_journal::JournalMode;
use rusqlite::{Connection, OpenFlags};
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
#[derive(Debug, Clone)]
pub(super) struct ApprovedRoot {
path: PathBuf,
directory: Arc<File>,
}
pub(super) struct OpenedRegularFile {
pub file: File,
pub path: PathBuf,
pub metadata: Metadata,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct DirectoryVisit {
pub visited: usize,
pub complete: bool,
}
pub(super) struct ReadTransactionSqlite {
connection: Connection,
#[allow(dead_code)]
metadata: Metadata,
}
impl ReadTransactionSqlite {
fn new(connection: Connection, metadata: Metadata) -> Self {
Self {
connection,
metadata,
}
}
#[allow(dead_code)]
pub fn metadata(&self) -> &Metadata {
&self.metadata
}
}
impl Deref for ReadTransactionSqlite {
type Target = Connection;
fn deref(&self) -> &Self::Target {
&self.connection
}
}
impl Drop for ReadTransactionSqlite {
fn drop(&mut self) {
let _ = self.connection.execute_batch("ROLLBACK");
}
}
impl ApprovedRoot {
pub fn new(path: &Path) -> Option<Self> {
let path = dunce::canonicalize(path).ok()?;
#[cfg(unix)]
let directory = unix::open_directory_path(&path)?;
#[cfg(windows)]
let directory = windows::open_directory_path(&path)?;
#[cfg(not(any(unix, windows)))]
let directory: File = return None;
let metadata = directory.metadata().ok()?;
if !metadata.is_dir() || has_reparse_point(&metadata) {
return None;
}
#[cfg(windows)]
if !windows::directory_path_matches(&path, &directory) {
return None;
}
Some(Self {
path,
directory: Arc::new(directory),
})
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn join(&self, path: impl AsRef<Path>) -> PathBuf {
self.path.join(path)
}
#[allow(dead_code)]
pub fn modified(&self) -> Option<SystemTime> {
self.directory.metadata().ok()?.modified().ok()
}
pub fn subroot(&self, path: &Path) -> Option<Self> {
#[cfg(unix)]
{
let relative = self.relative_path(path)?;
let directory = unix::open_directory_relative(&self.directory, &relative)?;
let metadata = directory.metadata().ok()?;
if !metadata.is_dir() {
return None;
}
Some(Self {
path: self.path.join(relative),
directory: Arc::new(directory),
})
}
#[cfg(not(unix))]
{
let _ = path;
None
}
}
pub fn for_each_entry(&self, visit: impl FnMut(OsString)) -> bool {
#[cfg(unix)]
{
unix::visit_directory_names(&self.directory, visit)
}
#[cfg(not(unix))]
{
let _ = visit;
false
}
}
pub fn for_each_entry_bounded(
&self,
max_entries: usize,
visit: impl FnMut(OsString),
) -> DirectoryVisit {
#[cfg(unix)]
{
unix::visit_directory_names_bounded(&self.directory, max_entries, visit)
}
#[cfg(not(unix))]
{
let _ = (max_entries, visit);
DirectoryVisit {
visited: 0,
complete: false,
}
}
}
fn relative_path(&self, path: &Path) -> Option<PathBuf> {
let relative = if path.is_absolute() {
path.strip_prefix(&self.path).ok()?
} else {
path
};
relative
.components()
.all(|component| {
matches!(
component,
std::path::Component::Normal(_) | std::path::Component::CurDir
)
})
.then(|| relative.to_path_buf())
}
pub fn resolve_regular_file(&self, path: &Path) -> Option<(PathBuf, Metadata)> {
let opened = self.open_regular_file(path)?;
Some((opened.path, opened.metadata))
}
pub fn open_regular_file(&self, path: &Path) -> Option<OpenedRegularFile> {
#[cfg(unix)]
{
let relative = self.relative_path(path)?;
let file = unix::open_regular_relative(&self.directory, &relative)?;
let metadata = file.metadata().ok()?;
if !metadata.is_file() {
return None;
}
Some(OpenedRegularFile {
file,
path: self.path.join(relative),
metadata,
})
}
#[cfg(windows)]
{
let path = if path.is_absolute() {
path.to_path_buf()
} else {
self.join(path)
};
let parent = dunce::canonicalize(path.parent()?).ok()?;
if !parent.starts_with(&self.path) {
return None;
}
let path = parent.join(path.file_name()?);
let expected = std::fs::symlink_metadata(&path).ok()?;
if !expected.is_file()
|| expected.file_type().is_symlink()
|| has_reparse_point(&expected)
{
return None;
}
let canonical_path = dunce::canonicalize(&path).ok()?;
if canonical_path.parent() != Some(parent.as_path())
|| !canonical_path.starts_with(&self.path)
{
return None;
}
let file = windows::open_regular_path(&canonical_path)?;
let metadata = file.metadata().ok()?;
if !metadata.is_file()
|| has_reparse_point(&metadata)
|| !windows::canonical_file_matches(&canonical_path, &file)
{
return None;
}
return Some(OpenedRegularFile {
file,
path: canonical_path,
metadata,
});
}
#[cfg(not(any(unix, windows)))]
{
let _ = path;
None
}
}
}
pub(super) fn open_sqlite_transaction(
root: &ApprovedRoot,
path: &Path,
) -> Option<ReadTransactionSqlite> {
open_sqlite_transaction_with_journal_mode(root, path, JournalMode::for_db_path(path))
}
fn open_sqlite_transaction_with_journal_mode(
root: &ApprovedRoot,
path: &Path,
journal_mode: JournalMode,
) -> Option<ReadTransactionSqlite> {
match journal_mode {
JournalMode::Wal => {}
JournalMode::Truncate => return None,
}
let opened = root.open_regular_file(path)?;
// These are same-user application stores. Canonical containment and a
// non-symlink final file are validated above; adversarial swap-and-restore
// races are outside this scanner's local-user threat model. Only local WAL
// reaches this direct read-only/query-only open; its native coordination may
// still update SHM read marks despite scanner SQL making no logical writes.
let connection = Connection::open_with_flags(
&opened.path,
OpenFlags::SQLITE_OPEN_READ_ONLY
| OpenFlags::SQLITE_OPEN_NO_MUTEX
| OpenFlags::SQLITE_OPEN_NOFOLLOW,
)
.ok()?;
let _ = connection.busy_timeout(Duration::from_millis(50));
connection
.execute_batch("PRAGMA query_only=ON; BEGIN DEFERRED")
.ok()?;
connection
.query_row("SELECT COUNT(*) FROM sqlite_schema", [], |row| {
row.get::<_, i64>(0)
})
.ok()?;
Some(ReadTransactionSqlite::new(connection, opened.metadata))
}
#[cfg(windows)]
fn has_reparse_point(metadata: &Metadata) -> bool {
use std::os::windows::fs::MetadataExt as _;
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x400;
metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0
}
#[cfg(not(windows))]
fn has_reparse_point(_metadata: &Metadata) -> bool {
false
}
#[cfg(test)]
mod tests;
@@ -0,0 +1,224 @@
#[cfg(unix)]
use std::ffi::OsString;
#[cfg(windows)]
use super::windows;
use super::*;
use crate::foreign_sessions::canonical_tempdir;
#[cfg(unix)]
#[test]
fn retained_directory_capability_survives_path_replacement() {
use std::io::Read as _;
let (_tempdir, root) = canonical_tempdir();
let outside = tempfile::tempdir().unwrap();
let original = root.join("sessions");
std::fs::create_dir_all(&original).unwrap();
std::fs::write(original.join("inside"), "inside").unwrap();
std::fs::write(outside.path().join("outside"), "outside").unwrap();
let approved = ApprovedRoot::new(&root).unwrap();
let retained = approved.subroot(&original).unwrap();
let moved = root.join("sessions-original");
std::fs::rename(&original, &moved).unwrap();
std::os::unix::fs::symlink(outside.path(), &original).unwrap();
let mut names = Vec::new();
assert!(retained.for_each_entry(|name| names.push(name)));
assert!(names.contains(&OsString::from("inside")));
assert!(!names.contains(&OsString::from("outside")));
let mut contents = String::new();
retained
.open_regular_file(&retained.join("inside"))
.unwrap()
.file
.read_to_string(&mut contents)
.unwrap();
assert_eq!(contents, "inside");
assert!(
retained
.open_regular_file(&retained.join("outside"))
.is_none()
);
}
#[cfg(unix)]
#[test]
fn bounded_directory_visit_reports_exact_cutoff() {
let root = tempfile::tempdir().unwrap();
for index in 0..5 {
std::fs::write(root.path().join(format!("entry-{index}")), "").unwrap();
}
let approved = ApprovedRoot::new(root.path()).unwrap();
let mut visited = Vec::new();
let outcome = approved.for_each_entry_bounded(3, |name| visited.push(name));
assert_eq!(visited.len(), 3);
assert_eq!(
outcome,
DirectoryVisit {
visited: 3,
complete: false,
}
);
let small = tempfile::tempdir().unwrap();
std::fs::write(small.path().join("a"), "").unwrap();
std::fs::write(small.path().join("b"), "").unwrap();
let approved = ApprovedRoot::new(small.path()).unwrap();
let mut visited = 0;
assert_eq!(
approved.for_each_entry_bounded(3, |_| visited += 1),
DirectoryVisit {
visited: 2,
complete: true,
}
);
let exact = tempfile::tempdir().unwrap();
for index in 0..3 {
std::fs::write(exact.path().join(format!("entry-{index}")), "").unwrap();
}
let approved = ApprovedRoot::new(exact.path()).unwrap();
let mut visited = 0;
assert_eq!(
approved.for_each_entry_bounded(3, |_| visited += 1),
DirectoryVisit {
visited: 3,
complete: true,
}
);
assert_eq!(visited, 3);
}
#[cfg(unix)]
#[test]
fn nonblocking_open_rejects_fifo() {
use std::os::unix::ffi::OsStrExt as _;
let root = tempfile::tempdir().unwrap();
let fifo = root.path().join("metadata");
let path = std::ffi::CString::new(fifo.as_os_str().as_bytes()).unwrap();
// SAFETY: the path is NUL-terminated and points into the live tempdir.
assert_eq!(unsafe { libc::mkfifo(path.as_ptr(), 0o600) }, 0);
let approved = ApprovedRoot::new(root.path()).unwrap();
assert!(approved.open_regular_file(&fifo).is_none());
}
#[test]
fn sqlite_truncate_mode_skips_foreign_database_without_mutation() {
let root = tempfile::tempdir().unwrap();
let path = root.path().join("state.db");
let connection = rusqlite::Connection::open(&path).unwrap();
connection
.execute_batch(
"PRAGMA journal_mode=WAL;
CREATE TABLE values_for_test (value INTEGER);
INSERT INTO values_for_test VALUES (42);",
)
.unwrap();
drop(connection);
let contents = std::fs::read(&path).unwrap();
let approved = ApprovedRoot::new(root.path()).unwrap();
assert!(
open_sqlite_transaction_with_journal_mode(&approved, &path, JournalMode::Truncate)
.is_none()
);
assert_eq!(std::fs::read(&path).unwrap(), contents);
let connection =
rusqlite::Connection::open_with_flags(&path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)
.unwrap();
let journal_mode: String = connection
.query_row("PRAGMA journal_mode", [], |row| row.get(0))
.unwrap();
assert_eq!(journal_mode, "wal");
}
#[test]
fn sqlite_wal_mode_queries_and_pins_snapshot() {
let (_tempdir, root) = canonical_tempdir();
let path = root.join("state.db");
let writer = rusqlite::Connection::open(&path).unwrap();
writer
.execute_batch(
"PRAGMA journal_mode=WAL;
PRAGMA wal_autocheckpoint=0;
CREATE TABLE values_for_test (value INTEGER);
PRAGMA wal_checkpoint(TRUNCATE);
INSERT INTO values_for_test VALUES (42);",
)
.unwrap();
let approved = ApprovedRoot::new(&root).unwrap();
let database =
open_sqlite_transaction_with_journal_mode(&approved, &path, JournalMode::Wal).unwrap();
let value: i64 = database
.query_row("SELECT value FROM values_for_test", [], |row| row.get(0))
.unwrap();
assert_eq!(value, 42);
writer
.execute("INSERT INTO values_for_test VALUES (43)", [])
.unwrap();
let count: i64 = database
.query_row("SELECT COUNT(*) FROM values_for_test", [], |row| row.get(0))
.unwrap();
assert_eq!(count, 1);
drop(writer);
}
#[test]
fn sqlite_scanner_connection_is_query_only() {
let (_tempdir, root) = canonical_tempdir();
let path = root.join("state.db");
let connection = rusqlite::Connection::open(&path).unwrap();
connection
.execute_batch("CREATE TABLE values_for_test (value INTEGER);")
.unwrap();
drop(connection);
let approved = ApprovedRoot::new(&root).unwrap();
let database = open_sqlite_transaction(&approved, &path).unwrap();
let query_only: i64 = database
.query_row("PRAGMA query_only", [], |row| row.get(0))
.unwrap();
assert_eq!(query_only, 1);
assert!(
database
.execute("INSERT INTO values_for_test VALUES (1)", [])
.is_err()
);
}
#[cfg(windows)]
#[test]
fn windows_open_verifies_stable_file_identity() {
let root = tempfile::tempdir().unwrap();
let first = root.path().join("first");
let second = root.path().join("second");
std::fs::write(&first, "first").unwrap();
std::fs::write(&second, "second").unwrap();
let approved = ApprovedRoot::new(root.path()).unwrap();
let opened = approved.open_regular_file(&first).unwrap();
let first_again = windows::open_regular_path(&first).unwrap();
let second = windows::open_regular_path(&second).unwrap();
assert!(windows::same_file_for_test(&first_again, &opened.file));
assert!(!windows::same_file_for_test(&second, &opened.file));
assert!(opened.path.starts_with(approved.path()));
assert!(windows::final_path_matches_for_test(
&opened.path,
&opened.file
));
}
#[cfg(windows)]
#[test]
fn windows_directory_scans_fail_closed() {
let root = tempfile::tempdir().unwrap();
let child = root.path().join("child");
std::fs::create_dir_all(&child).unwrap();
let approved = ApprovedRoot::new(root.path()).unwrap();
assert!(approved.subroot(&child).is_none());
assert!(!approved.for_each_entry(|_| panic!("enumerated on Windows")));
}
@@ -0,0 +1,240 @@
use std::ffi::{CStr, OsStr, OsString};
use std::fs::{File, OpenOptions};
use std::path::Path;
use super::DirectoryVisit;
pub(super) fn open_directory_path(path: &Path) -> Option<File> {
use std::os::unix::fs::OpenOptionsExt as _;
let mut options = OpenOptions::new();
options
.read(true)
.custom_flags(libc::O_DIRECTORY | libc::O_CLOEXEC | libc::O_NOFOLLOW | libc::O_NONBLOCK);
options.open(path).ok()
}
pub(super) fn open_directory_relative(directory: &File, path: &Path) -> Option<File> {
let mut current = directory.try_clone().ok()?;
for component in path.components() {
let std::path::Component::Normal(name) = component else {
continue;
};
current = openat_component(
&current,
name,
libc::O_RDONLY
| libc::O_DIRECTORY
| libc::O_CLOEXEC
| libc::O_NOFOLLOW
| libc::O_NONBLOCK,
)?;
}
Some(current)
}
pub(super) fn open_regular_relative(directory: &File, path: &Path) -> Option<File> {
let parent = path.parent().unwrap_or_else(|| Path::new(""));
let directory = open_directory_relative(directory, parent)?;
openat_component(
&directory,
path.file_name()?,
libc::O_RDONLY | libc::O_CLOEXEC | libc::O_NOFOLLOW | libc::O_NONBLOCK,
)
}
fn openat_component(directory: &File, name: &OsStr, flags: i32) -> Option<File> {
use std::os::fd::{AsRawFd as _, FromRawFd as _};
use std::os::unix::ffi::OsStrExt as _;
let name = std::ffi::CString::new(name.as_bytes()).ok()?;
// SAFETY: the directory fd and NUL-terminated child name are valid; no
// creation mode argument is required because the flags never create.
let fd = unsafe { libc::openat(directory.as_raw_fd(), name.as_ptr(), flags) };
// SAFETY: a nonnegative `openat` result transfers one owned fd.
(fd >= 0).then(|| unsafe { File::from_raw_fd(fd) })
}
struct DirectoryStream(*mut libc::DIR);
impl DirectoryStream {
fn open(directory: &File) -> Option<Self> {
use std::os::fd::AsRawFd as _;
let dot = c".";
// SAFETY: opening "." relative to a live directory fd returns an
// independent directory description for `fdopendir` to own.
let fd = unsafe {
libc::openat(
directory.as_raw_fd(),
dot.as_ptr(),
libc::O_RDONLY
| libc::O_DIRECTORY
| libc::O_CLOEXEC
| libc::O_NOFOLLOW
| libc::O_NONBLOCK,
)
};
if fd < 0 {
return None;
}
// SAFETY: `fd` is an owned directory fd and ownership passes to DIR.
let stream = unsafe { libc::fdopendir(fd) };
if stream.is_null() {
// SAFETY: ownership did not transfer when `fdopendir` failed.
unsafe {
libc::close(fd);
}
return None;
}
Some(Self(stream))
}
fn next(&mut self) -> Result<Option<OsString>, ()> {
use std::os::unix::ffi::OsStringExt as _;
loop {
set_errno(0);
// SAFETY: the stream remains owned and live until close/drop.
let entry = unsafe { libc::readdir(self.0) };
if entry.is_null() {
return if errno() == 0 { Ok(None) } else { Err(()) };
}
// SAFETY: POSIX guarantees a NUL-terminated d_name for this entry.
let name = unsafe { CStr::from_ptr((*entry).d_name.as_ptr()) }.to_bytes();
if name != b"." && name != b".." {
return Ok(Some(OsString::from_vec(name.to_vec())));
}
}
}
fn close(mut self) -> bool {
let stream = std::mem::replace(&mut self.0, std::ptr::null_mut());
// SAFETY: this is the one explicit close for the owned DIR stream.
unsafe { libc::closedir(stream) == 0 }
}
}
impl Drop for DirectoryStream {
fn drop(&mut self) {
if !self.0.is_null() {
// SAFETY: Drop owns the stream only when explicit close did not.
unsafe {
libc::closedir(self.0);
}
}
}
}
pub(super) fn visit_directory_names(directory: &File, mut visit: impl FnMut(OsString)) -> bool {
let Some(mut stream) = DirectoryStream::open(directory) else {
return false;
};
let complete = loop {
match stream.next() {
Ok(Some(name)) => visit(name),
Ok(None) => break true,
Err(()) => break false,
}
};
stream.close() && complete
}
pub(super) fn visit_directory_names_bounded(
directory: &File,
max_entries: usize,
mut visit: impl FnMut(OsString),
) -> DirectoryVisit {
let Some(mut stream) = DirectoryStream::open(directory) else {
return DirectoryVisit {
visited: 0,
complete: false,
};
};
let mut visited = 0;
let complete = loop {
if visited == max_entries {
break matches!(stream.next(), Ok(None));
}
match stream.next() {
Ok(Some(name)) => {
visited += 1;
visit(name);
}
Ok(None) => break true,
Err(()) => break false,
}
};
DirectoryVisit {
visited,
complete: stream.close() && complete,
}
}
#[cfg(any(target_os = "linux", target_os = "dragonfly"))]
fn errno() -> i32 {
// SAFETY: libc exposes one thread-local errno cell for the current thread.
unsafe { *libc::__errno_location() }
}
#[cfg(any(target_os = "linux", target_os = "dragonfly"))]
fn set_errno(value: i32) {
// SAFETY: libc exposes one thread-local errno cell for the current thread.
unsafe {
*libc::__errno_location() = value;
}
}
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
fn errno() -> i32 {
// SAFETY: libc exposes one thread-local errno cell for the current thread.
unsafe { *libc::__error() }
}
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
fn set_errno(value: i32) {
// SAFETY: libc exposes one thread-local errno cell for the current thread.
unsafe {
*libc::__error() = value;
}
}
#[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))]
fn errno() -> i32 {
// SAFETY: libc exposes one thread-local errno cell for the current thread.
unsafe { *libc::__errno() }
}
#[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))]
fn set_errno(value: i32) {
// SAFETY: libc exposes one thread-local errno cell for the current thread.
unsafe {
*libc::__errno() = value;
}
}
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)))]
fn errno() -> i32 {
1
}
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)))]
fn set_errno(_value: i32) {}
@@ -0,0 +1,166 @@
use std::ffi::OsString;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
pub(super) fn open_directory_path(path: &Path) -> Option<File> {
use std::os::windows::fs::OpenOptionsExt as _;
const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x0200_0000;
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
let mut options = OpenOptions::new();
options
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
options.open(path).ok()
}
pub(super) fn open_regular_path(path: &Path) -> Option<File> {
use std::os::windows::fs::OpenOptionsExt as _;
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
let mut options = OpenOptions::new();
options
.read(true)
.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);
options.open(path).ok()
}
pub(super) fn directory_path_matches(path: &Path, expected: &File) -> bool {
let Some(opened) = open_directory_path(path) else {
return false;
};
same_open_file(expected, &opened) && final_handle_path_matches(path, expected)
}
pub(super) fn canonical_file_matches(path: &Path, expected: &File) -> bool {
let Ok(canonical) = dunce::canonicalize(path) else {
return false;
};
if canonical != path {
return false;
}
let Some(opened) = open_regular_path(path) else {
return false;
};
same_open_file(expected, &opened) && final_handle_path_matches(path, expected)
}
pub(super) fn same_open_file(expected: &File, opened: &File) -> bool {
file_identity(expected)
.is_some_and(|expected| file_identity(opened).is_some_and(|opened| opened == expected))
}
pub(super) fn final_handle_path_matches(path: &Path, file: &File) -> bool {
use std::os::windows::io::AsRawHandle as _;
final_path_from_raw_handle(file.as_raw_handle().cast()).is_some_and(|handle_path| {
dunce::canonicalize(path).is_ok_and(|path| path == dunce::simplified(&handle_path))
})
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct FileIdentity {
volume_serial_number: u32,
file_index: u64,
}
#[repr(C)]
#[allow(dead_code)]
struct FileTime {
low: u32,
high: u32,
}
#[repr(C)]
#[allow(dead_code)]
struct ByHandleFileInformation {
file_attributes: u32,
creation_time: FileTime,
last_access_time: FileTime,
last_write_time: FileTime,
volume_serial_number: u32,
file_size_high: u32,
file_size_low: u32,
number_of_links: u32,
file_index_high: u32,
file_index_low: u32,
}
fn file_identity(file: &File) -> Option<FileIdentity> {
use std::os::windows::io::AsRawHandle as _;
raw_handle_identity(file.as_raw_handle().cast())
}
fn raw_handle_identity(handle: *mut std::ffi::c_void) -> Option<FileIdentity> {
#[link(name = "kernel32")]
unsafe extern "system" {
#[link_name = "GetFileInformationByHandle"]
fn get_file_information_by_handle(
file: *mut std::ffi::c_void,
information: *mut ByHandleFileInformation,
) -> i32;
}
let mut information = std::mem::MaybeUninit::uninit();
// SAFETY: the borrowed handle is live and the output has the exact
// BY_HANDLE_FILE_INFORMATION layout.
let result = unsafe { get_file_information_by_handle(handle, information.as_mut_ptr()) };
if result == 0 {
return None;
}
// SAFETY: a nonzero result initializes the entire output structure.
let information = unsafe { information.assume_init() };
Some(FileIdentity {
volume_serial_number: information.volume_serial_number,
file_index: (u64::from(information.file_index_high) << 32)
| u64::from(information.file_index_low),
})
}
fn final_path_from_raw_handle(handle: *mut std::ffi::c_void) -> Option<PathBuf> {
use std::os::windows::ffi::OsStringExt as _;
#[link(name = "kernel32")]
unsafe extern "system" {
#[link_name = "GetFinalPathNameByHandleW"]
fn get_final_path_name_by_handle(
file: *mut std::ffi::c_void,
path: *mut u16,
path_len: u32,
flags: u32,
) -> u32;
}
// SAFETY: a null output with length zero is the documented size query.
let needed = unsafe { get_final_path_name_by_handle(handle, std::ptr::null_mut(), 0, 0) };
if needed == 0 {
return None;
}
let mut buffer = vec![0_u16; needed as usize + 1];
// SAFETY: `buffer` has the advertised writable UTF-16 capacity.
let written = unsafe {
get_final_path_name_by_handle(
handle,
buffer.as_mut_ptr(),
u32::try_from(buffer.len()).unwrap_or(u32::MAX),
0,
)
};
if written == 0 || written as usize >= buffer.len() {
return None;
}
Some(PathBuf::from(OsString::from_wide(
&buffer[..written as usize],
)))
}
#[cfg(test)]
pub(super) fn same_file_for_test(expected: &File, opened: &File) -> bool {
same_open_file(expected, opened)
}
#[cfg(test)]
pub(super) fn final_path_matches_for_test(path: &Path, file: &File) -> bool {
final_handle_path_matches(path, file)
}
@@ -0,0 +1,497 @@
use std::collections::HashSet;
use std::io::{Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use serde_json::Value;
use super::{
ApprovedRoot, ForeignSessionSource, ForeignSessionSummary, ForeignSessionTool, MAX_SESSION_AGE,
MAX_SESSIONS_PER_TOOL, RecentCandidate, RecentProbe, approved_root_for_recent,
finish_tool_scan, is_within, normalize_title, retain_top_k_by,
};
mod projects;
const READ_CHUNK: usize = 64 * 1024;
const MAX_HEAD: usize = 4 * 1024 * 1024;
const MAX_CONTENT_READS: usize = 128;
const MAX_RECENT_CONTENT_READS: usize = 16;
const MAX_RECENT_DIRECTORY_ENTRIES: usize = 64;
struct Candidate {
root: ApprovedRoot,
path: PathBuf,
session_id: String,
modified: SystemTime,
size: u64,
}
pub(super) fn scan(cwd: &Path, now: SystemTime) -> Vec<ForeignSessionSummary> {
let Some(config_dir) = std::env::var_os("CLAUDE_CONFIG_DIR")
.map(PathBuf::from)
.or_else(|| dirs::home_dir().map(|home| home.join(".claude")))
else {
return Vec::new();
};
scan_in_config_dir(&config_dir, cwd, now)
}
pub(super) fn most_recent(
cwd: &Path,
now: SystemTime,
within: Duration,
) -> RecentProbe<RecentCandidate> {
let Some(config_dir) = std::env::var_os("CLAUDE_CONFIG_DIR")
.map(PathBuf::from)
.or_else(|| dirs::home_dir().map(|home| home.join(".claude")))
else {
return RecentProbe::Complete(None);
};
most_recent_in_config_dir(&config_dir, cwd, now, within)
}
fn scan_in_config_dir(
config_dir: &Path,
cwd: &Path,
now: SystemTime,
) -> Vec<ForeignSessionSummary> {
let Some(root) = ApprovedRoot::new(config_dir) else {
return Vec::new();
};
let project_dirs = projects::scoped_project_dirs(root.path(), cwd);
scan_project_dirs(&root, &project_dirs, cwd, now)
}
fn most_recent_in_config_dir(
config_dir: &Path,
cwd: &Path,
now: SystemTime,
within: Duration,
) -> RecentProbe<RecentCandidate> {
let root = match approved_root_for_recent(config_dir) {
Ok(Some(root)) => root,
Ok(None) => return RecentProbe::Complete(None),
Err(()) => return RecentProbe::Incomplete,
};
let Some(project_dir) = projects::project_dir_path(root.path(), cwd) else {
return RecentProbe::Complete(None);
};
match std::fs::symlink_metadata(&project_dir) {
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return RecentProbe::Complete(None);
}
Err(_) => return RecentProbe::Incomplete,
Ok(metadata) if project_directory_is_safe(&metadata) => {}
Ok(_) => return RecentProbe::Incomplete,
}
#[cfg(unix)]
let collected = root
.subroot(&project_dir)
.and_then(|project_root| collect_recent_candidates(&project_root, now, within));
#[cfg(windows)]
let collected = collect_recent_candidates_windows(&root, &project_dir, now, within);
#[cfg(not(any(unix, windows)))]
let collected = None;
let Some((candidates, truncated)) = collected else {
return RecentProbe::Incomplete;
};
finish_recent_candidates(candidates, truncated, cwd)
}
fn finish_recent_candidates(
candidates: Vec<Candidate>,
truncated: bool,
cwd: &Path,
) -> RecentProbe<RecentCandidate> {
let candidate = candidates.into_iter().find_map(|candidate| {
qualify_candidate(&candidate, cwd)?;
Some(RecentCandidate {
tool: ForeignSessionTool::Claude,
source: ForeignSessionSource::ClaudeCode,
native_id: candidate.session_id,
updated_at: candidate.modified,
})
});
if candidate.is_none() && truncated {
RecentProbe::Incomplete
} else {
RecentProbe::Complete(candidate)
}
}
fn project_directory_is_safe(metadata: &std::fs::Metadata) -> bool {
if !metadata.is_dir() || metadata.file_type().is_symlink() {
return false;
}
#[cfg(windows)]
{
use std::os::windows::fs::MetadataExt as _;
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x400;
if metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
return false;
}
}
true
}
fn collect_recent_candidates(
project_root: &ApprovedRoot,
now: SystemTime,
within: Duration,
) -> Option<(Vec<Candidate>, bool)> {
let mut candidates = Vec::with_capacity(MAX_RECENT_CONTENT_READS);
let mut qualifying = 0;
let outcome = project_root.for_each_entry_bounded(MAX_RECENT_DIRECTORY_ENTRIES, |name| {
let path = project_root.join(&name);
let Some(session_id) = path
.file_name()
.and_then(|name| name.to_str())
.and_then(|name| name.strip_suffix(".jsonl"))
.filter(|id| uuid::Uuid::try_parse(id).is_ok())
else {
return;
};
let Some((path, metadata)) = project_root.resolve_regular_file(&path) else {
return;
};
if metadata.len() == 0 {
return;
}
let Ok(modified) = metadata.modified() else {
return;
};
if is_within(modified, now, within) {
qualifying += 1;
retain_top_k_by(
&mut candidates,
Candidate {
root: project_root.clone(),
path,
session_id: session_id.to_owned(),
modified,
size: metadata.len(),
},
MAX_RECENT_CONTENT_READS,
candidate_order,
);
}
});
outcome
.complete
.then_some((candidates, qualifying > MAX_RECENT_CONTENT_READS))
}
#[cfg(windows)]
fn collect_recent_candidates_windows(
config_root: &ApprovedRoot,
project_dir: &Path,
now: SystemTime,
within: Duration,
) -> Option<(Vec<Candidate>, bool)> {
let canonical_project = dunce::canonicalize(project_dir).ok()?;
if canonical_project.as_path() != project_dir
|| !canonical_project.starts_with(config_root.path())
{
return None;
}
let project_root = ApprovedRoot::new(&canonical_project)?;
if project_root.path() != canonical_project.as_path() {
return None;
}
let mut entries = std::fs::read_dir(&canonical_project).ok()?;
let mut candidates = Vec::with_capacity(MAX_RECENT_CONTENT_READS);
let mut qualifying = 0;
for _ in 0..MAX_RECENT_DIRECTORY_ENTRIES {
let Some(entry) = entries.next() else {
return Some((candidates, qualifying > MAX_RECENT_CONTENT_READS));
};
let entry = entry.ok()?;
let path = entry.path();
let Some(session_id) = path
.file_name()
.and_then(|name| name.to_str())
.and_then(|name| name.strip_suffix(".jsonl"))
.filter(|id| uuid::Uuid::try_parse(id).is_ok())
else {
continue;
};
let opened = project_root.open_regular_file(&path)?;
if opened.metadata.len() == 0 {
continue;
}
let modified = opened.metadata.modified().ok()?;
if is_within(modified, now, within) {
qualifying += 1;
retain_top_k_by(
&mut candidates,
Candidate {
root: project_root.clone(),
path: opened.path,
session_id: session_id.to_owned(),
modified,
size: opened.metadata.len(),
},
MAX_RECENT_CONTENT_READS,
candidate_order,
);
}
}
match entries.next() {
None => Some((candidates, qualifying > MAX_RECENT_CONTENT_READS)),
Some(_) => None,
}
}
fn scan_project_dirs(
root: &ApprovedRoot,
project_dirs: &[PathBuf],
cwd: &Path,
now: SystemTime,
) -> Vec<ForeignSessionSummary> {
let candidates =
collect_candidates(root, project_dirs, now, MAX_SESSION_AGE, MAX_CONTENT_READS);
let mut accepted_ids = HashSet::new();
let mut sessions = Vec::new();
for candidate in candidates {
if accepted_ids.contains(&candidate.session_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;
}
}
finish_tool_scan(sessions)
}
fn collect_candidates(
root: &ApprovedRoot,
project_dirs: &[PathBuf],
now: SystemTime,
within: Duration,
limit: usize,
) -> Vec<Candidate> {
let mut candidates = Vec::with_capacity(limit);
for project_dir in project_dirs.iter().take(projects::MAX_PROJECT_DIRS) {
let Some(project_root) = root.subroot(project_dir) else {
continue;
};
let mut project_candidates = Vec::with_capacity(limit);
// Enumerate every direct entry in these already-scoped directories so
// filesystem order cannot decide which sessions receive the read budget.
let complete = project_root.for_each_entry(|name| {
let path = project_root.join(&name);
let Some(session_id) = path
.file_name()
.and_then(|name| name.to_str())
.and_then(|name| name.strip_suffix(".jsonl"))
.filter(|id| uuid::Uuid::try_parse(id).is_ok())
else {
return;
};
let Some((path, metadata)) = project_root.resolve_regular_file(&path) else {
return;
};
if !metadata.is_file() || metadata.len() == 0 {
return;
}
let Ok(modified) = metadata.modified() else {
return;
};
if !is_within(modified, now, within) {
return;
}
retain_top_k_by(
&mut project_candidates,
Candidate {
root: project_root.clone(),
path,
session_id: session_id.to_owned(),
modified,
size: metadata.len(),
},
limit,
candidate_order,
);
});
if !complete {
continue;
}
for candidate in project_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.session_id.cmp(&b.session_id))
.then_with(|| a.path.cmp(&b.path))
}
fn read_candidate(candidate: Candidate, requested_cwd: &Path) -> Option<ForeignSessionSummary> {
let (head, stored_cwd) = qualify_candidate(&candidate, requested_cwd)?;
let tail = read_tail(&candidate.root, &candidate.path, candidate.size)?;
let first_prompt = first_prompt(&head);
let title = [
last_string(&tail, "customTitle").or_else(|| last_string(&head, "customTitle")),
last_string(&tail, "aiTitle").or_else(|| last_string(&head, "aiTitle")),
last_string(&tail, "lastPrompt").or_else(|| last_string(&head, "lastPrompt")),
last_string(&tail, "summary").or_else(|| last_string(&head, "summary")),
first_prompt,
]
.into_iter()
.flatten()
.find_map(|value| normalize_title(&value))?;
let branch = last_string(&tail, "gitBranch")
.or_else(|| last_string(&head, "gitBranch"))
.and_then(|value| normalize_title(&value));
Some(ForeignSessionSummary {
tool: ForeignSessionTool::Claude,
source: ForeignSessionSource::ClaudeCode,
native_id: candidate.session_id,
title,
cwd: PathBuf::from(stored_cwd),
updated_at: candidate.modified,
branch,
})
}
fn qualify_candidate(candidate: &Candidate, requested_cwd: &Path) -> Option<(String, String)> {
let (head, stored_cwd) = read_head_for_cwd(&candidate.root, &candidate.path, candidate.size)?;
let first_line = head.lines().next().unwrap_or(&head);
if first_line.contains("\"isSidechain\":true") || first_line.contains("\"isSidechain\": true") {
return None;
}
let stored_cwd = stored_cwd?;
if Path::new(&stored_cwd) != requested_cwd {
return None;
}
Some((head, stored_cwd))
}
fn read_head_for_cwd(
root: &ApprovedRoot,
path: &Path,
size: u64,
) -> Option<(String, Option<String>)> {
let max = usize::try_from(size.min(MAX_HEAD as u64)).ok()?;
let mut limit = READ_CHUNK.min(max);
loop {
let head = read_prefix(root, path, limit)?;
let cwd = first_string(&head, "cwd");
if cwd.is_some() || limit >= max {
return Some((head, cwd));
}
limit = limit.saturating_mul(4).min(max);
}
}
fn read_prefix(root: &ApprovedRoot, path: &Path, limit: usize) -> Option<String> {
let file = root.open_regular_file(path)?.file;
let mut bytes = Vec::with_capacity(limit);
file.take(limit as u64).read_to_end(&mut bytes).ok()?;
Some(String::from_utf8_lossy(&bytes).into_owned())
}
fn read_tail(root: &ApprovedRoot, path: &Path, size: u64) -> Option<String> {
let len = size.min(READ_CHUNK as u64);
let mut file = root.open_regular_file(path)?.file;
file.seek(SeekFrom::Start(size.saturating_sub(len))).ok()?;
let mut bytes = Vec::with_capacity(len as usize);
file.take(len).read_to_end(&mut bytes).ok()?;
Some(String::from_utf8_lossy(&bytes).into_owned())
}
fn first_string(text: &str, key: &str) -> Option<String> {
text.lines().find_map(|line| string_field(line, key))
}
fn last_string(text: &str, key: &str) -> Option<String> {
text.lines().rev().find_map(|line| string_field(line, key))
}
fn string_field(line: &str, key: &str) -> Option<String> {
let value: Value = serde_json::from_str(line).ok()?;
value.get(key)?.as_str().map(str::to_owned)
}
fn first_prompt(head: &str) -> Option<String> {
let mut command_fallback = None;
for line in head.lines() {
if line.contains("\"tool_result\"") {
continue;
}
let Ok(entry) = serde_json::from_str::<Value>(line) else {
continue;
};
if entry.get("type").and_then(Value::as_str) != Some("user")
|| entry.get("isMeta").and_then(Value::as_bool) == Some(true)
|| entry.get("isCompactSummary").and_then(Value::as_bool) == Some(true)
{
continue;
}
let Some(content) = entry.pointer("/message/content") else {
continue;
};
let texts = match content {
Value::String(text) => vec![text.as_str()],
Value::Array(blocks) => blocks
.iter()
.filter(|block| block.get("type").and_then(Value::as_str) == Some("text"))
.filter_map(|block| block.get("text").and_then(Value::as_str))
.collect(),
_ => Vec::new(),
};
for text in texts {
let normalized = text.split_whitespace().collect::<Vec<_>>().join(" ");
if normalized.is_empty() {
continue;
}
if let Some(command) = between(&normalized, "<command-name>", "</command-name>") {
command_fallback.get_or_insert_with(|| command.to_owned());
continue;
}
if let Some(command) = between(&normalized, "<bash-input>", "</bash-input>") {
return normalize_title(&format!("! {}", command.trim()));
}
if is_generated_prompt(&normalized) {
continue;
}
return normalize_title(&normalized);
}
}
command_fallback.and_then(|value| normalize_title(&value))
}
fn between<'a>(value: &'a str, start: &str, end: &str) -> Option<&'a str> {
let start = value.find(start)? + start.len();
let end = value[start..].find(end)? + start;
Some(&value[start..end])
}
fn is_generated_prompt(value: &str) -> bool {
if value.starts_with("[Request interrupted by user") {
return true;
}
let value = value.trim_start();
value
.strip_prefix('<')
.and_then(|rest| rest.chars().next())
.is_some_and(|first| first.is_ascii_lowercase())
}
#[cfg(all(test, unix))]
mod tests;
#[cfg(all(test, windows))]
mod windows_tests;
@@ -0,0 +1,55 @@
use std::collections::HashSet;
use std::path::{Path, PathBuf};
pub(super) const MAX_PROJECT_DIRS: usize = 16;
pub(super) const MAX_SANITIZED_LENGTH: usize = 200;
pub(super) fn scoped_project_dirs(config_dir: &Path, cwd: &Path) -> Vec<PathBuf> {
let mut paths = vec![cwd.to_path_buf()];
if let Ok(repository) = git2::Repository::discover(cwd) {
if let Some(workdir) = repository.workdir() {
paths.push(dunce::canonicalize(workdir).unwrap_or_else(|_| workdir.to_path_buf()));
if repository.path() != repository.commondir()
&& let Some(main_workdir) = repository.commondir().parent()
{
paths.push(
dunce::canonicalize(main_workdir)
.unwrap_or_else(|_| main_workdir.to_path_buf()),
);
}
}
if let Ok(worktrees) = repository.worktrees() {
for name in worktrees.iter().flatten().flatten().take(MAX_PROJECT_DIRS) {
if let Ok(worktree) = repository.find_worktree(name) {
let path = worktree.path();
paths.push(dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf()));
}
}
}
}
let mut seen = HashSet::new();
paths
.into_iter()
.filter(|path| seen.insert(path.clone()))
.take(MAX_PROJECT_DIRS)
.filter_map(|path| project_dir_for_path(config_dir, &path))
.collect()
}
pub(super) fn project_dir_for_path(config_dir: &Path, path: &Path) -> Option<PathBuf> {
let project_dir = project_dir_path(config_dir, path)?;
let metadata = std::fs::symlink_metadata(&project_dir).ok()?;
(metadata.is_dir() && !metadata.file_type().is_symlink()).then_some(project_dir)
}
pub(super) fn project_dir_path(config_dir: &Path, path: &Path) -> Option<PathBuf> {
let sanitized = path
.to_string_lossy()
.chars()
.map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '-' })
.collect::<String>();
if sanitized.len() > MAX_SANITIZED_LENGTH {
return None;
}
Some(config_dir.join("projects").join(sanitized))
}
@@ -0,0 +1,411 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use filetime::FileTime;
use serde_json::json;
use tempfile::TempDir;
use super::*;
use crate::foreign_sessions::{MAX_SESSION_AGE, canonical_tempdir};
fn set_mtime(path: &Path, time: SystemTime) {
filetime::set_file_mtime(path, FileTime::from_system_time(time)).unwrap();
}
fn write_session(
project: &Path,
id: uuid::Uuid,
lines: &[serde_json::Value],
modified: SystemTime,
) {
fs::create_dir_all(project).unwrap();
let path = project.join(format!("{id}.jsonl"));
let contents = lines
.iter()
.map(serde_json::Value::to_string)
.collect::<Vec<_>>()
.join("\n");
fs::write(&path, format!("{contents}\n")).unwrap();
set_mtime(&path, modified);
}
fn user(cwd: &Path, prompt: &str) -> serde_json::Value {
json!({
"type": "user",
"cwd": cwd,
"message": {"role": "user", "content": prompt}
})
}
fn scoped_project(root: &Path, cwd: &Path) -> std::path::PathBuf {
let name = cwd
.to_string_lossy()
.chars()
.map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '-' })
.collect::<String>();
let path = root.join("projects").join(name);
fs::create_dir_all(&path).unwrap();
path
}
#[test]
fn recent_probe_skips_sidechains_and_wrong_cwds_before_winner() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let project = scoped_project(root.path(), &cwd);
let now = UNIX_EPOCH + Duration::from_secs(3_000_000);
write_session(
&project,
uuid::Uuid::from_u128(10),
&[json!({
"type": "user",
"cwd": cwd,
"isSidechain": true,
"message": {"content": "sidechain"}
})],
now,
);
write_session(
&project,
uuid::Uuid::from_u128(11),
&[user(Path::new("/other"), "wrong cwd")],
now - Duration::from_secs(1),
);
let winner = uuid::Uuid::from_u128(12);
write_session(
&project,
winner,
&[user(&cwd, "winner")],
now - Duration::from_secs(2),
);
let found =
most_recent_in_config_dir(root.path(), &cwd, now, Duration::from_secs(600)).unwrap();
assert_eq!(found.native_id, winner.to_string());
assert_eq!(found.source, ForeignSessionSource::ClaudeCode);
}
#[test]
fn recent_probe_bounds_content_reads() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let project = scoped_project(root.path(), &cwd);
let now = UNIX_EPOCH + Duration::from_secs(3_100_000);
for i in 0..MAX_RECENT_CONTENT_READS {
write_session(
&project,
uuid::Uuid::from_u128(100 + i as u128),
&[user(Path::new("/other"), "wrong cwd")],
now - Duration::from_secs(i as u64),
);
}
write_session(
&project,
uuid::Uuid::from_u128(999),
&[user(&cwd, "outside read budget")],
now - Duration::from_secs(MAX_RECENT_CONTENT_READS as u64),
);
assert_eq!(
most_recent_in_config_dir(root.path(), &cwd, now, Duration::from_secs(600)),
RecentProbe::Incomplete,
);
}
#[test]
fn recent_probe_fails_closed_at_directory_entry_cap() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let project = scoped_project(root.path(), &cwd);
let now = UNIX_EPOCH + Duration::from_secs(3_200_000);
write_session(
&project,
uuid::Uuid::from_u128(1_500),
&[user(&cwd, "must not win after incomplete discovery")],
now,
);
for index in 0..MAX_RECENT_DIRECTORY_ENTRIES {
fs::write(project.join(format!("junk-{index:03}")), "").unwrap();
}
assert_eq!(
most_recent_in_config_dir(root.path(), &cwd, now, Duration::from_secs(600)),
RecentProbe::Incomplete,
);
}
#[test]
fn filters_bad_rows_and_uses_newest_duplicate_with_title_precedence() {
let (_tempdir, root) = canonical_tempdir();
let cwd = root.join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(4_000_000);
let projects = root.join("projects");
let duplicate = uuid::Uuid::from_u128(1);
write_session(
&projects.join("old"),
duplicate,
&[
user(&cwd, "first prompt"),
json!({"type": "custom-title", "customTitle": "old title"}),
],
now - Duration::from_secs(20),
);
write_session(
&projects.join("new"),
duplicate,
&[
user(&cwd, "first prompt"),
json!({"summary": "summary"}),
json!({"lastPrompt": "last prompt"}),
json!({"aiTitle": "ai title"}),
json!({"customTitle": "custom title", "gitBranch": "feature"}),
],
now - Duration::from_secs(10),
);
write_session(
&projects.join("unreadable-newest"),
duplicate,
&[user(Path::new("/other"), "newest wrong cwd")],
now,
);
write_session(
&projects.join("sidechain"),
uuid::Uuid::from_u128(2),
&[json!({
"type": "user",
"cwd": cwd.display().to_string(),
"isSidechain": true,
"message": {"content": "hidden"}
})],
now,
);
write_session(
&projects.join("no-title"),
uuid::Uuid::from_u128(3),
&[json!({"type": "system", "cwd": cwd.display().to_string()})],
now,
);
write_session(
&projects.join("wrong-cwd"),
uuid::Uuid::from_u128(4),
&[user(Path::new("/other"), "wrong")],
now,
);
write_session(
&projects.join("stale"),
uuid::Uuid::from_u128(5),
&[user(&cwd, "stale")],
now - MAX_SESSION_AGE - Duration::from_secs(1),
);
let zero_dir = projects.join("zero");
fs::create_dir_all(&zero_dir).unwrap();
fs::write(
zero_dir.join(format!("{}.jsonl", uuid::Uuid::from_u128(6))),
"",
)
.unwrap();
fs::create_dir_all(projects.join("nested").join("subagents")).unwrap();
fs::write(
projects
.join("nested")
.join("subagents")
.join(format!("{}.jsonl", uuid::Uuid::from_u128(7))),
user(&cwd, "nested").to_string(),
)
.unwrap();
let project_dirs = fs::read_dir(&projects)
.unwrap()
.flatten()
.filter(|entry| entry.file_type().is_ok_and(|kind| kind.is_dir()))
.map(|entry| entry.path())
.collect::<Vec<_>>();
let approved_root = ApprovedRoot::new(&root).unwrap();
let sessions = scan_project_dirs(&approved_root, &project_dirs, &cwd, now);
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].native_id, duplicate.to_string());
assert_eq!(sessions[0].title, "custom title");
assert_eq!(sessions[0].branch.as_deref(), Some("feature"));
}
#[test]
fn bounds_results_and_orders_ties_by_id() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(5_000_000);
let project = scoped_project(root.path(), &cwd);
for i in 1..=55_u128 {
write_session(
&project,
uuid::Uuid::from_u128(i),
&[user(&cwd, &format!("prompt {i}"))],
now - Duration::from_secs((i / 2) as u64),
);
}
let sessions = scan_in_config_dir(root.path(), &cwd, now);
assert_eq!(sessions.len(), MAX_SESSIONS_PER_TOOL);
assert!(
sessions
.windows(2)
.all(|pair| pair[0].updated_at > pair[1].updated_at
|| (pair[0].updated_at == pair[1].updated_at
&& pair[0].native_id < pair[1].native_id))
);
}
#[test]
fn skips_meta_and_tool_noise_for_first_prompt() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(6_000_000);
write_session(
&scoped_project(root.path(), &cwd),
uuid::Uuid::from_u128(100),
&[
json!({
"type": "user",
"cwd": cwd.display().to_string(),
"isMeta": true,
"message": {"content": "meta"}
}),
json!({
"type": "user",
"message": {"content": [{"type": "tool_result", "content": "noise"}]}
}),
json!({
"type": "user",
"message": {"content": "<command-name>review</command-name>"}
}),
json!({
"type": "user",
"message": {"content": "real prompt"}
}),
],
now,
);
let sessions = scan_in_config_dir(root.path(), &cwd, now);
assert_eq!(sessions[0].title, "real prompt");
}
#[test]
fn scoped_scan_keeps_newest_over_budget_and_rejects_symlinks() {
let root = TempDir::new().unwrap();
let cwd = root.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(7_000_000);
let project = scoped_project(root.path(), &cwd);
for i in 0..520_u128 {
write_session(
&project,
uuid::Uuid::from_u128(1_000 + i),
&[user(Path::new("/other"), "wrong cwd")],
now - Duration::from_secs(i as u64 + 1),
);
}
let valid = uuid::Uuid::from_u128(2_000);
write_session(
&project,
valid,
&[user(&cwd, "valid after invalid rows")],
now,
);
write_session(
&project,
uuid::Uuid::from_u128(2_003),
&[user(&cwd, "arbitrary future mtime")],
now + Duration::from_secs(24 * 60 * 60),
);
write_session(
&root.path().join("projects").join("unrelated"),
uuid::Uuid::from_u128(2_001),
&[user(&cwd, "must not scan unrelated project")],
now,
);
#[cfg(unix)]
{
let outside = root.path().join("outside.jsonl");
fs::write(&outside, user(&cwd, "symlink escape").to_string()).unwrap();
std::os::unix::fs::symlink(
outside,
project.join(format!("{}.jsonl", uuid::Uuid::from_u128(2_002))),
)
.unwrap();
}
let sessions = scan_in_config_dir(root.path(), &cwd, now);
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].native_id, valid.to_string());
assert_eq!(sessions[0].title, "valid after invalid rows");
let long = PathBuf::from(format!(
"/{}",
"a".repeat(projects::MAX_SANITIZED_LENGTH + 1)
));
assert_eq!(projects::project_dir_for_path(root.path(), &long), None);
}
#[cfg(unix)]
#[test]
fn rejects_project_parent_symlink_escape() {
let config = TempDir::new().unwrap();
let outside = TempDir::new().unwrap();
let cwd = config.path().join("repo");
fs::create_dir_all(&cwd).unwrap();
let now = UNIX_EPOCH + Duration::from_secs(7_500_000);
let project = scoped_project(outside.path(), &cwd);
write_session(
&project,
uuid::Uuid::from_u128(2_100),
&[user(&cwd, "outside parent")],
now,
);
std::os::unix::fs::symlink(
outside.path().join("projects"),
config.path().join("projects"),
)
.unwrap();
assert!(scan_in_config_dir(config.path(), &cwd, now).is_empty());
}
#[test]
fn linked_worktree_scope_includes_main_and_siblings() {
let root = TempDir::new().unwrap();
let main = root.path().join("main");
fs::create_dir_all(&main).unwrap();
let repository = git2::Repository::init(&main).unwrap();
let signature = git2::Signature::now("test", "test@example.com").unwrap();
let tree = {
let mut index = repository.index().unwrap();
let oid = index.write_tree().unwrap();
repository.find_tree(oid).unwrap()
};
repository
.commit(Some("HEAD"), &signature, &signature, "init", &tree, &[])
.unwrap();
drop(tree);
let linked = root.path().join("linked");
let sibling = root.path().join("sibling");
repository.worktree("linked", &linked, None).unwrap();
repository.worktree("sibling", &sibling, None).unwrap();
let main_project = scoped_project(root.path(), &dunce::canonicalize(&main).unwrap());
let linked_project = scoped_project(root.path(), &dunce::canonicalize(&linked).unwrap());
let sibling_project = scoped_project(root.path(), &dunce::canonicalize(&sibling).unwrap());
let project_dirs =
projects::scoped_project_dirs(root.path(), &dunce::canonicalize(&linked).unwrap());
assert!(project_dirs.contains(&main_project));
assert!(project_dirs.contains(&linked_project));
assert!(project_dirs.contains(&sibling_project));
}
@@ -0,0 +1,121 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use filetime::FileTime;
use serde_json::json;
use tempfile::TempDir;
use super::*;
struct Fixture {
_root: TempDir,
config: PathBuf,
cwd: PathBuf,
project: PathBuf,
}
fn fixture() -> Fixture {
let root = TempDir::new().unwrap();
let config = root.path().join("config");
let cwd = root.path().join("repo");
fs::create_dir_all(&config).unwrap();
fs::create_dir_all(&cwd).unwrap();
let config = dunce::canonicalize(config).unwrap();
let cwd = dunce::canonicalize(cwd).unwrap();
let project = projects::project_dir_path(&config, &cwd).unwrap();
Fixture {
_root: root,
config,
cwd,
project,
}
}
fn write_session(project: &Path, cwd: &Path, id: uuid::Uuid, modified: SystemTime) {
fs::create_dir_all(project).unwrap();
let path = project.join(format!("{id}.jsonl"));
fs::write(
&path,
format!(
"{}\n",
json!({
"type": "user",
"cwd": cwd,
"message": {"role": "user", "content": "recent work"},
})
),
)
.unwrap();
filetime::set_file_mtime(&path, FileTime::from_system_time(modified)).unwrap();
}
#[test]
fn recent_probe_finds_valid_session() {
let fixture = fixture();
let now = SystemTime::now();
let id = uuid::Uuid::from_u128(1);
write_session(&fixture.project, &fixture.cwd, id, now);
assert_eq!(
most_recent_in_config_dir(&fixture.config, &fixture.cwd, now, Duration::from_secs(600),)
.unwrap()
.native_id,
id.to_string(),
);
}
#[test]
fn recent_probe_missing_project_is_complete_empty() {
let fixture = fixture();
assert_eq!(
most_recent_in_config_dir(
&fixture.config,
&fixture.cwd,
SystemTime::now(),
Duration::from_secs(600),
),
RecentProbe::Complete(None),
);
}
#[test]
fn recent_probe_directory_limit_is_incomplete() {
let fixture = fixture();
let now = SystemTime::now();
write_session(
&fixture.project,
&fixture.cwd,
uuid::Uuid::from_u128(2),
now,
);
for index in 0..MAX_RECENT_DIRECTORY_ENTRIES {
fs::write(fixture.project.join(format!("junk-{index:03}")), "").unwrap();
}
assert_eq!(
most_recent_in_config_dir(&fixture.config, &fixture.cwd, now, Duration::from_secs(600),),
RecentProbe::Incomplete,
);
}
#[test]
fn recent_probe_rejects_reparse_project_directory_when_supported() {
let fixture = fixture();
let outside = fixture._root.path().join("outside-project");
fs::create_dir_all(&outside).unwrap();
fs::create_dir_all(fixture.project.parent().unwrap()).unwrap();
if std::os::windows::fs::symlink_dir(&outside, &fixture.project).is_err() {
return;
}
assert_eq!(
most_recent_in_config_dir(
&fixture.config,
&fixture.cwd,
SystemTime::now(),
Duration::from_secs(600),
),
RecentProbe::Incomplete,
);
}
@@ -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)
}
}
@@ -0,0 +1,773 @@
//! Bounded, metadata-only listing of foreign coding-agent sessions.
//! Foreign SQLite stores are opened only when `kigi_sqlite_journal::JournalMode`
//! selects local WAL. The direct read-only/query-only transaction makes no
//! logical writes, though WAL coordination may update shared-memory read marks.
//! Network filesystems fail soft before SQLite open, conversion, or writes.
use std::cmp::Ordering;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
mod capability;
mod claude;
mod codex;
use capability::{ApprovedRoot, open_sqlite_transaction};
pub const MAX_SESSIONS_PER_TOOL: usize = 50;
pub const MAX_SESSION_AGE: Duration = Duration::from_secs(30 * 24 * 60 * 60);
pub const MAX_TITLE_CHARS: usize = 200;
const MAX_FUTURE_SKEW: Duration = Duration::from_secs(5 * 60);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ForeignSessionTool {
Claude,
Codex,
Cursor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ForeignSessionSource {
ClaudeCode,
CodexCli,
CodexVsCode,
CodexAtlas,
CodexChatGpt,
CursorDesktop,
CursorCli,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForeignSessionSummary {
pub tool: ForeignSessionTool,
pub source: ForeignSessionSource,
pub native_id: String,
pub title: String,
pub cwd: PathBuf,
pub updated_at: SystemTime,
pub branch: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecentForeignSession {
pub tool: ForeignSessionTool,
pub native_id: String,
pub age: Duration,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct RecentCandidate {
tool: ForeignSessionTool,
source: ForeignSessionSource,
native_id: String,
updated_at: SystemTime,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum RecentProbe<T> {
Complete(Option<T>),
Incomplete,
}
fn approved_root_for_recent(path: &Path) -> Result<Option<ApprovedRoot>, ()> {
match std::fs::symlink_metadata(path) {
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(_) => Err(()),
Ok(_) => ApprovedRoot::new(path).map(Some).ok_or(()),
}
}
#[cfg(test)]
impl<T> RecentProbe<T> {
fn unwrap(self) -> T {
match self {
Self::Complete(Some(value)) => value,
Self::Complete(None) => {
panic!("called RecentProbe::unwrap on complete-empty probe")
}
Self::Incomplete => panic!("called RecentProbe::unwrap on incomplete probe"),
}
}
fn is_none(&self) -> bool {
matches!(self, Self::Complete(None))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct EnabledForeignSessionSources {
pub claude: bool,
pub codex: bool,
pub cursor: bool,
}
pub fn scan_foreign_sessions(
cwd: &Path,
enabled: EnabledForeignSessionSources,
) -> Vec<ForeignSessionSummary> {
let scan_cursor = |_: &Path, _: SystemTime| Vec::new();
scan_with(cwd, enabled, claude::scan, codex::scan, scan_cursor)
}
pub fn most_recent_foreign_session(
cwd: &Path,
enabled: EnabledForeignSessionSources,
within: Duration,
) -> Option<RecentForeignSession> {
let recent_cursor = |_: &Path, _: SystemTime, _: Duration| RecentProbe::Complete(None);
match most_recent_with(
cwd,
enabled,
within,
SystemTime::now(),
claude::most_recent,
codex::most_recent,
recent_cursor,
) {
RecentProbe::Complete(session) => session,
RecentProbe::Incomplete => None,
}
}
fn most_recent_with<Claude, Codex, Cursor>(
cwd: &Path,
enabled: EnabledForeignSessionSources,
within: Duration,
now: SystemTime,
recent_claude: Claude,
recent_codex: Codex,
recent_cursor: Cursor,
) -> RecentProbe<RecentForeignSession>
where
Claude: FnOnce(&Path, SystemTime, Duration) -> RecentProbe<RecentCandidate>,
Codex: FnOnce(&Path, SystemTime, Duration) -> RecentProbe<RecentCandidate>,
Cursor: FnOnce(&Path, SystemTime, Duration) -> RecentProbe<RecentCandidate>,
{
if !enabled.claude && !enabled.codex && !enabled.cursor {
return RecentProbe::Complete(None);
}
let Ok(cwd) = dunce::canonicalize(cwd) else {
return RecentProbe::Complete(None);
};
let mut candidates = Vec::with_capacity(3);
if enabled.claude {
match recent_claude(&cwd, now, within) {
RecentProbe::Complete(candidate) => candidates.extend(candidate),
RecentProbe::Incomplete => return RecentProbe::Incomplete,
}
}
if enabled.codex {
match recent_codex(&cwd, now, within) {
RecentProbe::Complete(candidate) => candidates.extend(candidate),
RecentProbe::Incomplete => return RecentProbe::Incomplete,
}
}
if enabled.cursor {
match recent_cursor(&cwd, now, within) {
RecentProbe::Complete(candidate) => candidates.extend(candidate),
RecentProbe::Incomplete => return RecentProbe::Incomplete,
}
}
let winner = candidates
.into_iter()
.filter(|candidate| is_within(candidate.updated_at, now, within))
.min_by(recent_candidate_order);
RecentProbe::Complete(winner.map(|winner| {
RecentForeignSession {
tool: winner.tool,
native_id: winner.native_id,
age: now
.duration_since(winner.updated_at)
.unwrap_or(Duration::ZERO),
}
}))
}
fn recent_candidate_order(a: &RecentCandidate, b: &RecentCandidate) -> Ordering {
b.updated_at
.cmp(&a.updated_at)
.then_with(|| a.tool.cmp(&b.tool))
.then_with(|| a.native_id.cmp(&b.native_id))
.then_with(|| a.source.cmp(&b.source))
}
fn scan_with<Claude, Codex, Cursor>(
cwd: &Path,
enabled: EnabledForeignSessionSources,
mut scan_claude: Claude,
mut scan_codex: Codex,
mut scan_cursor: Cursor,
) -> Vec<ForeignSessionSummary>
where
Claude: FnMut(&Path, SystemTime) -> Vec<ForeignSessionSummary>,
Codex: FnMut(&Path, SystemTime) -> Vec<ForeignSessionSummary>,
Cursor: FnMut(&Path, SystemTime) -> Vec<ForeignSessionSummary>,
{
if !enabled.claude && !enabled.codex && !enabled.cursor {
return Vec::new();
}
let canonical_cwd = dunce::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf());
let mut cwd_spellings = vec![canonical_cwd];
if cwd_spellings[0].as_path() != cwd {
cwd_spellings.push(cwd.to_path_buf());
}
let now = SystemTime::now();
let mut sessions = Vec::new();
if enabled.claude {
let mut tool_sessions = Vec::new();
for cwd in &cwd_spellings {
tool_sessions.extend(scan_claude(cwd, now));
}
sessions.extend(finish_tool_scan(tool_sessions));
}
if enabled.codex {
let mut tool_sessions = Vec::new();
for cwd in &cwd_spellings {
tool_sessions.extend(scan_codex(cwd, now));
}
sessions.extend(finish_tool_scan(tool_sessions));
}
if enabled.cursor {
let mut tool_sessions = Vec::new();
for cwd in &cwd_spellings {
tool_sessions.extend(scan_cursor(cwd, now));
}
sessions.extend(finish_tool_scan(tool_sessions));
}
sort_sessions(&mut sessions);
sessions
}
fn sort_sessions(sessions: &mut [ForeignSessionSummary]) {
sessions.sort_by(|a, b| {
b.updated_at
.cmp(&a.updated_at)
.then_with(|| a.tool.cmp(&b.tool))
.then_with(|| a.native_id.cmp(&b.native_id))
.then_with(|| a.source.cmp(&b.source))
.then_with(|| a.title.cmp(&b.title))
.then_with(|| a.cwd.cmp(&b.cwd))
});
}
pub(super) fn finish_tool_scan(
mut sessions: Vec<ForeignSessionSummary>,
) -> Vec<ForeignSessionSummary> {
sort_sessions(&mut sessions);
let mut seen = HashSet::new();
sessions.retain(|session| seen.insert(session.native_id.clone()));
sessions.truncate(MAX_SESSIONS_PER_TOOL);
sessions
}
pub(super) fn retain_top_k_by<T>(
candidates: &mut Vec<T>,
candidate: T,
limit: usize,
compare: impl Fn(&T, &T) -> Ordering,
) {
if limit == 0 {
return;
}
if candidates.len() == limit {
if !candidates
.last()
.is_some_and(|worst| compare(worst, &candidate).is_gt())
{
return;
}
candidates.pop();
}
let index = candidates
.binary_search_by(|existing| compare(existing, &candidate))
.unwrap_or_else(|index| index);
candidates.insert(index, candidate);
}
pub(super) fn is_within(updated_at: SystemTime, now: SystemTime, within: Duration) -> bool {
match now.duration_since(updated_at) {
Ok(age) => age <= within,
Err(future) => future.duration() <= MAX_FUTURE_SKEW,
}
}
pub(super) fn system_time_from_millis(millis: i64) -> Option<SystemTime> {
let millis = u64::try_from(millis).ok()?;
UNIX_EPOCH.checked_add(Duration::from_millis(millis))
}
pub(super) fn millis_from_system_time(time: SystemTime) -> Option<i64> {
let millis = time.duration_since(UNIX_EPOCH).ok()?.as_millis();
i64::try_from(millis).ok()
}
pub(super) fn millis_bounds(now: SystemTime, within: Duration) -> Option<(i64, i64)> {
Some((
millis_from_system_time(now.checked_sub(within)?)?,
millis_from_system_time(now.checked_add(MAX_FUTURE_SKEW)?)?,
))
}
pub(super) fn normalize_title(value: &str) -> Option<String> {
let normalized = value.split_whitespace().collect::<Vec<_>>().join(" ");
if normalized.is_empty() {
return None;
}
let mut chars = normalized.chars();
let prefix: String = chars.by_ref().take(MAX_TITLE_CHARS).collect();
if chars.next().is_none() {
Some(prefix)
} else {
let mut truncated: String = prefix
.chars()
.take(MAX_TITLE_CHARS.saturating_sub(1))
.collect();
truncated.push('…');
Some(truncated)
}
}
/// Test fixture root: a tempdir plus its canonical path. `ApprovedRoot` (and
/// the path-capability code built on it) canonicalizes internally, so fixtures
/// must build paths from the canonical form or containment checks fail when
/// the OS tempdir is behind a symlink (macOS: `/var` -> `/private/var`).
#[cfg(test)]
fn canonical_tempdir() -> (tempfile::TempDir, PathBuf) {
let dir = tempfile::tempdir().unwrap();
let path = dunce::canonicalize(dir.path()).unwrap();
(dir, path)
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::{Cell, RefCell};
fn summary(id: &str, updated_at: SystemTime) -> ForeignSessionSummary {
ForeignSessionSummary {
tool: ForeignSessionTool::Claude,
source: ForeignSessionSource::ClaudeCode,
native_id: id.to_owned(),
title: id.to_owned(),
cwd: PathBuf::from("/repo"),
updated_at,
branch: None,
}
}
fn recent_candidate(
tool: ForeignSessionTool,
source: ForeignSessionSource,
id: &str,
updated_at: SystemTime,
) -> RecentCandidate {
RecentCandidate {
tool,
source,
native_id: id.to_owned(),
updated_at,
}
}
fn complete_candidate(
tool: ForeignSessionTool,
source: ForeignSessionSource,
id: &str,
updated_at: SystemTime,
) -> RecentProbe<RecentCandidate> {
RecentProbe::Complete(Some(recent_candidate(tool, source, id, updated_at)))
}
#[test]
fn recent_winner_is_newest_across_tools_with_deterministic_ties() {
let now = UNIX_EPOCH + Duration::from_secs(10_000);
let within = Duration::from_secs(600);
let root = tempfile::tempdir().unwrap();
let cwd = dunce::canonicalize(root.path()).unwrap();
let enabled = EnabledForeignSessionSources {
claude: true,
codex: true,
cursor: true,
};
let winner = most_recent_with(
&cwd,
enabled,
within,
now,
|_, _, _| {
complete_candidate(
ForeignSessionTool::Claude,
ForeignSessionSource::ClaudeCode,
"claude",
now - Duration::from_secs(3),
)
},
|_, _, _| {
complete_candidate(
ForeignSessionTool::Codex,
ForeignSessionSource::CodexCli,
"codex",
now - Duration::from_secs(1),
)
},
|_, _, _| {
complete_candidate(
ForeignSessionTool::Cursor,
ForeignSessionSource::CursorDesktop,
"cursor",
now - Duration::from_secs(2),
)
},
)
.unwrap();
assert_eq!(winner.tool, ForeignSessionTool::Codex);
assert_eq!(winner.native_id, "codex");
let tied = most_recent_with(
&cwd,
enabled,
within,
now,
|_, _, _| {
complete_candidate(
ForeignSessionTool::Claude,
ForeignSessionSource::ClaudeCode,
"claude",
now,
)
},
|_, _, _| {
complete_candidate(
ForeignSessionTool::Codex,
ForeignSessionSource::CodexCli,
"codex",
now,
)
},
|_, _, _| {
complete_candidate(
ForeignSessionTool::Cursor,
ForeignSessionSource::CursorDesktop,
"cursor",
now,
)
},
)
.unwrap();
assert_eq!(tied.tool, ForeignSessionTool::Claude);
assert_eq!(tied.native_id, "claude");
}
#[test]
fn recent_window_includes_cutoff_and_clamps_safe_future_age() {
let now = UNIX_EPOCH + Duration::from_secs(10_000);
let within = Duration::from_secs(600);
let root = tempfile::tempdir().unwrap();
let cwd = dunce::canonicalize(root.path()).unwrap();
let claude_only = EnabledForeignSessionSources {
claude: true,
..Default::default()
};
let at_cutoff = most_recent_with(
&cwd,
claude_only,
within,
now,
|_, _, _| {
complete_candidate(
ForeignSessionTool::Claude,
ForeignSessionSource::ClaudeCode,
"cutoff",
now - within,
)
},
|_, _, _| -> RecentProbe<RecentCandidate> { panic!("disabled codex store touched") },
|_, _, _| -> RecentProbe<RecentCandidate> { panic!("disabled cursor store touched") },
)
.unwrap();
assert_eq!(at_cutoff.age, within);
let future = most_recent_with(
&cwd,
claude_only,
within,
now,
|_, _, _| {
complete_candidate(
ForeignSessionTool::Claude,
ForeignSessionSource::ClaudeCode,
"future",
now + MAX_FUTURE_SKEW,
)
},
|_, _, _| -> RecentProbe<RecentCandidate> { panic!("disabled codex store touched") },
|_, _, _| -> RecentProbe<RecentCandidate> { panic!("disabled cursor store touched") },
)
.unwrap();
assert_eq!(future.age, Duration::ZERO);
assert!(
most_recent_with(
&cwd,
claude_only,
within,
now,
|_, _, _| {
complete_candidate(
ForeignSessionTool::Claude,
ForeignSessionSource::ClaudeCode,
"too-far-future",
now + MAX_FUTURE_SKEW + Duration::from_secs(1),
)
},
|_, _, _| -> RecentProbe<RecentCandidate> {
panic!("disabled codex store touched")
},
|_, _, _| -> RecentProbe<RecentCandidate> {
panic!("disabled cursor store touched")
},
)
.is_none()
);
}
#[test]
fn recent_scan_never_touches_disabled_tool_stores() {
let now = UNIX_EPOCH + Duration::from_secs(10_000);
let root = tempfile::tempdir().unwrap();
let cwd = dunce::canonicalize(root.path()).unwrap();
let calls = Cell::new((0, 0, 0));
let found = most_recent_with(
&cwd,
EnabledForeignSessionSources {
codex: true,
..Default::default()
},
Duration::from_secs(600),
now,
|_, _, _| {
let (_, codex, cursor) = calls.get();
calls.set((1, codex, cursor));
RecentProbe::Complete(None)
},
|_, _, _| {
let (claude, _, cursor) = calls.get();
calls.set((claude, 1, cursor));
complete_candidate(
ForeignSessionTool::Codex,
ForeignSessionSource::CodexCli,
"codex",
now,
)
},
|_, _, _| {
let (claude, codex, _) = calls.get();
calls.set((claude, codex, 1));
RecentProbe::Complete(None)
},
);
assert_eq!(calls.get(), (0, 1, 0));
assert_eq!(found.unwrap().tool, ForeignSessionTool::Codex);
}
#[test]
fn incomplete_enabled_tool_suppresses_cross_tool_winner() {
let now = UNIX_EPOCH + Duration::from_secs(10_000);
let root = tempfile::tempdir().unwrap();
let cwd = dunce::canonicalize(root.path()).unwrap();
let result = most_recent_with(
&cwd,
EnabledForeignSessionSources {
claude: true,
codex: true,
cursor: true,
},
Duration::from_secs(600),
now,
|_, _, _| RecentProbe::<RecentCandidate>::Incomplete,
|_, _, _| {
complete_candidate(
ForeignSessionTool::Codex,
ForeignSessionSource::CodexCli,
"codex",
now,
)
},
|_, _, _| RecentProbe::Complete(None),
);
assert_eq!(result, RecentProbe::Incomplete);
}
#[test]
fn recent_scan_normalizes_cwd_before_store_access() {
let root = tempfile::tempdir().unwrap();
let cwd = root.path().join("repo");
let child = cwd.join("child");
std::fs::create_dir_all(&child).unwrap();
let expected = dunce::canonicalize(&cwd).unwrap();
let spelled = child.join("..");
let found = most_recent_with(
&spelled,
EnabledForeignSessionSources {
codex: true,
..Default::default()
},
Duration::from_secs(600),
SystemTime::now(),
|_, _, _| -> RecentProbe<RecentCandidate> { panic!("disabled claude store touched") },
|received, _, _| {
assert_eq!(received, expected);
RecentProbe::Complete(None)
},
|_, _, _| -> RecentProbe<RecentCandidate> { panic!("disabled cursor store touched") },
);
assert!(found.is_none());
}
#[test]
fn recent_scan_canonicalization_failure_never_invokes_stores() {
let calls = Cell::new(0);
let root = tempfile::tempdir().unwrap();
let missing = root.path().join("missing");
let found = most_recent_with(
&missing,
EnabledForeignSessionSources {
claude: true,
codex: true,
cursor: true,
},
Duration::from_secs(600),
SystemTime::now(),
|_, _, _| {
calls.set(calls.get() + 1);
RecentProbe::Complete(None)
},
|_, _, _| {
calls.set(calls.get() + 1);
RecentProbe::Complete(None)
},
|_, _, _| {
calls.set(calls.get() + 1);
RecentProbe::Complete(None)
},
);
assert!(found.is_none());
assert_eq!(calls.get(), 0);
}
#[test]
fn disabled_sources_do_not_invoke_scanners() {
let sessions = scan_with(
Path::new("/repo"),
EnabledForeignSessionSources::default(),
|_, _| panic!("claude scanner called"),
|_, _| panic!("codex scanner called"),
|_, _| panic!("cursor scanner called"),
);
assert!(sessions.is_empty());
}
#[test]
fn only_enabled_sources_are_invoked() {
let claude_calls = Cell::new(0);
let codex_calls = Cell::new(0);
let cursor_calls = Cell::new(0);
scan_with(
Path::new("/repo"),
EnabledForeignSessionSources {
codex: true,
..Default::default()
},
|_, _| {
claude_calls.set(claude_calls.get() + 1);
Vec::new()
},
|_, _| {
codex_calls.set(codex_calls.get() + 1);
Vec::new()
},
|_, _| {
cursor_calls.set(cursor_calls.get() + 1);
Vec::new()
},
);
assert_eq!(
(claude_calls.get(), codex_calls.get(), cursor_calls.get()),
(0, 1, 0)
);
}
#[test]
fn finish_scan_deduplicates_sorts_and_caps() {
let now = UNIX_EPOCH + Duration::from_secs(1_000);
let mut sessions = (0..55)
.map(|i| summary(&format!("{i:02}"), now + Duration::from_secs(i)))
.collect::<Vec<_>>();
sessions.push(summary("54", now + Duration::from_secs(500)));
let sessions = finish_tool_scan(sessions);
assert_eq!(sessions.len(), MAX_SESSIONS_PER_TOOL);
assert_eq!(sessions[0].native_id, "54");
assert!(
sessions
.windows(2)
.all(|pair| pair[0].updated_at >= pair[1].updated_at)
);
}
#[test]
fn top_k_helper_uses_comparator_ties() {
let mut candidates = Vec::with_capacity(3);
for candidate in [(1, "z"), (3, "c"), (3, "a"), (2, "b"), (4, "d"), (3, "b")] {
retain_top_k_by(&mut candidates, candidate, 3, |a, b| {
b.0.cmp(&a.0).then_with(|| a.1.cmp(b.1))
});
}
assert_eq!(candidates, vec![(4, "d"), (3, "a"), (3, "b")]);
}
#[test]
fn title_truncation_is_utf8_safe_and_bounded() {
let title = normalize_title(&"é".repeat(250)).unwrap();
assert_eq!(title.chars().count(), MAX_TITLE_CHARS);
assert!(title.ends_with('…'));
}
#[test]
fn recency_allows_thirty_days_and_only_small_future_skew() {
let now = UNIX_EPOCH + Duration::from_secs(4_000_000);
assert!(is_within(now - MAX_SESSION_AGE, now, MAX_SESSION_AGE));
assert!(!is_within(
now - MAX_SESSION_AGE - Duration::from_secs(1),
now,
MAX_SESSION_AGE,
));
assert!(is_within(now + MAX_FUTURE_SKEW, now, MAX_SESSION_AGE));
assert!(!is_within(
now + MAX_FUTURE_SKEW + Duration::from_secs(1),
now,
MAX_SESSION_AGE,
));
}
#[test]
fn enabled_scanners_receive_canonical_and_supplied_cwd_spellings() {
let root = tempfile::tempdir().unwrap();
let cwd = root.path().join("repo");
let child = cwd.join("child");
std::fs::create_dir_all(&child).unwrap();
let expected = dunce::canonicalize(&cwd).unwrap();
let spelled = child.join("..");
let received = RefCell::new(Vec::new());
scan_with(
&spelled,
EnabledForeignSessionSources {
codex: true,
..Default::default()
},
|_, _| panic!("claude scanner called"),
|cwd, _| {
received.borrow_mut().push(cwd.to_path_buf());
Vec::new()
},
|_, _| panic!("cursor scanner called"),
);
assert_eq!(
received.into_inner(),
vec![expected.clone(), spelled.clone()]
);
#[cfg(unix)]
{
let link = root.path().join("linked-repo");
std::os::unix::fs::symlink(&cwd, &link).unwrap();
let received = RefCell::new(Vec::new());
scan_with(
&link,
EnabledForeignSessionSources {
cursor: true,
..Default::default()
},
|_, _| panic!("claude scanner called"),
|_, _| panic!("codex scanner called"),
|cwd, _| {
received.borrow_mut().push(cwd.to_path_buf());
Vec::new()
},
);
assert_eq!(received.into_inner(), vec![expected, link]);
}
}
#[cfg(windows)]
#[test]
fn normalized_cwd_uses_ordinary_windows_spelling() {
let root = tempfile::tempdir().unwrap();
let cwd = root.path().join("repo");
std::fs::create_dir_all(&cwd).unwrap();
scan_with(
&cwd,
EnabledForeignSessionSources {
codex: true,
..Default::default()
},
|_, _| panic!("claude scanner called"),
|received, _| {
assert_eq!(received, dunce::canonicalize(&cwd).unwrap());
assert!(!received.to_string_lossy().starts_with(r"\\?\"));
Vec::new()
},
|_, _| panic!("cursor scanner called"),
);
}
}