Files
boxmaker/crates/bxcore/src/wire.rs
T

266 lines
6.8 KiB
Rust

use serde::{Deserialize, Serialize};
use crate::gateway::{ApprovalOutcome, ApprovalPosted, ApprovalReply, ApprovalRequest};
use crate::memory::{
MemoryAdmin, MemoryAdminReply, MemoryBlockReply, MemoryCall, Reindexed, SessionState,
SessionStateReply, SessionUpdated,
};
use crate::{CallId, DataClass, DecisionRecord, SessionId, Timestamp, Usage};
pub const PROTOCOL_VERSION: u32 = 1;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Envelope {
pub v: u32,
pub id: u64,
pub r#final: bool,
pub msg: Message,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(
tag = "kind",
content = "body",
deny_unknown_fields,
rename_all = "snake_case"
)]
pub enum Message {
ToolRequest(ToolRequest),
ToolResponse(ToolResponse),
Error(WireError),
Turn(Turn),
TurnEvent(TurnEvent),
TurnDone(TurnDone),
Approvals(Empty),
ApprovalList(ApprovalList),
Approve(Approve),
ApproveResult(ApproveResult),
Refuse(Refuse),
Ok(Empty),
CheckGrants(Empty),
GrantsReport(GrantsReport),
ApprovalRequest(ApprovalRequest),
ApprovalPosted(ApprovalPosted),
ApprovalReply(ApprovalReply),
ApprovalOutcome(ApprovalOutcome),
MemoryBlock(Empty),
MemoryBlockReply(MemoryBlockReply),
SessionUpdated(SessionUpdated),
MemoryCall(MemoryCall),
Reindex(Empty),
Reindexed(Reindexed),
MemoryAdmin(MemoryAdmin),
MemoryAdminReply(MemoryAdminReply),
SessionState(SessionState),
SessionStateReply(SessionStateReply),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WireError {
pub code: ErrorCode,
pub detail: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorCode {
BadFrame,
BadVersion,
BadMessage,
Internal,
SessionFull,
TurnLimit,
SessionBusy,
NoSuchSession,
SessionExists,
Inference,
Forbidden,
NoSuchApproval,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolRequest {
pub session: SessionId,
pub call: CallId,
pub tool: String,
pub arguments: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "status", deny_unknown_fields, rename_all = "snake_case")]
pub enum ToolResponse {
PendingApproval {
approval: u64,
expires: Timestamp,
},
Result {
content: String,
class: DataClass,
untrusted: bool,
truncated: bool,
},
Failed {
message: String,
},
Denied {
reason: DenyReason,
/// Why the arguments were not valid, for `invalid_arguments` only. Written only when
/// present, so a denial without one has the same bytes as before M5b.
#[serde(default, skip_serializing_if = "Option::is_none")]
detail: Option<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DenyReason {
NoGrant,
GrantExpired,
TaintTooHigh,
DeniedByGrant,
ApprovalRefused,
ApprovalExpired,
GrantsInvalid,
AuditUnavailable,
InvalidArguments,
StateUnreadable,
}
// JSON: {"kind":"turn","body":{"session":"…","content":"…","resume":false,"speaker":"kyle"}}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Turn {
pub session: SessionId,
pub content: String,
pub resume: bool,
/// Who spoke the turn, when one person did (M5c spec, section 11). Written only when present,
/// and a frame without it reads as `None`, the safe side: a turn without a speaker is never
/// synced to Honcho.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub speaker: Option<String>,
}
// JSON: {"event":"content","text":"…"} — the tag sits beside the fields, snake_case
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "event", deny_unknown_fields, rename_all = "snake_case")]
pub enum TurnEvent {
Queued {
ahead: u64,
},
Waiting {
slot_busy: bool,
},
Progress {
total: u64,
cache: u64,
processed: u64,
},
Reasoning {
text: String,
},
Content {
text: String,
},
ToolCallStarted {
name: String,
},
ToolResult {
name: String,
class: DataClass,
truncated: bool,
},
ThinkingCapped {
tokens: u64,
},
Retrying {
attempt: u32,
after_ms: u64,
error: String,
},
CacheLoss {
expected: u64,
got: u64,
},
ApprovalPending {
approval: u64,
tool: String,
expires: Timestamp,
},
ToolDenied {
name: String,
reason: DenyReason,
},
}
// JSON: {"kind":"turn_done","body":{"content":"…","usage":{…},"untrusted":false}}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TurnDone {
pub content: String,
pub usage: Usage,
/// True once the session has read untrusted content, in this turn or an earlier one (M5b
/// spec, section 9). It has no default: a frame without it is refused, never read as clean.
pub untrusted: bool,
}
// The admin messages on admin.sock. JSON: {"kind":"approvals","body":{}}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Empty {}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PendingApproval {
pub approval: u64,
pub session: SessionId,
pub call: CallId,
pub tool: String,
pub arguments: String,
pub grant: String,
pub taint: DataClass,
pub created: Timestamp,
pub expires: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ApprovalList {
pub items: Vec<PendingApproval>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Approve {
pub approval: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ApproveResult {
pub outcome: DecisionRecord,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Refuse {
pub approval: u64,
pub reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GrantProblem {
pub file: String,
pub line: Option<u64>,
pub problem: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GrantsReport {
pub problems: Vec<GrantProblem>,
}