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
4 changes: 2 additions & 2 deletions crates/codeoid-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ pub use message::{
ContentPart, IdentityType, MessageIdentity, MessageRole, SessionMessage, SessionMessageDelta,
};
pub use session::{
CollaborationConfig, CollaborationRole, ForkedFrom, SessionInfo, SessionMode, SessionStatus,
SessionUsage, SessionWorktree, Subagent, TurnUsage,
CollaborationConfig, CollaborationRole, CollaborationRoleRef, ForkedFrom, SessionInfo,
SessionMode, SessionStatus, SessionUsage, SessionWorktree, Subagent, TurnUsage,
};
pub use tool::{CancelReason, ConfirmedBy, ToolInfo, ToolPhase, ToolState};

Expand Down
32 changes: 32 additions & 0 deletions crates/codeoid-protocol/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ pub struct SessionInfo {
/// restart the way `role`/`provider_id` already do.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub collaboration: Option<CollaborationConfig>,

/// Set when this session is a role-CHILD of a collaborative session.
/// Absent = not a collaboration child. Pairs with `collaboration` above,
/// which marks the orchestrating parent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub collaboration_role: Option<CollaborationRoleRef>,
}

/// Where a forked session came from — the parent id, the parent's name at
Expand Down Expand Up @@ -148,6 +154,32 @@ pub struct CollaborationRole {
/// What this role is for; surfaced in the child's brief.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub purpose: Option<String>,
/// Whether this role's children may modify the workspace.
///
/// `None`/`false` = read-only, and that default is load-bearing: the
/// daemon gives a read-only role a leaf identity with no write scope at
/// all, so a reviewer provably cannot write rather than being asked not
/// to. Write authority is opt-in per role.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub write: Option<bool>,
}

/// Set on a role-CHILD of a collaborative session: which collaboration it
/// belongs to and which role it plays.
///
/// The mirror of [`SessionInfo::collaboration`] (set on the orchestrating
/// parent), so a client can group a fleet without inferring it from names.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CollaborationRoleRef {
/// Session id of the orchestrating parent.
pub parent_session_id: String,
/// Role name from the parent's config (already lowercased by the daemon).
pub role_name: String,
/// 1-based index within this role's fan-out (`review` ×3 → 1, 2, 3).
pub ordinal: u32,
/// Whether this child's identity carries write authority.
pub write: bool,
}

/// Collaborative-session config: one goal worked by several role-children on
Expand Down
10 changes: 10 additions & 0 deletions crates/codeoid-protocol/tests/wire_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,26 @@ fn sample_session_info() -> SessionInfo {
model: None,
count: None,
purpose: None,
write: None,
},
codeoid_protocol::CollaborationRole {
name: "review".into(),
provider_id: "gemini".into(),
model: Some("gemini-2.5-pro".into()),
count: Some(3),
purpose: Some("independent critique".into()),
write: Some(false),
},
],
}),
// A real session is either a parent or a child, never both; populated
// here anyway so the camelCase walker visits this struct's keys too.
collaboration_role: Some(codeoid_protocol::CollaborationRoleRef {
parent_session_id: "parent-1".into(),
role_name: "review".into(),
ordinal: 2,
write: false,
}),
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/codeoid-tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2653,6 +2653,7 @@ mod tests {
forked_from: None,
worktree: None,
collaboration: None,
collaboration_role: None,
});
state
}
Expand Down
1 change: 1 addition & 0 deletions crates/codeoid-tui/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,7 @@ mod tests {
forked_from: None,
worktree: None,
collaboration: None,
collaboration_role: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/codeoid-tui/src/state/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ mod tests {
forked_from: None,
worktree: None,
collaboration: None,
collaboration_role: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/codeoid-tui/src/ui/approval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ mod tests {
forked_from: None,
worktree: None,
collaboration: None,
collaboration_role: None,
});
let mut m = msg(
MessageRole::ToolCall,
Expand Down
1 change: 1 addition & 0 deletions crates/codeoid-tui/src/ui/scrollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,7 @@ mod tests {
forked_from: None,
worktree: None,
collaboration: None,
collaboration_role: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/codeoid-tui/src/ui/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ mod tests {
forked_from: None,
worktree: None,
collaboration: None,
collaboration_role: None,
});
state.provider_commands.insert(
"s1".into(),
Expand Down
Loading