# Unify plexus.call with Streaming Pattern

## Problem

Currently `plexus.call` is a special case:

```rust
#[hub_method(override_call)]
async fn call(&self, method: String, params: Option<Value>) -> Result<PlexusStream, PlexusError>
```

This breaks the uniform streaming pattern where all methods return `impl Stream<Item = T>`. The `override_call` attribute:
- Bypasses the normal `wrap_stream` wrapping
- Returns `Result<PlexusStream, _>` directly
- Has no return type schema (causes `RefUnknown` in codegen)
- Is the only method that returns errors via `Result` rather than stream events

## Solution

Make `plexus.call` return a stream like everything else:

```rust
#[hub_method(streaming)]
async fn call(&self, method: String, params: Option<Value>) -> impl Stream<Item = CallEvent> + Send
```

Where `CallEvent` wraps the dynamic response:

```rust
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CallEvent {
    /// Forwarded data from the called method
    Data { content: Value },
    /// Error from routing or execution
    Error { message: String, code: Option<i32> },
}
```

## Benefits

1. **Uniform pattern**: Every method returns a stream, errors are stream events
2. **Proper schema**: `CallEvent` has a JSON Schema, no more `RefUnknown`
3. **Remove `override_call`**: Simplifies hub-macro, one less special case
4. **TypeScript consistency**: `call()` returns `AsyncGenerator<CallEvent>` like everything else

## Changes by Library

### hub-core (`src/plexus/plexus.rs`)

1. Add `CallEvent` enum:
   ```rust
   #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
   #[serde(tag = "type", rename_all = "snake_case")]
   pub enum CallEvent {
       Data { content: Value },
       Error { message: String, code: Option<i32> },
   }
   ```

2. Change `call` method signature:
   ```rust
   #[hub_method(streaming, description = "Route a call to a registered activation")]
   async fn call(&self, method: String, params: Option<Value>) -> impl Stream<Item = CallEvent> + Send
   ```

3. Implementation:
   ```rust
   async fn call(&self, method: String, params: Option<Value>) -> impl Stream<Item = CallEvent> + Send + 'static {
       let result = self.route(&method, params.unwrap_or_default()).await;

       stream! {
           match result {
               Ok(mut stream) => {
                   while let Some(item) = stream.next().await {
                       match item {
                           PlexusStreamItem::Data { content, .. } => {
                               yield CallEvent::Data { content };
                           }
                           PlexusStreamItem::Error { message, code, .. } => {
                               yield CallEvent::Error { message, code };
                           }
                           PlexusStreamItem::Done { .. } => {
                               // Stream complete, will naturally end
                           }
                       }
                   }
               }
               Err(e) => {
                   yield CallEvent::Error {
                       message: e.to_string(),
                       code: None
                   };
               }
           }
       }
   }
   ```

### hub-macro

1. **Remove `override_call` support** (or deprecate):
   - Remove parsing of `override_call` attribute
   - Remove special codegen paths for override methods
   - Error if anyone tries to use it (with migration message)

2. **Alternatively, keep but fix**:
   - If `override_call` is still needed for edge cases, emit `schemars::schema_for!(serde_json::Value)` as return type
   - This makes it `RefAny` instead of `RefUnknown`

### synapse (Haskell)

No changes required. The IR builder already handles:
- Streaming methods (from `streaming: true` in schema)
- Discriminated unions like `CallEvent`
- The return type will be properly extracted as `RefNamed "CallEvent"`

### hub-codegen (TypeScript)

No changes required. Will generate:
```typescript
export interface PlexusClient {
  call(method: string, params?: unknown): AsyncGenerator<CallEvent>;
}
```

Where `CallEvent` is generated as a discriminated union in `types.ts`.

## Migration

1. Any code using `plexus.call` needs to handle `CallEvent` instead of raw `PlexusStreamItem`
2. Error handling moves from `Result` to checking for `CallEvent::Error` in the stream
3. The `content` field contains the actual response data (already `Value`)

## Open Questions

1. **Should `CallEvent::Data` include metadata?** Currently `PlexusStreamItem::Data` has `metadata`, `content_type`. Do callers need this?

2. **Multiple error events?** Current design allows multiple errors in the stream. Is this desired, or should we stop after first error?

3. **Keep `override_call` for internal use?** Some activations might need raw `PlexusStream` access. Could keep the attribute but emit proper schema.

## Decision

Recommend implementing option 1 (full streaming conversion) because:
- Simplifies the mental model
- Removes a whole category of special-case code
- Makes TypeScript generation cleaner
- Errors-as-events is the established pattern in this codebase
