Complete agent monitoring and Galaxy Control integration
- expose command-monitor conversations and preserve visible agent transcripts - add bounded polling and a dedicated shell interrupt tool - improve direct-provider images, skills, tool history, and usage handling - package and brand Galaxy Control across releases, installers, persistence, and docs
This commit is contained in:
@@ -314,6 +314,20 @@ impl AIAgentActionType {
|
||||
matches!(self, Self::WriteToLongRunningShellCommand { .. })
|
||||
}
|
||||
|
||||
/// Returns whether this action represents an exact terminal interrupt (Ctrl+C).
|
||||
///
|
||||
/// Direct AI providers expose a typed `interrupt_shell_command` tool, but encode it through
|
||||
/// the existing write-to-PTY protobuf for backwards compatibility. Keeping this predicate on
|
||||
/// the shared action type lets execution and UI code distinguish that typed operation from
|
||||
/// ordinary process input without relying on printable escape spellings from the model.
|
||||
pub fn is_shell_command_interrupt(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::WriteToLongRunningShellCommand { input, mode, .. }
|
||||
if mode.is_shell_interrupt(input)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cancelled_result(&self) -> AIAgentActionResultType {
|
||||
match self {
|
||||
Self::RequestCommandOutput { .. } => AIAgentActionResultType::RequestCommandOutput(
|
||||
@@ -802,6 +816,12 @@ pub enum AIAgentPtyWriteMode {
|
||||
}
|
||||
|
||||
impl AIAgentPtyWriteMode {
|
||||
pub fn is_shell_interrupt(self, bytes: &[u8]) -> bool {
|
||||
use galaxy_terminal::model::escape_sequences;
|
||||
|
||||
self == Self::Raw && bytes == [escape_sequences::C0::ETX]
|
||||
}
|
||||
|
||||
/// Decorates input bytes according to the write mode.
|
||||
pub fn decorate_bytes(
|
||||
self,
|
||||
@@ -928,3 +948,7 @@ impl FileEdit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "mod_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
use galaxy_terminal::model::escape_sequences;
|
||||
|
||||
use super::AIAgentPtyWriteMode;
|
||||
|
||||
#[test]
|
||||
fn raw_etx_is_the_only_shell_interrupt_payload() {
|
||||
assert!(AIAgentPtyWriteMode::Raw.is_shell_interrupt(&[escape_sequences::C0::ETX]));
|
||||
assert!(!AIAgentPtyWriteMode::Raw.is_shell_interrupt(b"C-c"));
|
||||
assert!(!AIAgentPtyWriteMode::Raw.is_shell_interrupt(br"\u0003"));
|
||||
assert!(!AIAgentPtyWriteMode::Line.is_shell_interrupt(&[escape_sequences::C0::ETX]));
|
||||
assert!(!AIAgentPtyWriteMode::Block.is_shell_interrupt(&[escape_sequences::C0::ETX]));
|
||||
}
|
||||
@@ -120,8 +120,8 @@ pub fn parse_skill(path: &Path) -> Result<ParsedSkill> {
|
||||
/// Parse a bundled skill markdown file.
|
||||
///
|
||||
/// Unlike `parse_skill`, this function does not require the path to match a known
|
||||
/// skill provider directory. Bundled skills are always assigned `SkillProvider::Warp`
|
||||
/// and `SkillScope::Bundled`.
|
||||
/// skill provider directory. Bundled Galaxy skills retain the wire-compatible
|
||||
/// `SkillProvider::Warp` variant and use `SkillScope::Bundled`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `path` - Path to the skill markdown file to parse
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Skill provider definitions and utilities.
|
||||
//!
|
||||
//! This module defines the supported skill providers (i.e. Agents, Claude, Codex, Warp) and their
|
||||
//! This module defines the supported skill providers (i.e. Agents, Claude, Codex, Galaxy) and their
|
||||
//! associated skills directory paths. It provides utilities for looking up providers
|
||||
//! from paths and vice versa.
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -14,7 +14,9 @@ use galaxy_util::local_or_remote_path::LocalOrRemotePath;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{Display, EnumString, VariantNames};
|
||||
|
||||
/// Represents a skill provider/origin (Agents, Claude, Codex, or Warp).
|
||||
/// Represents a skill provider/origin.
|
||||
///
|
||||
/// `Warp` is retained as the wire-compatible variant for Galaxy-managed skills.
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -62,7 +64,7 @@ pub enum SkillScope {
|
||||
Home,
|
||||
/// Skills from a project directory (e.g., `./repo/.agents/skills`).
|
||||
Project,
|
||||
/// Bundled skills distributed with Warp.
|
||||
/// Bundled skills distributed with Galaxy.
|
||||
Bundled,
|
||||
}
|
||||
|
||||
@@ -82,11 +84,11 @@ impl SkillProvider {
|
||||
SkillProvider::Gemini => Icon::GeminiLogo,
|
||||
SkillProvider::Droid => Icon::DroidLogo,
|
||||
SkillProvider::OpenCode => Icon::OpenCodeLogo,
|
||||
SkillProvider::Warp
|
||||
| SkillProvider::Agents
|
||||
SkillProvider::Warp => Icon::GalaxyLogo,
|
||||
SkillProvider::Agents
|
||||
| SkillProvider::Cursor
|
||||
| SkillProvider::Copilot
|
||||
| SkillProvider::Github => Icon::WarpLogoLight,
|
||||
| SkillProvider::Github => Icon::GalaxyLogo,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use galaxy_core::ui::icons::Icon;
|
||||
use galaxy_util::host_id::HostId;
|
||||
use galaxy_util::local_or_remote_path::LocalOrRemotePath;
|
||||
use galaxy_util::remote_path::RemotePath;
|
||||
@@ -9,7 +10,7 @@ use super::{
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn warp_home_skills_path_uses_warp_home_path() {
|
||||
fn galaxy_managed_home_skills_path_uses_galaxy_home_path() {
|
||||
assert_eq!(
|
||||
home_skills_path(SkillProvider::Warp),
|
||||
galaxy_core::paths::galaxy_home_skills_dir()
|
||||
@@ -17,12 +18,12 @@ fn warp_home_skills_path_uses_warp_home_path() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warp_home_skill_path_is_home_warp_skill() {
|
||||
let Some(warp_home_skills_dir) = galaxy_core::paths::galaxy_home_skills_dir() else {
|
||||
fn galaxy_managed_home_skill_path_is_home_skill() {
|
||||
let Some(galaxy_home_skills_dir) = galaxy_core::paths::galaxy_home_skills_dir() else {
|
||||
eprintln!("Skipping test: home directory not available");
|
||||
return;
|
||||
};
|
||||
let path = warp_home_skills_dir.join("my-skill").join("SKILL.md");
|
||||
let path = galaxy_home_skills_dir.join("my-skill").join("SKILL.md");
|
||||
|
||||
assert_eq!(
|
||||
get_provider_for_path(&LocalOrRemotePath::Local(path.clone())),
|
||||
@@ -31,6 +32,11 @@ fn warp_home_skill_path_is_home_warp_skill() {
|
||||
assert_eq!(get_scope_for_path(&path), SkillScope::Home);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn galaxy_managed_skills_use_the_galaxy_logo() {
|
||||
assert_eq!(SkillProvider::Warp.icon(), Icon::GalaxyLogo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_provider_path_is_classified_by_structure() {
|
||||
let path = LocalOrRemotePath::Remote(RemotePath::new(
|
||||
|
||||
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
pub enum SkillReference {
|
||||
/// A skill identified by the path to its SKILL.md file.
|
||||
Path(LocalOrRemotePath),
|
||||
/// A bundled skill distributed with Warp.
|
||||
/// A bundled skill distributed with Galaxy.
|
||||
BundledSkillId(String),
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ impl fmt::Display for SkillReference {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
SkillReference::Path(path) => path.display_path().fmt(f),
|
||||
SkillReference::BundledSkillId(id) => write!(f, "@warp-skill:{id}"),
|
||||
SkillReference::BundledSkillId(id) => write!(f, "@galaxy-skill:{id}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user