Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/client/src/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use devo_protocol::SessionForkParams;
use devo_protocol::SessionForkResult;
use devo_protocol::SessionListParams;
use devo_protocol::SessionListResult;
use devo_protocol::SessionMetadataUpdateParams;
use devo_protocol::SessionMetadataUpdateResult;
use devo_protocol::SessionResumeParams;
use devo_protocol::SessionResumeResult;
use devo_protocol::SessionStartParams;
Expand Down Expand Up @@ -167,6 +169,13 @@ impl StdioServerClient {
self.request("session/title/update", params).await
}

pub async fn session_metadata_update(
&mut self,
params: SessionMetadataUpdateParams,
) -> Result<SessionMetadataUpdateResult> {
self.request("session/metadata/update", params).await
}

pub async fn session_fork(&mut self, params: SessionForkParams) -> Result<SessionForkResult> {
self.request("session/fork", params).await
}
Expand Down
23 changes: 17 additions & 6 deletions crates/core/src/conversation/records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ pub struct SessionRecord {
pub model_provider: String,
/// The latest resolved model slug for the session.
pub model: Option<String>,
/// The latest resolved reasoning effort for the session.
pub reasoning_effort: Option<String>,
/// The logical thinking selection used as the default for the next turn.
pub thinking: Option<String>,
/// The working directory associated with the session.
pub cwd: PathBuf,
/// The CLI version that created the session.
Expand Down Expand Up @@ -75,8 +75,14 @@ pub struct TurnRecord {
pub completed_at: Option<DateTime<Utc>>,
/// The current lifecycle status of the turn.
pub status: TurnStatus,
/// The resolved model slug used by the turn.
pub model_slug: String,
/// The logical model selection used for the turn.
pub model: String,
/// The logical thinking selection used for the turn.
pub thinking: Option<String>,
/// The concrete request model used to execute the turn.
pub request_model: String,
/// The concrete request thinking parameter used to execute the turn.
pub request_thinking: Option<String>,
/// The estimated input-token count at turn start, when available.
pub input_token_estimate: Option<u32>,
/// The authoritative provider token usage, when available.
Expand Down Expand Up @@ -117,6 +123,8 @@ pub struct ToolProgressItem {
pub struct ToolResultItem {
/// The tool call this result belongs to.
pub tool_call_id: String,
/// The runtime tool name when it is available at result time.
pub tool_name: Option<String>,
/// The normalized structured output returned by the tool.
pub output: serde_json::Value,
/// Whether the result represents an error outcome.
Expand Down Expand Up @@ -319,7 +327,7 @@ mod tests {
agent_path: None,
model_provider: "test".into(),
model: None,
reasoning_effort: None,
thinking: None,
cwd: ".".into(),
cli_version: "0.1.0".into(),
title: None,
Expand Down Expand Up @@ -381,7 +389,10 @@ mod tests {
started_at: Utc::now(),
completed_at: None,
status: TurnStatus::Pending,
model_slug: "test-model".into(),
model: "test-model".into(),
thinking: None,
request_model: "test-model".into(),
request_thinking: None,
input_token_estimate: Some(42),
usage: None,
schema_version: 1,
Expand Down
23 changes: 19 additions & 4 deletions crates/protocol/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;

use crate::session::{SessionRuntimeStatus, SessionSummary};
use crate::turn::TurnSummary;
use crate::session::{SessionMetadata, SessionRuntimeStatus};
use crate::turn::TurnMetadata;
use crate::{ItemId, SessionId, TurnId, TurnUsage};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -20,6 +20,21 @@ pub struct ItemEnvelope {
pub payload: serde_json::Value,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolCallPayload {
pub tool_call_id: String,
pub tool_name: String,
pub parameters: serde_json::Value,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolResultPayload {
pub tool_call_id: String,
pub tool_name: Option<String>,
pub content: serde_json::Value,
pub is_error: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ItemEventPayload {
pub context: EventContext,
Expand All @@ -37,7 +52,7 @@ pub struct ItemDeltaPayload {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TurnEventPayload {
pub session_id: SessionId,
pub turn: TurnSummary,
pub turn: TurnMetadata,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -51,7 +66,7 @@ pub struct TurnUsageUpdatedPayload {

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionEventPayload {
pub session: SessionSummary,
pub session: SessionMetadata,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
67 changes: 54 additions & 13 deletions crates/protocol/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use serde::Serialize;

use crate::SessionId;
use crate::SessionTitleState;
use crate::turn::TurnSummary;
use crate::turn::TurnMetadata;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
Expand All @@ -20,15 +20,16 @@ pub enum SessionRuntimeStatus {
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionSummary {
pub struct SessionMetadata {
pub session_id: SessionId,
pub cwd: PathBuf,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub title: Option<String>,
pub title_state: SessionTitleState,
pub ephemeral: bool,
pub resolved_model: Option<String>,
pub model: Option<String>,
pub thinking: Option<String>,
pub total_input_tokens: usize,
pub total_output_tokens: usize,
pub status: SessionRuntimeStatus,
Expand All @@ -44,11 +45,7 @@ pub struct SessionStartParams {

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionStartResult {
pub session_id: SessionId,
pub created_at: DateTime<Utc>,
pub cwd: PathBuf,
pub ephemeral: bool,
pub resolved_model: Option<String>,
pub session: SessionMetadata,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -58,8 +55,8 @@ pub struct SessionResumeParams {

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionResumeResult {
pub session: SessionSummary,
pub latest_turn: Option<TurnSummary>,
pub session: SessionMetadata,
pub latest_turn: Option<TurnMetadata>,
pub loaded_item_count: u64,
pub history_items: Vec<SessionHistoryItem>,
}
Expand All @@ -76,6 +73,7 @@ pub enum SessionHistoryItemKind {

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionHistoryItem {
pub tool_call_id: Option<String>,
pub kind: SessionHistoryItemKind,
pub title: String,
pub body: String,
Expand All @@ -86,7 +84,7 @@ pub struct SessionListParams {}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionListResult {
pub sessions: Vec<SessionSummary>,
pub sessions: Vec<SessionMetadata>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -97,7 +95,19 @@ pub struct SessionTitleUpdateParams {

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionTitleUpdateResult {
pub session: SessionSummary,
pub session: SessionMetadata,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionMetadataUpdateParams {
pub session_id: SessionId,
pub model: Option<String>,
pub thinking: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionMetadataUpdateResult {
pub session: SessionMetadata,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -109,6 +119,37 @@ pub struct SessionForkParams {

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionForkResult {
pub session: SessionSummary,
pub session: SessionMetadata,
pub forked_from_session_id: SessionId,
}

#[cfg(test)]
mod tests {
use chrono::Utc;
use pretty_assertions::assert_eq;

use super::*;
use crate::SessionTitleState;

#[test]
fn session_metadata_roundtrips_with_model_and_thinking() {
let metadata = SessionMetadata {
session_id: SessionId::new(),
cwd: "/tmp".into(),
created_at: Utc::now(),
updated_at: Utc::now(),
title: Some("Test".to_string()),
title_state: SessionTitleState::Unset,
ephemeral: false,
model: Some("test-model".to_string()),
thinking: Some("medium".to_string()),
total_input_tokens: 12,
total_output_tokens: 34,
status: SessionRuntimeStatus::Idle,
};

let json = serde_json::to_string(&metadata).expect("serialize");
let restored: SessionMetadata = serde_json::from_str(&json).expect("deserialize");
assert_eq!(restored, metadata);
}
}
41 changes: 39 additions & 2 deletions crates/protocol/src/turn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ use serde::{Deserialize, Serialize};

use crate::{ItemId, SessionId, TurnId, TurnStatus, TurnUsage};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TurnSummary {
pub struct TurnMetadata {
pub turn_id: TurnId,
pub session_id: SessionId,
pub sequence: u32,
pub status: TurnStatus,
pub model_slug: String,
pub model: String,
pub thinking: Option<String>,
pub request_model: String,
pub request_thinking: Option<String>,
pub started_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
pub usage: Option<TurnUsage>,
Expand Down Expand Up @@ -91,3 +94,37 @@ pub struct ActiveTurnSteeringState {
pub turn_kind: TurnKind,
pub pending_inputs: VecDeque<SteerInputRecord>,
}

#[cfg(test)]
mod tests {
use chrono::Utc;
use pretty_assertions::assert_eq;

use super::*;

#[test]
fn turn_metadata_roundtrips_with_logical_and_request_fields() {
let metadata = TurnMetadata {
turn_id: TurnId::new(),
session_id: SessionId::new(),
sequence: 1,
status: TurnStatus::Completed,
model: "logical-model".to_string(),
thinking: Some("high".to_string()),
request_model: "provider-model".to_string(),
request_thinking: Some("medium".to_string()),
started_at: Utc::now(),
completed_at: Some(Utc::now()),
usage: Some(TurnUsage {
input_tokens: 10,
output_tokens: 20,
cache_creation_input_tokens: None,
cache_read_input_tokens: None,
}),
};

let json = serde_json::to_string(&metadata).expect("serialize");
let restored: TurnMetadata = serde_json::from_str(&json).expect("deserialize");
assert_eq!(restored, metadata);
}
}
1 change: 1 addition & 0 deletions crates/server/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pub use devo_protocol::{
ItemEventPayload, ItemKind, PendingServerRequestContext, RequestUserInputPayload, ServerEvent,
ServerRequestKind, ServerRequestResolvedPayload, SessionEventPayload,
SessionStatusChangedPayload, TurnEventPayload, TurnUsageUpdatedPayload,
ToolCallPayload, ToolResultPayload,
};
14 changes: 7 additions & 7 deletions crates/server/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use devo_tools::ToolRegistry;

use crate::{
InputItem, SkillRecord,
session::{SessionHistoryItem, SessionSummary},
turn::TurnSummary,
session::{SessionHistoryItem, SessionMetadata},
turn::TurnMetadata,
};

/// Shared server-owned runtime dependencies used by live turn execution.
Expand Down Expand Up @@ -175,14 +175,14 @@ fn render_resolved_skill(skill: &ResolvedSkill) -> String {
pub(crate) struct RuntimeSession {
/// Canonical persisted session metadata when the session is durable.
pub(crate) record: Option<SessionRecord>,
/// Transport-facing summary exposed over the API.
pub(crate) summary: SessionSummary,
/// Transport-facing metadata exposed over the API.
pub(crate) summary: SessionMetadata,
/// Canonical core session state used by the query loop.
pub(crate) core_session: Arc<Mutex<SessionState>>,
/// Currently active turn, if any.
pub(crate) active_turn: Option<TurnSummary>,
/// Latest terminal turn summary for the session.
pub(crate) latest_turn: Option<TurnSummary>,
pub(crate) active_turn: Option<TurnMetadata>,
/// Latest terminal turn metadata for the session.
pub(crate) latest_turn: Option<TurnMetadata>,
/// Number of items loaded or appended for the session.
pub(crate) loaded_item_count: u64,
/// Replay-friendly ordered history used by interactive clients during session resume.
Expand Down
Loading
Loading