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:
foldSubagentActivitiesonly skipsagentKind !== "agent", so the watcher joins the Agents roster.task.startedmarks itrunning, 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 theopus-5-5 · highchip. With notypedUsage, the row falls back to— tok. - The same set drives sidebar liveness.
monitor_wsgoes 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.