update: SessionId / SessionStart / SessionOrigin 等を Segment 系名称へ

- Type/Function/Variantを Segment* 系へ統一
  - SessionId/SessionStart/SessionOrigin/SessionStartState/SessionState/SessionLogSink/SessionLockInfo
  - new_session_id / session_id / create_session* / list_sessions / lookup_session / update_session / find_by_session
  - protocol Event::SessionRotated → SegmentRotated、CompactDone.new_session_id → new_segment_id
- Module: session_log → segment_log / session → segment (file mv 含む)
  pod 側の session_log_sink → segment_log_sink も同様
- crate 名 (session-store)、CLI flag (--session)、ResumeWithSession (CLI tied) は据え置き
- session-tests/session_metrics_test 等の Store impl も追従
This commit is contained in:
2026-05-20 05:06:04 +09:00
parent de549812ab
commit 22f5d02385
55 changed files with 611 additions and 610 deletions
+10 -10
View File
@@ -483,7 +483,7 @@ impl App {
self.blocks.push(Block::UserMessage { segments });
self.assistant_streaming = false;
}
Event::SessionRotated { entry } => {
Event::SegmentRotated { entry } => {
self.reset_for_rotation();
self.apply_log_entry_raw(&entry);
self.assistant_streaming = false;
@@ -685,7 +685,7 @@ impl App {
started_at: Instant::now(),
}));
}
Event::CompactDone { new_session_id } => {
Event::CompactDone { new_segment_id } => {
if let Some(evt) = self.last_streaming_compact_mut() {
let elapsed_secs = match evt {
CompactEvent::Streaming { started_at } => {
@@ -694,12 +694,12 @@ impl App {
_ => None,
};
*evt = CompactEvent::Done {
new_session_id,
new_segment_id,
elapsed_secs,
};
} else {
self.blocks.push(Block::Compact(CompactEvent::Done {
new_session_id,
new_segment_id,
elapsed_secs: None,
}));
}
@@ -932,7 +932,7 @@ impl App {
}
/// Drop the derived view in preparation for replaying a new
/// `SessionStart` (compaction / fork). Greeting is preserved
/// `SegmentStart` (compaction / fork). Greeting is preserved
/// because the Pod identity hasn't changed.
fn reset_for_rotation(&mut self) {
let greeting = self.blocks.iter().find_map(|b| match b {
@@ -958,7 +958,7 @@ impl App {
return;
};
match entry {
session_store::LogEntry::SessionStart { history, .. } => {
session_store::LogEntry::SegmentStart { history, .. } => {
for logged in history {
let item: llm_worker::Item = logged.into();
let item_value = serde_json::to_value(&item).expect("Item is Serialize");
@@ -1445,7 +1445,7 @@ mod completion_flow_tests {
#[test]
fn snapshot_renders_system_message_block_from_session_start() {
let mut app = App::new("test".into());
let session_start = session_store::LogEntry::SessionStart {
let session_start = session_store::LogEntry::SegmentStart {
ts: 1,
system_prompt: None,
config: Default::default(),
@@ -1525,15 +1525,15 @@ mod completion_flow_tests {
let id = uuid::Uuid::parse_str("12345678-1234-5678-1234-567812345678").unwrap();
app.handle_pod_event(Event::CompactStart);
app.handle_pod_event(Event::CompactDone { new_session_id: id });
app.handle_pod_event(Event::CompactDone { new_segment_id: id });
assert_eq!(compact_block_count(&app), 1);
assert!(matches!(
app.blocks.as_slice(),
[Block::Compact(CompactEvent::Done {
new_session_id,
new_segment_id,
elapsed_secs: Some(_),
})] if *new_session_id == id
})] if *new_segment_id == id
));
}
+1 -1
View File
@@ -83,7 +83,7 @@ pub enum CompactEvent {
Streaming { started_at: Instant },
/// Compaction ended cleanly with `CompactDone`.
Done {
new_session_id: uuid::Uuid,
new_segment_id: uuid::Uuid,
elapsed_secs: Option<u64>,
},
/// Compaction ended with `CompactFailed`.
+5 -5
View File
@@ -25,7 +25,7 @@ use crossterm::terminal::{
use protocol::{Method, PodStatus};
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use session_store::SessionId;
use session_store::SegmentId;
use client::PodClient;
@@ -56,7 +56,7 @@ enum Mode {
Resume,
/// `tui --session <UUID>`: skip the picker, go straight to the
/// resume name dialog with `id` baked in.
ResumeWithSession(SessionId),
ResumeWithSession(SegmentId),
}
enum ParseError {
@@ -78,7 +78,7 @@ impl std::fmt::Display for ParseError {
fn parse_args() -> Result<Mode, ParseError> {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut resume = false;
let mut session: Option<SessionId> = None;
let mut session: Option<SegmentId> = None;
let mut socket_override: Option<PathBuf> = None;
let mut positional: Option<String> = None;
@@ -94,7 +94,7 @@ fn parse_args() -> Result<Mode, ParseError> {
.get(i + 1)
.ok_or(ParseError::MissingValue("--session"))?;
session = Some(
raw.parse::<SessionId>()
raw.parse::<SegmentId>()
.map_err(|_| ParseError::InvalidSession(raw.clone()))?,
);
i += 2;
@@ -216,7 +216,7 @@ async fn run_resume() -> Result<(), Box<dyn std::error::Error>> {
run_spawn(Some(id)).await
}
async fn run_spawn(resume_from: Option<SessionId>) -> Result<(), Box<dyn std::error::Error>> {
async fn run_spawn(resume_from: Option<SegmentId>) -> Result<(), Box<dyn std::error::Error>> {
let ready = match spawn::run(resume_from).await? {
SpawnOutcome::Ready(r) => r,
SpawnOutcome::Cancelled => return Ok(()),
+9 -9
View File
@@ -2,7 +2,7 @@
//!
//! Reads the most recent sessions from the configured store, lets the
//! user pick one with the arrow keys, and returns the chosen
//! `SessionId`. Closes its inline viewport before returning so the
//! `SegmentId`. Closes its inline viewport before returning so the
//! caller can open a fresh viewport for the name dialog.
//!
//! The picker only handles selection. Forking, pod-registry checks, and
@@ -12,7 +12,7 @@ use std::io;
use std::time::Duration;
use crossterm::event::{self, Event as TermEvent, KeyCode, KeyEventKind, KeyModifiers};
use pod_registry::lookup_session;
use pod_registry::lookup_segment;
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use ratatui::layout::{Constraint, Layout};
@@ -20,7 +20,7 @@ use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::{Frame, TerminalOptions, Viewport};
use session_store::{FsStore, LogEntry, LoggedContentPart, LoggedItem, SessionId, Store};
use session_store::{FsStore, LogEntry, LoggedContentPart, LoggedItem, SegmentId, Store};
const MAX_ROWS: usize = 10;
const VIEWPORT_LINES: u16 = MAX_ROWS as u16 + 4;
@@ -60,14 +60,14 @@ impl From<session_store::StoreError> for PickerError {
}
pub enum PickerOutcome {
Picked(SessionId),
Picked(SegmentId),
Cancelled,
}
/// One row in the picker view. Rendered from the session log so the
/// user can recognise their session at a glance without parsing UUIDs.
struct Row {
id: SessionId,
id: SegmentId,
/// Last user / assistant snippet, or a `[corrupt]` placeholder.
preview: String,
/// `Some(pod_name)` when a live Pod currently holds an allocation
@@ -79,7 +79,7 @@ struct Row {
pub async fn run() -> Result<PickerOutcome, PickerError> {
let store = open_default_store()?;
let ids = store.list_sessions()?;
let ids = store.list_segments()?;
if ids.is_empty() {
return Err(PickerError::NoSessions);
}
@@ -89,7 +89,7 @@ pub async fn run() -> Result<PickerOutcome, PickerError> {
// Best-effort live check. A pods.json I/O hiccup downgrades
// the row to "no badge" rather than killing the picker — the
// user still gets to see the listing.
let live_pod = lookup_session(id).ok().flatten().map(|info| info.pod_name);
let live_pod = lookup_segment(id).ok().flatten().map(|info| info.pod_name);
rows.push(Row {
id,
preview,
@@ -158,7 +158,7 @@ fn open_default_store() -> Result<FsStore, PickerError> {
Ok(FsStore::new(&dir)?)
}
fn build_preview(store: &FsStore, id: SessionId) -> String {
fn build_preview(store: &FsStore, id: SegmentId) -> String {
match store.read_all(id) {
Ok(entries) => last_message_preview(&entries).unwrap_or_else(|| "[empty]".to_string()),
Err(_) => "[corrupt]".to_string(),
@@ -313,7 +313,7 @@ fn row_line(row: &Row, selected: bool) -> Line<'_> {
Line::from(spans)
}
fn short_session(id: SessionId) -> String {
fn short_session(id: SegmentId) -> String {
let s = id.to_string();
s.chars().take(8).collect()
}
+11 -11
View File
@@ -28,7 +28,7 @@ use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::{Frame, TerminalOptions, Viewport};
use session_store::SessionId;
use session_store::SegmentId;
const VIEWPORT_LINES: u16 = 6;
@@ -46,7 +46,7 @@ pub enum SpawnOutcome {
pub enum SpawnError {
Io(io::Error),
Store(session_store::StoreError),
MissingResumeScope { session_id: SessionId },
MissingResumeScope { segment_id: SegmentId },
Spawn(client::SpawnError),
}
@@ -55,9 +55,9 @@ impl std::fmt::Display for SpawnError {
match self {
Self::Io(e) => write!(f, "io error: {e}"),
Self::Store(e) => write!(f, "failed to read session log: {e}"),
Self::MissingResumeScope { session_id } => write!(
Self::MissingResumeScope { segment_id } => write!(
f,
"session {session_id} has no persisted scope snapshot; refusing resume without explicit scope"
"session {segment_id} has no persisted scope snapshot; refusing resume without explicit scope"
),
Self::Spawn(e) => write!(f, "{e}"),
}
@@ -89,7 +89,7 @@ type InlineTerminal = Terminal<CrosstermBackend<io::Stdout>>;
/// Source session for a resume run. `None` = fresh spawn (current
/// behaviour); `Some(id)` swaps the dialog into "Resume Pod" mode and
/// passes `--session <id>` to the spawned `pod` child.
pub async fn run(resume_from: Option<SessionId>) -> Result<SpawnOutcome, SpawnError> {
pub async fn run(resume_from: Option<SegmentId>) -> Result<SpawnOutcome, SpawnError> {
let cwd = std::env::current_dir().map_err(SpawnError::Io)?;
// Run the same merge pod itself uses, then read what's missing
@@ -321,7 +321,7 @@ fn build_overlay_toml(form: &Form) -> String {
toml::to_string(&toml::Value::Table(root)).expect("overlay serialisation cannot fail")
}
async fn load_resume_scope(session_id: SessionId) -> Result<ScopeConfig, SpawnError> {
async fn load_resume_scope(segment_id: SegmentId) -> Result<ScopeConfig, SpawnError> {
let store_dir = manifest::paths::sessions_dir().ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
@@ -329,10 +329,10 @@ async fn load_resume_scope(session_id: SessionId) -> Result<ScopeConfig, SpawnEr
)
})?;
let store = session_store::FsStore::new(&store_dir)?;
let state = session_store::restore(&store, session_id)?;
let state = session_store::restore(&store, segment_id)?;
let snapshot = state
.pod_scope
.ok_or(SpawnError::MissingResumeScope { session_id })?;
.ok_or(SpawnError::MissingResumeScope { segment_id })?;
Ok(ScopeConfig {
allow: snapshot.allow,
deny: snapshot.deny,
@@ -376,7 +376,7 @@ struct Form {
/// switches, the source session is shown to the user, and the
/// child pod is launched with `--session <id>` so it restores
/// from `id` and appends to the same session log.
resume_from: Option<SessionId>,
resume_from: Option<SegmentId>,
/// Scope snapshot recovered from the source session log. Set only for
/// resume runs, and serialized into the overlay instead of cwd-default
/// scope so resume does not silently broaden access.
@@ -473,7 +473,7 @@ fn draw_form(f: &mut Frame<'_>, form: &Form) {
/// First 8 hex digits of a UUID — short enough to skim, long enough
/// to disambiguate inside a 10-row picker.
pub(crate) fn short_session(id: SessionId) -> String {
pub(crate) fn short_session(id: SegmentId) -> String {
let s = id.to_string();
s.chars().take(8).collect()
}
@@ -584,7 +584,7 @@ mod tests {
#[test]
fn overlay_uses_resume_scope_snapshot() {
let mut f = form("agent-r", false);
f.resume_from = Some(session_store::new_session_id());
f.resume_from = Some(session_store::new_segment_id());
f.resume_scope = Some(ScopeConfig {
allow: vec![manifest::ScopeRule {
target: PathBuf::from("/work/example"),
+2 -2
View File
@@ -1019,10 +1019,10 @@ fn render_compact(lines: &mut Vec<Line<'static>>, evt: &CompactEvent, width: u16
)
}
CompactEvent::Done {
new_session_id,
new_segment_id,
elapsed_secs,
} => {
let short = new_session_id
let short = new_segment_id
.to_string()
.chars()
.take(8)