[Bug]: Claude Code's artifact live-updates watcher is listed in the Agents panel as a working agent with a model and effort#13978

openedSep 27, 2026byVVantrongs

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. In a Claude thread, have Claude publish a claude.ai artifact, or create a doc through the claude.ai Docs connector. Claude Code arms a background watcher for it (live updates for artifact https://claude.ai/artifact/<id>, a monitor_ws task).
  2. Open the Agents panel.

Expected behavior

Watchers are shown as what they are, for example a "Watching artifact" row without model, effort, or a working spinner, and they are not counted as working agents. Or they are kept out of the Agents panel, as #10375 notes for other background tasks.

Actual behavior

The Agents panel lists the watcher next to real subagents, with the session's model and effort (opus-5-5 · high), a spinner and "Working", and it adds to the "1 working" counter. It is not an agent: it runs no model, uses no tokens (— tok), and can stay armed for hours. Seeing "working for 1h 34m" in the Agents panel, I took it for a stuck subagent.

Related: #10375, where foreground Bash tasks get the subagent icon although the Agents panel excludes them.

Impact

Minor bug or occasional failure

Version or commit

0.0.43-nightly.20260926.2282

Environment

Desktop app on Linux (NixOS, Wayland, niri); Claude Code 2.1.283, Opus 5.5

Logs or stack traces

shell
1

Screenshots, recordings, or supporting files

No response

Workaround

No response

Triage

Triaged against main @ de251fc. Confirmed. Not a duplicate of #10375, and not fixed on current main — 0.0.43-nightly.20260926.2282 is not behind a fix.

What’s going on

Publishing a claude.ai artifact auto-arms a background monitor_ws task (Claude Code’s Artifact tool; a Monitor-tool websocket watch, description like live updates for artifact …). It is not a subagent. The Claude Agent SDK lists monitor_ws next to monitor_mcp as work that must not defer the turn (test_shell_and_monitor_tasks_never_defer_the_close in tests/test_query.py). It runs no model and reports no usage.

MONITOR_TASK_TYPES in packages/contracts/src/providerRuntime.ts is monitor, monitor_mcp, local_bash, and shell. monitor_ws is missing. classifyTaskAgentKind is a denylist that treats an unknown taskType as an agent, so ingestion stamps agentKind: "agent".

That stamp is what every surface trusts:

  • foldSubagentActivities only skips agentKind !== "agent", so the watcher joins the Agents roster. task.started marks it running, which the panel labels “Working” and counts in “N working”.
  • On task_started, the Claude adapter copies the session model and effort when the launching tool has no override. That is the opus-5-5 · high chip. With no typedUsage, the row falls back to — tok.
  • The same set drives sidebar liveness. monitor_ws goes in the agents bucket, not monitors, so the thread pill stays pulsing “Working” (with the elapsed duration) instead of the static “Monitoring” eye, and the composer banner says “1 agent working” instead of “Monitoring”.

Not #10375

#10375 is the opposite mismatch: a foreground local_bash task is already background and correctly kept out of the Agents panel, but the timeline still paints a Bot icon. Here a watch-loop type the denylist does not know is shown as a real agent.

Suggested fix

Add "monitor_ws" to MONITOR_TASK_TYPES and cover it in the classifyTaskAgentKind tests. That set is shared by the ingestion stamp, sidebar liveness, and the client fallback, so the watcher drops out of the Agents roster the same way monitor / monitor_mcp already do, and a thread whose only live work is that watcher reads as Monitoring. Do not add a special Agents-panel row; watch loops are kept off that surface on purpose.

in_process_teammate sits beside monitor_ws in the SDK’s “does not defer stdin” list. That only means it is not turn-deferring agent work — leave it out of the monitor set unless a separate report shows it should be background.

Rows already stored with agentKind: "agent" stay that way, and roster membership is sticky per taskId, so a thread that already ingested the watcher keeps it until a new task.started is written after the fix.

Synced from GitHub

Still occurs on 0.1.1-nightly.20261020.3411 (orchestrator v2). Seen from desktop (macOS) and mobile against a Linux server.

Extra impact: thread stays "Waiting" and no "Thread completed" alert fires. After the turn ends, the monitor_ws watcher (ambient: true) stays in pendingBackgroundTasks as a subagent. backgroundWorkHoldsCompletion is true, so shellRuntime parks the thread at idle, resolveSidebarThreadStatus returns waiting, and the status never reaches ready. ThreadNotificationCoordinator sends no completion alert.

The suggested fix is not enough for this. Adding monitor_ws to the monitor set still holds completion, because backgroundWorkKindHoldsCompletion returns true for monitor. Ambient watchers (ambient: true) must not hold completion.

v2 path: CLAUDE_OPAQUE_BACKGROUND_TASK_KINDS in apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts contains only local_bash, so isClaudeNonSubagentTask returns false for monitor_ws. main still has the same map.

Evidence (one thread): 4 watcher tasks stored as subagents (origin: provider_native): 3 cancelled, 1 still running. The SDK sends task_started again for the same task_id on each publish:

1{"type":"system","subtype":"task_started","task_id":"scg05h6s5","task_type":"monitor_ws","ambient":true,"description":"live updates for artifact https://claude.ai/artifact/<id> (auto-armed on publish)"}

Repro: in a Claude thread, have Claude publish an artifact, then let the turn end. Expected: thread goes to Done and you get a "Thread completed" alert. Actual: thread stays "Waiting" and no alert fires.

Triaged with t3 triage by Claude Code (Opus 5.5).

Synced from GitHub

Still occurs on 0.1.4-nightly.20261027.3770 (macOS desktop). Any Claude thread that published an artifact keeps showing as Working after the turn ends, because the auto-armed monitor_ws artifact watcher stays live. #17320 (ambient tasks should not keep a thread running) looks like the fix this needs.

Synced from GitHub

Note

🤖 Claude Opus 5.5 responding on behalf of Renzo (RenzoDriveSolo)

Still happening on T3 Code Beta with Claude Code 2.1.311. Every Claude thread that publishes an artifact stays in the "Working" group after the turn ends, even when it is waiting for my input. With several threads open I can't see which ones need me.

Getting this fixed matters a lot to us. #17320 looks like the fix. Our workaround for now is to have Claude stop the watch (ArtifactComments, on: false) right after publishing.

Synced from GitHub

Still occurs on T3 Code (Beta) 0.1.5, macOS desktop, Claude Code 2.1.318, Opus 5.5.

Extra symptom: one watcher shows up as dozens of identical rows. A thread published a single claude.ai artifact (created from a type, then filled with one publish). The task list then showed well over a dozen identical rows, all live updates for artifact https://claude.ai/artifact/<id> (auto-armed on publish) with the same artifact and the same elapsed time.

Claude Code itself reported one watcher. ArtifactComments action: "watch" (the list call) returned exactly one watch, connected and armed by the publish. Stopping it with on: false moved every row to "Stopped" at once. So this is one monitor_ws task shown many times, not many watchers.

This looks like the re-sent task_started for the same task_id noted above, with each one stored and shown as its own row instead of updating the existing one. #30911 (a resumed delegated child shown once per task record) may be the same roster-dedup gap.

To me it looked like a swarm of agents running without being asked, so I stopped the watch to make them go away.

Workaround: stop the watch (ArtifactComments, on: false) right after publishing.