Add structured slash-command lifecycle events to AgentEvent
- Lenguaje dominante
- Rust
- Estrellas
- 54.2k
- Forks
- 6.2k
- Merge medio
- 3 d 2 h
- PR fusionados (30 d)
- 262
Descripción
# Add structured slash-command lifecycle events to AgentEvent
## Summary
Currently, consumers of Goose agent streams (ACP, Desktop, CLI integrations, etc.) must infer slash-command execution state indirectly by inspecting user messages and reproducing internal execution logic.
This became particularly apparent during the ACP integration work in:
- #8925
ACP currently has to manually:
- parse slash commands from raw user text
- resolve command mappings
- check command existence
- synthesize its own “Running command…” notifications
This duplicates logic from `execute_commands.rs` and creates a fragile coupling between consumers and internal implementation details.
## Problem
The slash-command execution subsystem currently owns the authoritative execution state for commands, but does not expose that state through the agent event stream.
As a result:
- ACP and other consumers must guess what is happening internally
- consumers may diverge from CLI/Desktop behavior
- nested or future hierarchical command execution becomes difficult to represent cleanly
- parallel command execution cannot be represented robustly
- command failures cannot be surfaced structurally
The current workaround in ACP looks roughly like:
```rust
if let Some(parsed) = parse_slash_command(&message_text) {
let full_command = format!(“/{}”, parsed.command);
if let Some(recipe_path) =
crate::slash_commands::get_recipe_for_command(&full_command)
{
if recipe_path.exists() {
// synthesize notification
}
}
}
```
This logic should instead live in the execution layer and emit authoritative lifecycle events.
## Proposed design
Add a new `AgentEvent::SlashCommand(…)` variant.
```rust
#[derive(Clone, Debug)]
pub enum AgentEvent {
Message(Message),
McpNotification((String, ServerNotification)),
HistoryReplaced(Conversation),
SlashCommand(SlashCommandEvent),
}
```
```rust
#[derive(Clone, Debug)]
pub enum SlashCommandEvent {
ExecutionStarted {
execution_id: String,
parent_execution_id: Option,
command: String,
},
ExecutionCompleted {
execution_id: String,
},
ExecutionFailed {
execution_id: String,
kind: Option,
message: String,
},
ExecutionCancelled {
execution_id: String,
},
}
```
## Why include execution_id?
This supports:
- nested slash commands / subcommands
- subagents
- repeated invocations of the same command
- future hierarchical execution
- parallel execution
Example:
```text
/main execution_id=A
/subtask1 execution_id=B parent=A
/subtask2 execution_id=C parent=A
```
Without execution IDs, overlapping executions become ambiguous.
## Why `kind: Option` instead of a fixed enum?
A fixed error taxonomy would assume all current and future slash commands share the same lifecycle model.
That assumption may not hold as Goose evolves toward:
- subagents
- nested orchestration
- future command runtimes
- non-recipe-backed commands
Using:
```rust
kind: Option
```
allows implementations to provide machine-readable hints without forcing a universal lifecycle taxonomy.
Examples:
```text
parse
resolve
validate
build
execute
subagent.spawn
permission.denied
```
Consumers that do not care about structured failure kinds can simply display `message`.
## Why not add granular progress/stage events?
Goose already emits runtime progress through existing event types:
- `AgentEvent::Message`
- `AgentEvent::McpNotification`
- tool request/response messages
- system notifications
The missing abstraction is primarily:
- authoritative execution start
- authoritative execution completion
- authoritative execution failure/cancellation
Adding additional stage/progress events would increase API surface area without solving the core synchronization problem.
## Lifecycle expectations
Expected lifecycle:
```text
ExecutionStarted
→ normal agent/tool/MCP events
→ exactly one terminal event:
- ExecutionCompleted
- ExecutionFailed
- ExecutionCancelled
```
`ExecutionStarted` should be emitted before execution-related work begins.
## Impact on ACP
ACP could replace speculative pre-processing logic with direct event handling:
```rust
AgentEvent::SlashCommand(
SlashCommandEvent::ExecutionStarted { command, .. }
)
```
instead of:
- parsing raw messages
- resolving commands manually
- guessing execution state
This would simplify ACP substantially and make it more robust to future command-system refactors.
## Potential impact on other consumers
This could also benefit:
- Goose Desktop
- CLI status/progress rendering
- schedulers
- automation systems
- telemetry/tracing
- future subagent orchestration
- hierarchical execution visualizations
without requiring them to replicate execution logic from `execute_commands.rs`.
## Impact assessment
## Compile-time impact
Adding a new `AgentEvent` variant may break Rust code that exhaustively matches on `AgentEvent`.
Example:
```rust
match event {
AgentEvent::Message(message) => { … }
AgentEvent::McpNotification(notification) => { … }
AgentEvent::HistoryReplaced(conversation) => { … }
}
```
Consumers will need to add:
- explicit `SlashCommand(…)` handling
- a wildcard arm
## Serialization / IPC impact
If `AgentEvent` is serialized across ACP/Desktop/IPC boundaries, consumers may need schema updates.
Consumers that ignore unknown variants should continue functioning normally.
## Runtime impact
This proposal is additive and should not change slash-command execution semantics.
Consumers that ignore the new event variant should continue operating normally.
## Migration considerations
To reduce downstream breakage:
- update all in-tree `match AgentEvent` sites
- document the new variant clearly
- add migration examples for ACP/Desktop consumers
- consider `#[non_exhaustive]` for `AgentEvent` in the future if external consumers are expected
## Implementation notes
Likely emission points:
- `execute_commands.rs`
- slash-command dispatch layer
- future subagent dispatch layer
Suggested `execution_id` generation:
- UUIDv4 or equivalent
- uniqueness only needs to be guaranteed within the agent process lifetime
## Suggested follow-up work
- integration tests for:
- nested commands
- parallel commands
- cancellation
- failure before completion
- update ACP/OpenAPI/event schemas if applicable
- add tracing correlation using `execution_id` where useful
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.