aaif-goose / aaif-goose/goose

Add structured slash-command lifecycle events to AgentEvent

オープン
#9,261 コメント 9 件 リアクション 0 件 担当者 1 名 @lifeizhou-ap が担当を希望しています GitHub で見る
主要言語
Rust
スター
54.2k
フォーク
6.2k
平均マージ
3日 2時間
マージ済み PR(30日)
262

説明

# 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

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。