Skip to content

An agent's answer is readable in the chat and the property panel - #57

Merged
endrix merged 3 commits into
mainfrom
feat/readable-agent-outputs
Sep 22, 2026
Merged

endrix merged 3 commits into
mainfrom
feat/readable-agent-outputs

Conversation

@endrix

@endrix endrix commented Sep 22, 2026

Copy link
Copy Markdown
Owner

A wfpy agent finishes a turn by writing its ports, and the runtime reports that as one JSON object whose port values are themselves JSON. The text reaching the UI is escaped twice, so a unified diff arrives as a single line of \n and \". In the chat it rendered as one unreadable paragraph. In the property panel it was worse: the value was truncated at 500 characters, and what did show went through the Markdown heuristic, whose list rule matches any line beginning with a dash and a space, which is every removed line of a patch. A diff therefore rendered as a bullet list with its minus signs eaten, which silently changes what the patch appears to say.

What changed

agent-outputs.ts is a new module with one job: turn an outputs object into Markdown the panel already parses and sanitizes. A heading per port, one line per scalar field, a fenced block for anything long, tagged diff when it is one. Scalars come before blocks so the options and the reason read before the patch. Prose keeps its punctuation while an option string gets backticks. A value carrying its own fence gets a longer one. Anything that is not an outputs object returns null and goes through untouched, so ordinary replies are unaffected.

The property panel shares that module's three predicates rather than copying them, so the two views cannot drift on what counts as a diff. Its string branch now unwraps an embedded JSON value and recurses into the tree, and sends a diff or any multi-line string to a scrolling monospace block styled from the editor's own tokens.

Before and after

A planner's round, as the chat showed it:

{"outputs":{"Next":"{\"front_options\":\"ftd=auto\",\"handshake_options\":\"target=asap7 ...

and as it shows it now:

### Next

- **front_options** `ftd=auto`
- **handshake_options** `target=asap7 decision-slots=4 ...`
- **stop** `false`

**model_patch**

    --- a/gen/membus/memif.py
    +++ b/gen/membus/memif.py
    @@ -64,7 +64,8 @@
    -        ("read", "finish_prefetch", "hold"),
    +        ("read", "finish_prefetch", "idle"),

Testing

Nine tests on the new module, including a real planner answer from a run, a bullet list that must not be taken for a diff, a broken JSON string that must be returned untouched, and a value containing its own fence. The package's 198 tests pass and all five packages type-check.

A wfpy agent finishes a turn by writing its ports, and the runtime reports
that as one JSON object whose port values are themselves JSON. The text
reaching the chat is therefore escaped twice: a unified diff arrives as a
single line of \n and \" and renders as one unreadable paragraph, which
is what the planner's rounds have looked like all along.

agent-outputs.ts turns such a message into Markdown, which the panel
already parses and sanitizes: a heading per port, one line per scalar
field, and a fenced block for anything long, tagged diff when it is one.
Scalars come before blocks so the options and the reason read before the
patch. Prose keeps its punctuation; an option string gets backticks. A
value carrying its own fence gets a longer one. Anything that is not an
outputs object returns null and goes through untouched, so ordinary
replies are unaffected.
Two faults on the same values the chat now formats. A port's value is
itself JSON, so the tree showed one escaped string truncated at 500
characters instead of the fields inside it. And a diff went through the
Markdown heuristic, whose list rule matches a line beginning with a dash
and a space, which is every removed line: a patch rendered as a bullet
list with its minus signs eaten.

The string branch now unwraps an embedded JSON value and recurses, and
sends a diff or any multi-line string to a scrolling pre instead. The
three predicates are shared with the chat's formatter rather than copied,
so the two panels agree on what a diff is.
Gate 1 of the neutrality check refuses the product names in
packages/*/src, and the doc comment opened "A wfpy agent finishes a
turn". The sentence says the same thing about any agent that writes
ports, so the name was carrying nothing.
@endrix
endrix merged commit 8ec2855 into main Sep 22, 2026
1 check passed
@endrix
endrix deleted the feat/readable-agent-outputs branch September 22, 2026 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant