Skip to content

fix(core-agent): prevent plain-text tool call leaks - #2459

Open
zimo-xiao-zheng wants to merge 8 commits into
moeru-ai:mainfrom
zimo-xiao-zheng:zimo-xiao-zheng/feat/tool-call-fallback-2161
Open

fix(core-agent): prevent plain-text tool call leaks#2459
zimo-xiao-zheng wants to merge 8 commits into
moeru-ai:mainfrom
zimo-xiao-zheng:zimo-xiao-zheng/feat/tool-call-fallback-2161

Conversation

@zimo-xiao-zheng

@zimo-xiao-zheng zimo-xiao-zheng commented Sep 4, 2026

Copy link
Copy Markdown

Description

Some models print a tool call as JSON instead of using the native tool protocol. This exposes the call in chat or reasoning output.

  • Inspect both channels through step completion, including JSON before, after, or across native tool events.
  • Bound total JSON parsing work per channel and step. Reject excessive malformed candidates without releasing unchecked output.
  • Preserve ordinary JSON and event order. Native tool activity signals replay risk before buffered UI events reach the caller.
  • Apply tool and content-array fallbacks through one bounded classifier. Each capability changes at most once per request.
  • Do not retry after output, transcript delivery, finish, or native tool activity. Preserve required tool choices and explicit content-array overrides.
  • Drain accepted errors before success. On failure, stop pending output and wait for the active consumer callback.

Linked Issues

Closes #2161

Additional Context

The guard matches a complete JSON object with a known tool name and a top-level parameters or arguments field. It handles nested parameters, escaped characters, and malformed prefixes. Valid outer objects keep their nested examples intact.

Candidate lengths can total at most eight times the channel length, measured in UTF-16 code units. Each span consumes this budget before slicing or parsing. Valid objects still need one pass. If recovery exhausts the budget, the response rejects and discards unchecked buffered output. This error does not trigger a store retry or change tool compatibility. Deeply malformed ordinary output can also fail.

Ordinary text and reasoning stream until either channel starts a JSON candidate. Candidates and subsequent native UI notifications stay buffered until step completion. Native events do not disable inspection.

The chat store receives a synchronous native-activity signal because xsAI does not await UI callbacks before tool execution. This prevents replay even when a leak rejects buffered notifications. It does not cancel or roll back SDK tool execution.

The two compatibility fallbacks can apply in either order, with at most three top-level stream attempts. SDK tool rounds are not compatibility retries. Repeated errors reject. Explicit supportsContentArray: true keeps its override instead of retrying the same payload.

Cache-disabled requests resolve current tool names for inspection without sending tools or tool_choice. Each request owns its names, including during retries. Explicit supportsTools: false requests skip tool resolution. A request that starts without tools does not retry a tool leak.

Provider completion closes event admission. Accepted errors still reject, while later provider events remain ignored. Failure stops queued output and buffered flushes. An active consumer callback must settle before rejection.

Verification

  • pnpm lint: passed with 10 existing warnings.
  • pnpm typecheck: passed across 58 workspace projects.
  • pnpm build:packages: 33 tasks passed.
  • pnpm -F @proj-airi/core-agent exec vitest run: 142 tests passed.
  • AIRI_TEST_REAL_SSE=1 pnpm -F @proj-airi/stage-ui exec vitest run: 815 tests passed, including browser tests.
  • Test-first reproduction: two core tests and two chat-store tests failed on the old code. Added nine tests for this revision.
  • New cases cover both channels, budget exhaustion, later tool JSON, deep valid JSON, separate invalid candidates, and budget isolation.
  • Chat-store tests check that exhaustion does not retry or change tool compatibility for the next request.
  • The five existing opt-in tests with real xsAI and HTTP/SSE passed again in the full suite.
  • One unchanged hearing analytics test timed out in the first full run. Its isolated run and the full rerun passed without timeout changes.
  • Changed-file lint and git diff --check passed.

A local experiment used the public core build, real xsAI, and a loopback HTTP/SSE server.
It supplied {"a": repeated N times, then x and N closing braces.
Each case ran once on Windows with Node 24.14.0, after a 256-level warm-up.
Times include the complete local request, not just the guard.

Text input Before After
10,000 levels / 60,001 characters 1,088.37 ms 10.24 ms
20,000 levels / 120,001 characters 4,613.79 ms 17.95 ms

Both text and reasoning cases rejected on exhaustion without output, message, finish, or usage callbacks.
These measurements check this fixture; they are not a general latency guarantee.
The work bound is proportional to input length, not an absolute response-size or time limit.

The SSE responses are scripted transport fixtures, not model inference. Earlier evidence remains separate: initial llama3:8b and qwen3:0.6b simulations, fourth-round repeated-leak model runs, and fifth-round callback-drain SSE checks.

Visual changes

The original comparison below records the initial chat recovery scenario. This revision changes stream handling, not component layout. It does not include new captures.

Before After
Chat / raw tool JSON Chat / tool-free recovery

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 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-08T03:35:26.027303Z 300b675 New commits
ℹ️ 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.

@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: f9a179cd0c

ℹ️ 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 packages/core-agent/src/runtime/llm-service.ts
Comment thread packages/core-agent/src/runtime/llm-service.ts

@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: f7f529284c

ℹ️ 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 packages/core-agent/src/runtime/llm-service.test.ts Outdated
Comment thread packages/core-agent/src/runtime/llm-service.ts

@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: 1b19dfefea

ℹ️ 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 packages/core-agent/src/runtime/llm-service.ts Outdated
Comment thread packages/stage-ui/src/stores/ai/chat-llm/llm.ts Outdated

@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: 2404f84274

ℹ️ 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 packages/stage-ui/src/stores/ai/chat-llm/llm.ts Outdated
Comment thread packages/core-agent/src/runtime/llm-service.ts Outdated
Comment thread packages/core-agent/src/runtime/llm-service.ts

@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: 1a333626ea

ℹ️ 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 packages/core-agent/src/runtime/llm-service.ts Outdated
Comment thread packages/core-agent/src/runtime/llm-service.ts Outdated

@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: b6fe504ce1

ℹ️ 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 packages/core-agent/src/runtime/llm-service.ts Outdated
Comment thread packages/stage-ui/src/stores/ai/chat-llm/llm.ts Outdated

@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: 95d441a884

ℹ️ 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 packages/core-agent/src/runtime/llm-service.ts

@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: 300b675f1b

ℹ️ 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 +423 to +424
if (event.type === 'step.done') {
await finishPossibleToolCall()

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 Preserve incomplete candidates across step boundaries

When a tool-enabled generation splits a serialized call across tool rounds—for example, one step emits {"name":"tool", and the next emits "arguments":{}}—this branch flushes the incomplete prefix and resets the guard. The suffix then contains no opening brace and streams unchecked. chat-orchestrator-runtime.ts:824-834 concatenates text deltas across these steps, so users still receive the complete raw tool-call JSON. Carry unmatched candidates into the next step, or inspect the combined channel through full-stream completion.

Useful? React with 👍 / 👎.

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.

bug: should not leak raw JSON function calling to UI

1 participant