Skip to content

feat(web): forward SDK lifecycle events to Web UI (tasks + hooks) #975

@Wirasm

Description

@Wirasm

Migrated from dynamous-community/remote-coding-agent#933 — Archon active development has moved to coleam00/Archon. Original issue retained as historical reference.


Problem

The Claude SDK emits lifecycle events for subagent tasks and hooks that we ignore. Users see nothing when a DAG node spawns subagents or when hooks fire — they wait blindly until the node completes.

Events to forward

Task lifecycle (subagent visibility)

SDK event Subtype Key fields When it fires
SDKTaskStartedMessage task_started task_id, description, task_type?, prompt? Subagent spawned
SDKTaskProgressMessage task_progress task_id, description, usage, last_tool_name?, summary? Subagent working (~30s intervals with agentProgressSummaries)
SDKTaskNotificationMessage task_notification task_id, status, summary, usage? Subagent completed/failed/stopped

Currently these hit the else branch in claude.ts and get logged as claude.system_message_unhandled.

Hook lifecycle (hook observability)

SDK event Subtype Key fields When it fires
SDKHookStartedMessage hook_started hook_id, hook_name, hook_event Hook callback invoked
SDKHookProgressMessage hook_progress hook_id, hook_name, stdout, stderr Hook running
SDKHookResponseMessage hook_response hook_id, hook_name, outcome, exit_code? Hook finished

Available since SDK v0.2.89 (just bumped). Per-node hooks (#445) already work but events are invisible.

Implementation

Phase 1: Yield from claude.ts

Add handling for system subtype messages:

case 'task_started':
  yield { type: 'task_started', taskId: msg.task_id, description: msg.description };
  break;
case 'task_progress':
  yield { type: 'task_progress', taskId: msg.task_id, summary: msg.summary, usage: msg.usage };
  break;
case 'task_notification':
  yield { type: 'task_notification', taskId: msg.task_id, status: msg.status, summary: msg.summary };
  break;
case 'hook_started':
  yield { type: 'hook_started', hookId: msg.hook_id, hookName: msg.hook_name, hookEvent: msg.hook_event };
  break;
case 'hook_response':
  yield { type: 'hook_response', hookId: msg.hook_id, outcome: msg.outcome };
  break;

Phase 2: Forward through SSE

Add new SSE event types in WorkflowEventEmitter and workflow-bridge.ts:

  • task_activity — combines task_started/progress/notification
  • hook_activity — combines hook_started/progress/response

Phase 3: Render in Web UI

  • Task events: show as expandable sub-items under the parent node in run detail view
  • Hook events: show as inline indicators (e.g., "PreToolUse(Bash) → approved")
  • agentProgressSummaries: true should be enabled by default for workflow nodes to get AI-generated progress summaries

Phase 4: Enable agentProgressSummaries

Add agentProgressSummaries: true to the SDK options in claude.ts when running workflow nodes. This makes task_progress events include an AI-generated summary field every ~30s.

Acceptance criteria

  • Task started/progress/completed events forwarded to Web UI via SSE
  • Hook started/response events forwarded to Web UI via SSE
  • Web UI shows subagent activity during workflow execution
  • Web UI shows hook outcomes per node
  • agentProgressSummaries enabled for workflow nodes
  • bun run validate passes

Supersedes

#617 (task lifecycle events), #618 (hook lifecycle events)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priority - Backlog, when time permitsarea: webWeb UI (packages/web) - React frontendarea: workflowsWorkflow engineeffort/mediumFew files, one domain or module, some coordination neededfeatureNew functionality (planned)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions