feat: label process_output rows with the process command by DanielleMaywood · Pull Request #28300 · coder/coder · GitHub
Skip to content

feat: label process_output rows with the process command - #28300

Merged
DanielleMaywood merged 24 commits into
mainfrom
feat/chatd-process-output-command
Aug 20, 2026
Merged

feat: label process_output rows with the process command#28300
DanielleMaywood merged 24 commits into
mainfrom
feat/chatd-process-output-command

Conversation

@DanielleMaywood

@DanielleMaywood DanielleMaywood commented Aug 19, 2026

Copy link
Copy Markdown
Contributor
image

Every background-process poll in an agent chat transcript rendered as a generic "Process output" row. The row carried no indication of which process it belonged to, so following an agent's work meant expanding each poll and pattern-matching the output against an execute row above.

The agent's process output endpoint already loads the process's info; it just never serialized the command. This PR returns command from GET /api/v0/processes/{id}/output, propagates it through the chatd process_output tool result, and renders verb-first labels mirroring the execute tool: "Checking npm start" while the process runs, "Checked npm start" once it exits. Results without a command (older agents, historical transcripts) fall back to the previous generic label, and the field rides into the model's context too, so polled results are self-describing when several background processes are in flight.

Also aligns the output body with the execute tool's styling: tinted rounded panel with a scrollable max-h-64 viewport, replacing the bordered box and the bespoke secondary expand/collapse button. Collapse/expand is now only the standard header toggle; long output scrolls like every other tool.

Like execute, process_output now accepts an optional model_intent so the agent can say why it is checking: "Waiting for the dev server to be ready on npm start". The execute intent sanitizer is reused to strip redundant command references; calls without an intent keep the Checking/Checked labels.

command is additive and omitempty: new coderd against old agent sees the field absent and falls back; old coderd against new agent ignores it.


Implementation plan (generated during development)

Problem

process_output renders as a collapsible row labeled "Process output" with the raw output. The reader cannot tell which background process a poll belongs to without expanding it, and the row used a bespoke two-level expansion (header toggle + 54px clamp with a secondary chevron button) unlike any other tool.

Approach

Backend enrichment (chosen over frontend-only correlation in messageParsing.ts, which fails when the original execute row is absent: process_list-discovered processes, compacted transcripts, subagent chats):

  1. codersdk/workspacesdk: add Command to ProcessOutputResponse.
  2. agent/agentproc/api.go: populate from proc.info() in handleProcessOutput (already loaded for the TOCTOU-safe read).
  3. coderd/x/chatd/chattool/execute.go: add Command to ExecuteResult, copy from the response in ProcessOutput().
  4. Frontend: ProcessOutputRenderer reads result.command; ProcessOutputTool renders Checking <cmd> / Checked <cmd>, falling back to "Process output".
  5. Body restyle to match ExecuteTool: rounded-xl bg-surface-secondary/60, max-h-64 scroll viewport; remove the secondary toggle and the now-orphaned COLLAPSED_OUTPUT_HEIGHT.
  6. model_intent arg on process_output (mirrors execute), composed into the label as <intent> on <command> after sanitizing redundant command references.

Label decision: the tool's purpose is a status check ("check the result later"), so the label is verb-first with tense carrying the outcome, paralleling execute's "Ran …" / "Failed to run …". When the model supplies model_intent, the intent phrase replaces the Checking/Checked verb, as execute does with "Ran". Duration suffix and started_at/exited_at were considered and dropped (not consumed by the UI; keeps API surface minimal).

🤖 Generated by Coder Agents

The process_output tool result carried only output, exit code, and
running state, so chat transcripts rendered every poll as a generic
"Process output" row with no indication of which process produced it.

Return the process command from the agent's process output endpoint
and propagate it through the chatd tool result. The UI now renders
"Checking <command>" while the process runs and "Checked <command>"
once it exits, mirroring the execute tool's verb-first label grammar.
Results without a command (older agents, historical transcripts) fall
back to the previous generic label.

Also align the process output body with the execute tool: tinted
rounded panel with a scrollable max-h-64 viewport instead of the
bordered box, and drop the bespoke secondary expand/collapse button
in favor of the standard header toggle plus scrolling.
Mirroring the execute tool, process_output now takes an optional
model_intent describing why the agent is checking the process. The
chat header composes it with the command (e.g. "Waiting for the dev
server to be ready on npm start"), reusing the execute intent
sanitizer to strip redundant command references. Calls without an
intent keep the previous Checking/Checked labels.
@DanielleMaywood DanielleMaywood changed the title feat(chatd): label process_output rows with the process command feat: label process_output rows with the process command Aug 19, 2026
go generate emits third-party import grouping; make gen applies gci
with the repo's coder/cdr.dev prefix group on top. Run the formatter
so the gen freshness check passes.
execute and process_output rows were visually indistinguishable: same
terminal icon, same body chrome, and a static layers icon that failed
to communicate that a process was still running in the background.

- process_output now leads with the activity (pulse) icon so act and
  observe rows read differently at scan distance.
- The layers icon is replaced by a persistent status chip on the
  backgrounded execute row: pulsing dot + ticking elapsed time while
  the process is alive, exit state once an observation arrives. State
  is derived in messageParsing by correlating process_output results
  and process_signal calls with the execute row's background_process_id.
- The misleading spawn-duration suffix ("for 0ms") is suppressed on
  backgrounded execute rows; the chip carries the live state instead.
- process_output now shows a completion chip on clean exits (exit 0)
  instead of rendering no right-edge state at all.
- process_output no longer renders an "exit 0" badge on clean exits;
  the Checked verb carries success and only failures earn the red
  badge, matching the execute tool's quiet-success convention. Failed
  checks now read "Failed <command>".
- The background process chip collapses to a bare check icon with a
  tooltip on clean exit instead of spelling out "exit 0", so the
  execute row's copy button stays aligned with the process_output
  row's copy button.
The chip annotation only read process_output results, so a process
checked via process_list (or never polled) stayed "running" forever.
Also populate startedAtMs from the execute message timestamp so the
running chip shows elapsed time.
The execute row's custom grid added gap-x-2 between the header button
and the actions column, insetting its copy button 8px from the right
edge shared by every other tool row. Drop the gap; the header button
already truncates.
Following dogfooding feedback: the chip answered a question readers
weren't asking. Live state was derivable-but-stale the moment it
rendered (a transcript records what was observed, not what is true),
success states were noise, and the ticking pill mostly advertised
that the design was trying too hard. The transcript shows what the
agent saw, when it saw it; cross-process liveness dashboards are a
different feature.

Removes BackgroundProcessChip, the backgroundProcess annotation pass
in messageParsing, and their tests and stories. Keeps the durability
fixes that stood on their own: the activity icon for process_output,
suppressing the misleading spawn-duration on backgrounded execute
rows, and quiet-exit labeling on process_output.
Backgrounded execute calls with a model intent now read "<intent> in
the background using <command>" so the launch mode is visible in the
row; the intent description tells the model the framing exists so it
does not write "background" itself.

process_output stops appending the command after the intent
("<intent> on <command>"): the intent is written to be
self-sufficient, and the appended command rendered as a stutter
against the execute row's "using <command>" summary above it. Rows
without an intent keep the command-based Checking/Checked/Failed
labels.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d344b1cf72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Two review fixes:

- process_output results now serialize running:true when the process
  outlived the poll's wait, instead of only embedding it in the note
  string. The chat UI reads the flag so a completed poll over a live
  process renders "Checking <command>" (and keeps its running
  spinner) instead of "Checked <command>".
- Backgrounded execute calls without a model intent now render
  "Started <command> in the background" instead of "Ran <command>",
  restoring a background-specific label for the no-intent case.
- Update the execute schema test for the reworded model_intent
  description from the background framing change.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e87ab3c8b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.tsx
A process_output poll that timed out with running:true kept the row
labeled "Checking <command>" and suppressed the killed-by-signal icon
even when the transcript recorded a subsequent successful
process_signal for the same process. The snapshot was true at poll
time; the kill is newer information and wins.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29b731575b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.stories.tsx Outdated
The ProcessOutputRunningThenKilled story queried the indicator by
lucide's private CSS class, violating FE10. Wrap the icon in an
aria-labeled span (mirroring the execute tool's existing pattern) and
query it by role and accessible name instead.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 356c8b6032

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

background_process_id is set on both intentional run_in_background
launches and foreground commands that exceeded their timeout (so the
caller can re-attach), and the UI derived "backgrounded" from its
presence alone. A timed-out foreground command was therefore labeled
"Started ... in the background" and lost its meaningful
wall_duration_ms suffix.

ExecuteResult now carries an explicit backgrounded flag set only on
the intentional path, and the frontend derives the background label
and duration suppression from it.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a07711d0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/toolVisibility.ts Outdated
Transcripts recorded before the backgrounded result flag existed
carry the launch intent in the persisted call args
(run_in_background: true). Falling back to the args when the result
predates the flag keeps legacy rows labeled as background launches
while foreground timeouts (backgrounded omitted, not false) still
render as failures with their duration.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 4ff5319c40

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@DanielleMaywood
DanielleMaywood marked this pull request as ready for review August 19, 2026 19:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 221d924e08

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.stories.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/toolVisibility.ts Outdated
The execute tool promotes "cmd &" to background mode and strips the
ampersand locally, but the persisted tool-call args keep the original
command with no run_in_background. Those legacy rows have neither the
backgrounded flag nor the args flag, so they lost the background
label and gained a meaningless spawn-duration suffix. Detect the
trailing-ampersand form (excluding && and |&) as another legacy
fallback. Also drop a story comment that restated its assertion.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae3fa9738c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.stories.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.stories.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.stories.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/Tool.stories.tsx Outdated
SIGTERM is catchable: the agent delivers it and returns without
waiting for exit, so a successful terminate must not flip a poll
row's recorded running state to "Checked". Only an uncatchable kill
overrides the snapshot. Also drops story comments that restated
their assertions.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3672b2cf62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread site/src/pages/AgentsPage/components/ChatElements/tools/toolVisibility.ts Outdated
run_in_background in the persisted args records intent, not outcome:
a failed StartProcess returns an error result with neither flag nor
process ID, and the legacy fallback alone labeled it "Started ... in
the background". Gate the args fallback on a nonempty
background_process_id so only launches that produced a process count.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 919e7ae769

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Tool output viewports were plain scrollable divs with no tab stop, so
keyboard-only users in browsers that do not auto-focus overflow
containers could not reach content below the fold. Add
viewportTabIndex={0} to every tool ScrollArea so the regions accept
focus and scroll by keyboard. The shared ScrollArea already forwards
viewportTabIndex; a positive default there would break the one
consumer that opts out with -1, so call sites opt in explicitly.
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa1ec43ce4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

site/AGENTS.md requires every tabIndex={0} element to carry a
semantic role; the scroll-area sweep created fourteen anonymous
generic focus stops. ScrollArea now accepts viewportAriaLabel and
renders role="region" with it on the viewport when set, and each
tool output region supplies an accessible name (process output,
command output, diffs by path, file contents by name, subagent
prompt/response/report, advisor, models, summary, build log).
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3640605c1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Twelve stories had accreted across review rounds, several asserting
the same label in different fixtures. Kept the four that pin distinct
mechanisms: quiet clean exit, intent as label, running flag from a
timed-out poll, and the kill-override. Dropped duplicates whose
behavior is already covered by the sanitizer unit tests or by
another story's play function, and merged the two signal stories
into one (the terminate non-override is the default path the
running-flag story already proves).
@DanielleMaywood
DanielleMaywood merged commit 4a7a0a3 into main Aug 20, 2026
28 checks passed
@DanielleMaywood
DanielleMaywood deleted the feat/chatd-process-output-command branch August 20, 2026 10:56
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants