Repository: moeru-ai/airi
File: packages/stage-pages/src/pages/devtools/context-flow.vue (Vue)

Code before:
  }
  catch {
    pushEntry({
      direction: 'incoming',
      channel: 'devtools',
      type: 'spark:notify',
      summary: 'invalid json',
      payload: { raw },
    })
    return
  }

  const destinations = Array.isArray(parsed?.destinations) ? parsed.destinations.filter((d: unknown) => typeof d === 'string') : []
  if (!parsed?.headline || !destinations.length) {
    pushEntry({
      direction: 'incoming',
      channel: 'devtools',
      type: 'spark:notify',
      summary: 'missing required fields (headline, destinations[])',
      payload: parsed,
    })
    return
  }

  const notify = {
    id: typeof parsed.id === 'string' && parsed.id ? parsed.id : nanoid(),
    eventId: typeof parsed.eventId === 'string' && parsed.eventId ? parsed.eventId : nanoid(),
    lane: typeof parsed.lane === 'string' ? parsed.lane : undefined,
    kind: parsed.kind === 'alarm' || parsed.kind === 'ping' || parsed.kind === 'reminder' ? parsed.kind : 'ping',
    urgency: parsed.urgency === 'immediate' || parsed.urgency === 'soon' || parsed.urgency === 'later' ? parsed.urgency : 'immediate',
    headline: String(parsed.headline),
    note: typeof parsed.note === 'string' ? parsed.note : undefined,
    payload: parsed.payload && typeof parsed.payload === 'object' ? parsed.payload : undefined,
    ttlMs: typeof parsed.ttlMs === 'number' ? parsed.ttlMs : undefined,
    requiresAck: typeof parsed.requiresAck === 'boolean' ? parsed.requiresAck : undefined,
    destinations,
    metadata: parsed.metadata && typeof parsed.metadata === 'object' ? parsed.metadata : undefined,
  }

  const simulatedEvent: WebSocketBaseEvent<'spark:notify', WebSocketEvents['spark:notify']> = {
    type: 'spark:notify',
    source: 'devtools',
    data: notify,
  }

  pushEntry({
    direction: 'incoming',
    channel: 'server',
    type: 'spark:notify',
    summary: summarizeServerEvent(simulatedEvent as any),
    payload: simulatedEvent,

Review comment (suggestion):
```suggestion
  // TODO(@nekomeowww): improve server event, support to have zod or valibot schema validation for better cross runtime handling
  const notify = {
```

Code after:
  }
  catch {
    pushEntry({
      direction: 'incoming',
      channel: 'devtools',
      type: 'spark:notify',
      summary: 'invalid json',
      payload: { raw },
    })
    return
  }

  const destinations = Array.isArray(parsed?.destinations) ? parsed.destinations.filter((d: unknown) => typeof d === 'string') : []
  if (!parsed?.headline || !destinations.length) {
    pushEntry({
      direction: 'incoming',
      channel: 'devtools',
      type: 'spark:notify',
      summary: 'missing required fields (headline, destinations[])',
      payload: parsed,
    })
    return
  }

  // TODO(@nekomeowww): improve server event, support to have zod or valibot schema validation for better cross runtime handling
  const notify = {
    id: typeof parsed.id === 'string' && parsed.id ? parsed.id : nanoid(),
    eventId: typeof parsed.eventId === 'string' && parsed.eventId ? parsed.eventId : nanoid(),
    lane: typeof parsed.lane === 'string' ? parsed.lane : undefined,
    kind: parsed.kind === 'alarm' || parsed.kind === 'ping' || parsed.kind === 'reminder' ? parsed.kind : 'ping',
    urgency: parsed.urgency === 'immediate' || parsed.urgency === 'soon' || parsed.urgency === 'later' ? parsed.urgency : 'immediate',
    headline: String(parsed.headline),
    note: typeof parsed.note === 'string' ? parsed.note : undefined,
    payload: parsed.payload && typeof parsed.payload === 'object' ? parsed.payload : undefined,
    ttlMs: typeof parsed.ttlMs === 'number' ? parsed.ttlMs : undefined,
    requiresAck: typeof parsed.requiresAck === 'boolean' ? parsed.requiresAck : undefined,
    destinations,
    metadata: parsed.metadata && typeof parsed.metadata === 'object' ? parsed.metadata : undefined,
  }

  const simulatedEvent: WebSocketBaseEvent<'spark:notify', WebSocketEvents['spark:notify']> = {
    type: 'spark:notify',
    source: 'devtools',
    data: notify,
  }

  pushEntry({
    direction: 'incoming',
    channel: 'server',
    type: 'spark:notify',
    summary: summarizeServerEvent(simulatedEvent as any),