first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -1,17 +1,23 @@
|
||||
use crate::ai::agent::conversation::AIConversationId;
|
||||
use galaxy_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
use strum_macros::{EnumDiscriminants, EnumIter};
|
||||
|
||||
use crate::ai::agent::conversation::AIConversationId;
|
||||
|
||||
#[derive(Debug, EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(EnumIter))]
|
||||
pub(crate) enum BlocklistOrchestrationTelemetryEvent {
|
||||
TeamAgentCommunicationFailed(TeamAgentCommunicationFailedEvent),
|
||||
PlanConfigApprovalToggled(PlanConfigApprovalToggledEvent),
|
||||
RunAgentsCardDecision(RunAgentsCardDecisionEvent),
|
||||
PillBarInteraction(PillBarInteractionEvent),
|
||||
OrchestrationEntered(OrchestrationEnteredEvent),
|
||||
AgentProposedConfig(AgentProposedConfigEvent),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum TeamAgentCommunicationKind {
|
||||
Message,
|
||||
LifecycleEvent,
|
||||
@@ -19,12 +25,14 @@ pub(crate) enum TeamAgentCommunicationKind {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum TeamAgentCommunicationTransport {
|
||||
Local,
|
||||
ServerApi,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum TeamAgentOrchestrationVersion {
|
||||
V1,
|
||||
V2,
|
||||
@@ -32,6 +40,7 @@ pub(crate) enum TeamAgentOrchestrationVersion {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum TeamAgentCommunicationFailureReason {
|
||||
InvalidLifecycleEventType,
|
||||
MissingSourceConversation,
|
||||
@@ -58,6 +67,218 @@ pub(crate) struct TeamAgentCommunicationFailedEvent {
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
/// Coarse approval transition for the plan card's
|
||||
/// `Use orchestration` toggle.
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum OrchestrationApprovalStatus {
|
||||
Approved,
|
||||
Disapproved,
|
||||
}
|
||||
|
||||
/// Run-wide execution mode reported on telemetry payloads. A flat
|
||||
/// enum so the payload stays metadata-only and never carries an
|
||||
/// environment id or worker host.
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum OrchestrationExecutionModeKind {
|
||||
Local,
|
||||
Remote,
|
||||
}
|
||||
|
||||
impl OrchestrationExecutionModeKind {
|
||||
pub(crate) fn from_run_agents(mode: &ai::agent::action::RunAgentsExecutionMode) -> Self {
|
||||
if mode.is_remote() {
|
||||
Self::Remote
|
||||
} else {
|
||||
Self::Local
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Closed-set bucket for the run-wide harness selection. Anything
|
||||
/// unrecognized collapses to `Unknown` to keep the analytics column
|
||||
/// low-cardinality.
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum OrchestrationHarnessKind {
|
||||
Oz,
|
||||
ClaudeCode,
|
||||
Codex,
|
||||
OpenCode,
|
||||
Gemini,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl OrchestrationHarnessKind {
|
||||
pub(crate) fn from_str(harness_type: &str) -> Self {
|
||||
match harness_type {
|
||||
"oz" | "" => Self::Oz,
|
||||
"claude" | "claude-code" | "claude_code" => Self::ClaudeCode,
|
||||
"codex" => Self::Codex,
|
||||
"opencode" | "open-code" | "open_code" => Self::OpenCode,
|
||||
"gemini" => Self::Gemini,
|
||||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stable names for run-wide config fields that can diverge between
|
||||
/// the dispatched orchestration request and either the original tool
|
||||
/// call or an active approved config. Match the server's equivalent
|
||||
/// field-name constants so the two telemetry streams can be joined.
|
||||
pub(crate) mod orchestration_modified_field {
|
||||
pub const MODEL_ID: &str = "model_id";
|
||||
pub const HARNESS: &str = "harness";
|
||||
pub const EXECUTION_MODE: &str = "execution_mode";
|
||||
pub const ENVIRONMENT_ID: &str = "environment_id";
|
||||
pub const WORKER_HOST: &str = "worker_host";
|
||||
pub const AUTH_SECRET: &str = "auth_secret";
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct PlanConfigApprovalToggledEvent {
|
||||
pub conversation_id: AIConversationId,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub plan_id: Option<String>,
|
||||
/// State after the toggle. The pre-toggle state is the opposite
|
||||
/// since this event only fires on a binary flip.
|
||||
pub status: OrchestrationApprovalStatus,
|
||||
pub execution_mode: OrchestrationExecutionModeKind,
|
||||
pub harness: OrchestrationHarnessKind,
|
||||
pub has_model: bool,
|
||||
pub has_environment: bool,
|
||||
pub has_worker_host: bool,
|
||||
pub has_auth_secret: bool,
|
||||
}
|
||||
|
||||
/// Decision a user took on the run_agents confirmation card.
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum RunAgentsCardDecision {
|
||||
Accept,
|
||||
AcceptWithoutOrchestration,
|
||||
Reject,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct RunAgentsCardDecisionEvent {
|
||||
pub conversation_id: AIConversationId,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub plan_id: Option<String>,
|
||||
pub decision: RunAgentsCardDecision,
|
||||
pub agent_count: usize,
|
||||
pub harness: OrchestrationHarnessKind,
|
||||
pub execution_mode: OrchestrationExecutionModeKind,
|
||||
/// Field names from [`orchestration_modified_field`] that diverged
|
||||
/// between the dispatched request and the LLM's original
|
||||
/// `RunAgentsRequest`. Empty when the user accepted without edits.
|
||||
pub modified_fields_from_tool_call: Vec<&'static str>,
|
||||
/// Same shape, but compared against the approved orchestration
|
||||
/// config snapshot. Empty when no approved config exists or the
|
||||
/// dispatched request matches it.
|
||||
pub modified_fields_from_active_config: Vec<&'static str>,
|
||||
pub had_active_config: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub active_config_status: Option<OrchestrationApprovalStatus>,
|
||||
}
|
||||
|
||||
/// Surface that first introduced orchestration into a conversation.
|
||||
///
|
||||
/// Plan-card surfacing is intentionally NOT a variant here — that signal
|
||||
/// is covered by [`AgentProposedConfigEvent`] (fires once per plan card
|
||||
/// instance when an agent-authored snapshot first becomes visible) plus
|
||||
/// [`PlanConfigApprovalToggledEvent`] (the user's approval toggle).
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum OrchestrationEntrySource {
|
||||
/// `/orchestrate` slash-command mode on a user query.
|
||||
SlashCommandOrchestrate,
|
||||
/// `run_agents` confirmation card was shown (not auto-launched).
|
||||
RunAgentsCardShown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct OrchestrationEnteredEvent {
|
||||
pub conversation_id: AIConversationId,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub plan_id: Option<String>,
|
||||
pub entry_source: OrchestrationEntrySource,
|
||||
}
|
||||
|
||||
/// Fires when an agent-authored orchestration config snapshot first
|
||||
/// becomes visible to the user on a plan card. One emission per
|
||||
/// `OrchestrationConfigBlockView` instance.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct AgentProposedConfigEvent {
|
||||
pub conversation_id: AIConversationId,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub plan_id: Option<String>,
|
||||
pub harness: OrchestrationHarnessKind,
|
||||
pub execution_mode: OrchestrationExecutionModeKind,
|
||||
pub has_model: bool,
|
||||
pub has_environment: bool,
|
||||
pub has_worker_host: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum PillBarPillKind {
|
||||
Orchestrator,
|
||||
Child,
|
||||
}
|
||||
|
||||
/// Concrete user actions against an orchestration pill bar entry.
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum PillBarActionKind {
|
||||
/// User clicked the pill body. See `switch_outcome` for what
|
||||
/// happened next.
|
||||
Switch,
|
||||
OpenInNewPane,
|
||||
OpenInNewTab,
|
||||
/// User picked "Focus pane" from a pill's 3-dot menu. Distinct
|
||||
/// from a pill-body click that resolves to the same outcome
|
||||
/// (those are `Switch` with `switch_outcome = focused_existing_pane`).
|
||||
FocusOpenedConversation,
|
||||
Stop,
|
||||
Kill,
|
||||
TogglePinOn,
|
||||
TogglePinOff,
|
||||
ViewInOz,
|
||||
OpenMenu,
|
||||
}
|
||||
|
||||
/// Outcome of a pill-body click. Closed enum so future navigation
|
||||
/// outcomes can be added without splitting `Switch` into multiple
|
||||
/// action variants again.
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum PillSwitchOutcome {
|
||||
/// Pill click navigated within the current pane.
|
||||
SwitchedInPlace,
|
||||
/// Target conversation was already owned by another visible
|
||||
/// terminal view; focus moved there instead of switching in place.
|
||||
FocusedExistingPane,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct PillBarInteractionEvent {
|
||||
pub action: PillBarActionKind,
|
||||
pub pill_kind: PillBarPillKind,
|
||||
pub total_pills: usize,
|
||||
pub total_pinned: usize,
|
||||
/// The orchestrator that hosts the pill bar.
|
||||
pub source_conversation_id: AIConversationId,
|
||||
/// The pill the action targets.
|
||||
pub target_conversation_id: AIConversationId,
|
||||
/// Present only when `action == Switch`. Distinguishes whether the
|
||||
/// pill-body click navigated within the current pane or moved
|
||||
/// focus to an existing pane already owning the conversation.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub switch_outcome: Option<PillSwitchOutcome>,
|
||||
}
|
||||
|
||||
impl TelemetryEvent for BlocklistOrchestrationTelemetryEvent {
|
||||
fn name(&self) -> &'static str {
|
||||
BlocklistOrchestrationTelemetryEventDiscriminants::from(self).name()
|
||||
@@ -66,6 +287,11 @@ impl TelemetryEvent for BlocklistOrchestrationTelemetryEvent {
|
||||
fn payload(&self) -> Option<Value> {
|
||||
match self {
|
||||
Self::TeamAgentCommunicationFailed(event) => Some(json!(event)),
|
||||
Self::PlanConfigApprovalToggled(event) => Some(json!(event)),
|
||||
Self::RunAgentsCardDecision(event) => Some(json!(event)),
|
||||
Self::PillBarInteraction(event) => Some(json!(event)),
|
||||
Self::OrchestrationEntered(event) => Some(json!(event)),
|
||||
Self::AgentProposedConfig(event) => Some(json!(event)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +318,11 @@ impl TelemetryEventDesc for BlocklistOrchestrationTelemetryEventDiscriminants {
|
||||
Self::TeamAgentCommunicationFailed => {
|
||||
"AgentMode.Orchestration.TeamAgentCommunicationFailed"
|
||||
}
|
||||
Self::PlanConfigApprovalToggled => "AgentMode.Orchestration.PlanConfigApprovalToggled",
|
||||
Self::RunAgentsCardDecision => "AgentMode.Orchestration.RunAgentsCardDecision",
|
||||
Self::PillBarInteraction => "AgentMode.Orchestration.PillBarInteraction",
|
||||
Self::OrchestrationEntered => "AgentMode.Orchestration.Entered",
|
||||
Self::AgentProposedConfig => "AgentMode.Orchestration.AgentProposedConfig",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +331,21 @@ impl TelemetryEventDesc for BlocklistOrchestrationTelemetryEventDiscriminants {
|
||||
Self::TeamAgentCommunicationFailed => {
|
||||
"Failed to send an orchestration message or lifecycle event for a TeamAgent"
|
||||
}
|
||||
Self::PlanConfigApprovalToggled => {
|
||||
"User toggled the Use orchestration switch on a plan card"
|
||||
}
|
||||
Self::RunAgentsCardDecision => {
|
||||
"User accepted, accepted-without-orchestration, or rejected a run_agents confirmation card. Reports which config fields diverged from the original tool call and/or the active approved config."
|
||||
}
|
||||
Self::PillBarInteraction => {
|
||||
"User interacted with the orchestration pill bar (switch, pin, open in pane/tab, stop, kill, etc.)"
|
||||
}
|
||||
Self::OrchestrationEntered => {
|
||||
"Orchestration was activated in a conversation via /orchestrate or a run_agents confirmation card surfacing. Plan-card entries are tracked separately via AgentProposedConfig + PlanConfigApprovalToggled."
|
||||
}
|
||||
Self::AgentProposedConfig => {
|
||||
"An agent-authored orchestration config snapshot first became visible to the user on a plan card"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user