Skip to content

🧰 refactor: Pure Step Reducer for Tool Call Events - #16290

Merged
berry-13 merged 4 commits into
canaryfrom
berry-13/chat-step-reducer-tools
Sep 24, 2026
Merged

berry-13 merged 4 commits into
canaryfrom
berry-13/chat-step-reducer-tools

Conversation

@berry-13

Copy link
Copy Markdown
Collaborator

Summary

Completes the pure step reducer behind useStepHandler by lifting the tool-call transitions into client/src/hooks/SSE/steps/tools.ts: opening a tool_calls run step, argument (and OAuth prompt) deltas, completion, and run-step closure, along with the skill-authoring detection. Stacked on #16289.

Each reducer takes the response message, the run step and one event and returns the next message. applyToolCallsStep also returns the tool-call id to record, so the hook keeps owning the step-to-tool-call-id map in its ref. Hook signature and behavior are unchanged.

Type of change

  • Refactor
  • Tests / tooling / CI

Testing

Tested environments/configuration:

  • lc dev servers on this branch, agents endpoint via Anthropic claude-sonnet-4-6 with Run Code enabled. A reasoning turn with four tool calls rendered its parts in order and matched the persisted message after a hard reload; a turn stopped mid-stream matched after reload. Successful code execution was not exercised: the sandbox in my environment was unavailable, so the calls completed with an error output.

Automated tests:

  • Extended steps.spec.ts with the tool-call family: open, stream args, complete, duplicate run step and duplicate completion, completion before open, OAuth prompt on a delta and its displacement, closure only stamping tool calls.
  • cd client && npx jest hooks/SSE hooks/Chat Providers: 28 suites, 778 tests pass; useStepHandler.spec.ts is unchanged.
  • cd client && npm run typecheck: passes.
  • npm run static-checks -- --against origin/canary: passes.

Screenshots / recordings

No user-facing change.

Risk / compatibility

Behavior-preserving refactor of the tool-call streaming path.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors
  • User-facing or complex behavior is documented where necessary

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-24T16:20:03.262713Z 667d59d New commits
🔒 Security Review ✅ Completed 2026-09-24T10:25:03.251848Z a5d8143 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@danny-avila
danny-avila added this pull request to stack #16292 September 24, 2026 10:38
@berry-13
berry-13 force-pushed the berry-13/chat-step-reducer-tools branch 2 times, most recently from 2b0c602 to ec1170c Compare September 24, 2026 14:47
Base automatically changed from berry-13/chat-step-reducer to canary September 24, 2026 15:36
@berry-13
berry-13 force-pushed the berry-13/chat-step-reducer-tools branch 2 times, most recently from 8ffd5f8 to 92d2c28 Compare September 24, 2026 15:41

@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: 92d2c289e0

ℹ️ 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 on lines +421 to +423
expect(completedTwice.content).toEqual(message.content);
expect(toolCallAt(reopened, 1)).toMatchObject({ id: 'call-1', name: 'search' });
expect(reopened.content).toHaveLength(2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify reopening preserves completed tool state

When ON_RUN_STEP is replayed after completion, applyToolCallsStep performs a non-final updateContent, which rebuilds the tool call without its output, progress, or terminal status. These assertions check only the ID, name, and array length, so the test passes even though the supposedly idempotent replay erases the result and regresses the card from completed; compare the reopened part with the completed part or explicitly assert the terminal fields.

AGENTS.md reference: AGENTS.md:L31-L35

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in ecb9642. The test overclaimed: it now asserts only what holds, a duplicate completion leaves the content equal and re-announcing a still-open step keeps its streamed args. A run step replayed after completion does drop the output, but that is canary's behavior, which this refactor keeps unchanged; tracked at berry-13#115.

Comment on lines +25 to +26
const toolCard = (page: Page) =>
messagesView(page).getByRole('button', { name: 'Finished running', exact: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow the duration suffix in the tool-card locator

When the mocked tool step takes at least one second, as can happen on a loaded CI runner, ProgressText appends the localized screen-reader duration to the button's accessible name, so it becomes something like Finished running took 1 second. This exact-name locator then finds no card and the scenario times out despite correct rendering; match the stable name prefix or locate the tool row without removing the accessible duration.

AGENTS.md reference: AGENTS.md:L31-L33

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in ecb9642. The card is matched by the /^Finished running/ name prefix, so a duration suffix no longer breaks the locator; the scenario passes in desktop light, desktop dark and mobile.

Comment on lines +439 to +445
it('fills the slot when a completion arrives before its run step opened it', () => {
const completed = applyToolCallCompleted(
createResponse(),
search,
toolEnd(search.id, { id: 'call-1', name: 'search', args: { q: 'cats' }, output: '3' }),
0,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exercise completion-before-open through the event handler

This test manually supplies the RunStep, so it does not cover the event ordering named by the test. When ON_RUN_STEP_COMPLETED actually arrives before ON_RUN_STEP, useStepHandler cannot find the step in stepMap and returns without buffering the completion, meaning the tool output is still lost; drive this case through the handler or add completion buffering before claiming this ordering is supported.

AGENTS.md reference: AGENTS.md:L31-L35

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in ecb9642. The test is renamed to what it covers: the reducer writes a completion into its step slot when the opening part is missing. The handler dropping a completion that arrives before its run step is canary's behavior and unchanged here; buffering it is tracked at berry-13#116.

Add pure reducers for tool_calls run steps, argument deltas, completions
and run-step closures, and move the skill-authoring detection with them.
useStepHandler keeps the step-to-tool-call-id map and records the id each
reducer returns.
Drop the claim that a replayed run step is idempotent after completion and
that a completion may arrive before its run step; cover re-announcing an
open step instead. Match the code card by its name prefix, since a slow
run appends its duration.
@berry-13
berry-13 force-pushed the berry-13/chat-step-reducer-tools branch from ecb9642 to 667d59d Compare September 24, 2026 16:16
@berry-13
berry-13 merged commit 9c7555a into canary Sep 24, 2026
26 checks passed
@berry-13
berry-13 deleted the berry-13/chat-step-reducer-tools branch September 24, 2026 16:50
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