Skip to content

Say in the chat when a tool is left out because the server refused its schema - #1221

Closed
sosidudku1 wants to merge 2 commits into
fix/ato-1399-tool-schema-boolfrom
fix/ato-1417-dropped-tool-notice
Closed

sosidudku1 wants to merge 2 commits into
fix/ato-1399-tool-schema-boolfrom
fix/ato-1417-dropped-tool-notice

Conversation

@sosidudku1

@sosidudku1 sosidudku1 commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #1148, retarget to main after it merges.

Fixes ATO-1417 (follow-up of ATO-1399).

What was wrong

#1148 resends a request without a tool whose JSON schema LM Studio or llama.cpp refused, but only writes a log line about it. In the terminal or the desktop the person sees a model that never uses their MCP tool, with no reason given.

What changes

  • Provider (openai-provider.ts): the tool the request was resent without is recorded on the completion as CompletionResult.droppedTools ({ name, reason: "schema_rejected" }, registry name, not wire name). Both the unary and the streamed path set it.
  • Agent loop: on each llm_completed, every dropped tool the turn has not announced yet becomes a new AgentLoopEvent tool_disabled (stepIndex, tool, reason). Once per tool per turn: every step sends the same tool list and is refused the same way, so a per-request line would repeat on every step.
  • Terminal TUI: a system line in the chat: "Tool X is disabled for this turn: the model server did not accept its schema." Background sessions do not paint into the chat on screen (the reducer's existing session filter).
  • HTTP stream: a named tool_disabled frame (object: atomic.tool_disabled, session_id, step_index, tool, reason, notice = the same sentence), extensions opt-in only, like provider_switched. The plain OpenAI stream is unchanged. README notes the frame.

Why a field on the result and not a new callback or a direct event

The provider has no session and no event sink, and should not get one. Provider-side facts already reach the UI this way: rateLimits and servedTransport ride CompletionResult, and the runtime/loop lift them into events. The result passes through the fallback seam untouched (it spreads the result), so nothing new has to be threaded through LlmStreamParams / llm-link-attempt, which a request callback like onPromptProgress would need. The loop then owns the per-turn dedupe, the same way it owns history_trimmed and profile_clipped.

Old clients

  • The frame is extensions-only. The desktop forwards every named frame to the renderer as kind: <name> and onChatEvent ignores a kind it does not know, so an older desktop just drops it. The desktop rendering is a separate PR on rel/xp-build.
  • The TUI reducer's exhaustive switch gets the new case; nothing else switches exhaustively on AgentLoopEvent.

Tests (written, not run)

  • src/llm/provider/openai/openai-tool-schema-retry.test.ts: droppedTools set on the unary and streamed resend, absent when nothing was dropped.
  • src/agent/tool-disabled-notice.test.ts: sentence and per-turn dedupe.
  • src/agent/agent-loop-tool-disabled.test.ts: once per turn across two completions of a turn (parse retry), again on the next turn, nothing without drops.
  • src/tui/agent-event-reducer.test.ts: system line, not painted for a background session.
  • src/http/tool-disabled-stream.test.ts: frame shape, nothing on the plain stream.

Checked on the second Mac on 12.11.2026 in the combined run with the other open PRs: build, lint, test types, vitest, desktop unit tests, desktop smoke and live smoke all green.

…sed its schema

The provider now records the tool it resent the request without on
CompletionResult.droppedTools. The agent loop turns that into one
tool_disabled event per tool per turn; the TUI shows it as a system
line and the HTTP stream sends a tool_disabled frame (extensions only)
with the chat sentence.
@sosidudku1

Copy link
Copy Markdown
Collaborator Author

Closing: this code is already merged into rel/xp-build via #1257 (and into main via #1256 where it applies). GitHub did not mark it merged because the PR targets another feature branch.

@sosidudku1 sosidudku1 closed this Nov 12, 2026
sheepdogs pushed a commit to sheepdogs/atomic-agent that referenced this pull request Nov 12, 2026
sheepdogs pushed a commit to sheepdogs/atomic-agent that referenced this pull request Nov 12, 2026
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