266 lines
6.8 KiB
Rust
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>,
|
|
}
|