aaif-goose / aaif-goose/goose

Add structured slash-command lifecycle events to AgentEvent

Abierto
#9,261 9 comentarios 0 reacciones 1 asignado Reclamado por @lifeizhou-ap Ver en GitHub
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

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.