Problem
nous status --line and nous status --watch promise a "last tool" indicator so the operator can see what the agent just ran (e.g., last=Bash, last=Edit). In production it's always empty.
Repro
Run any SDK-backed campaign and check status mid-run:
nous status <work_dir> --line
# Output: "<run> · DESIGN · iter 0 · 0 done · 0 principles" ← no `last=` field
Look at the streaming log directly — there are plenty of tool calls happening:
tail -2 <work_dir>/runs/iter-1/inputs/executor_log.jsonl
# {"type": "AssistantMessage", "ts": ..., "content": "[ToolUseBlock(id='...', name='Bash', input={...})]"}
So the data exists; the status reader just isn't extracting it.
Root cause
orchestrator/sdk_dispatch.py::_tee_event (the function that records each SDK message to executor_log.jsonl) reads:
for field_name in ("tool_name", "tool_use_id", "content"):
val = getattr(message, field_name, None)
if val is not None and not callable(val):
...
record[field_name] = val
But on an AssistantMessage, tool_name is not a top-level attribute. It lives on ToolUseBlock instances inside message.content (a list of content blocks: TextBlock, ThinkingBlock, ToolUseBlock). So getattr(message, "tool_name", None) always returns None, and the JSONL record never contains a tool_name field.
orchestrator/status.py::format_one_liner then looks for tool = snap.last_event.get("tool_name") or snap.last_event.get("tool"), finds nothing, and renders the line without a last= token.
Fix
In _tee_event, walk the content blocks and extract tool names:
content = getattr(message, "content", None)
if isinstance(content, list):
tool_names = []
for block in content:
name = getattr(block, "name", None) or getattr(block, "tool_name", None)
if name:
tool_names.append(name)
if tool_names:
# Last tool wins (most recent) — or join all
record["tool_name"] = tool_names[-1]
Files to touch
orchestrator/sdk_dispatch.py::_tee_event — extract tool_name from ToolUseBlock entries.
tests/test_sdk_dispatch.py — assert that after a dispatch with a Bash tool call, the JSONL row has tool_name == "Bash".
tests/test_status.py — drive a fixture log with a Bash event, assert format_one_liner includes last=Bash.
Discovered in
paper-burst friction-test, 2026-05-26.
Problem
nous status --lineandnous status --watchpromise a "last tool" indicator so the operator can see what the agent just ran (e.g.,last=Bash,last=Edit). In production it's always empty.Repro
Run any SDK-backed campaign and check status mid-run:
Look at the streaming log directly — there are plenty of tool calls happening:
So the data exists; the status reader just isn't extracting it.
Root cause
orchestrator/sdk_dispatch.py::_tee_event(the function that records each SDK message toexecutor_log.jsonl) reads:But on an
AssistantMessage,tool_nameis not a top-level attribute. It lives onToolUseBlockinstances insidemessage.content(a list of content blocks:TextBlock,ThinkingBlock,ToolUseBlock). Sogetattr(message, "tool_name", None)always returnsNone, and the JSONL record never contains atool_namefield.orchestrator/status.py::format_one_linerthen looks fortool = snap.last_event.get("tool_name") or snap.last_event.get("tool"), finds nothing, and renders the line without alast=token.Fix
In
_tee_event, walk the content blocks and extract tool names:Files to touch
orchestrator/sdk_dispatch.py::_tee_event— extract tool_name from ToolUseBlock entries.tests/test_sdk_dispatch.py— assert that after a dispatch with a Bash tool call, the JSONL row hastool_name == "Bash".tests/test_status.py— drive a fixture log with a Bash event, assertformat_one_linerincludeslast=Bash.Discovered in
paper-burst friction-test, 2026-05-26.