Description
Bug report
When the provider emits duplicate tool-call id dup_id_0001, Crush appends duplicate role=tool results for that id into later OpenAI Chat request history. This can corrupt provider conversation state, trigger provider-side schema errors, or amplify retries.
Before/after behavior
Before: the local mock provider emits two tool calls with the same id, dup_id_0001.
After: Crush sends duplicate tool-result history for dup_id_0001 in a subsequent provider request. In the latest local verification run, the reproducer observed 2228 duplicate entries for that id before timeout.
Expected: Crush should reject, ignore, or coalesce duplicate tool-call ids before executing tools or appending results to provider history.
Minimal reproducible example
Prerequisites: Docker, Python 3, and the GitHub CLI (gh) for the clone command below. The linked reproducer is self-contained and uses only Python standard-library modules plus Docker. It builds @charmland/crush@0.76.0 from the public npm package, starts a local mock provider, and runs the CLI in an isolated workspace. The Docker run is limited to 2 CPUs and 4 GiB RAM by default.
Complete self-contained reproducer: https://gist.github.com/N0zoM1z0/1ac3426608603aa4cd3249a40331614e
gh gist clone 1ac3426608603aa4cd3249a40331614e crush-duplicate-tool-id-history-pollution-reproducer
cd crush-duplicate-tool-id-history-pollution-reproducer
python3 crush-duplicate-tool-id-history-pollution.reproduce.py
To reuse an already-built local image:
python3 crush-duplicate-tool-id-history-pollution.reproduce.py --skip-build
Expected successful reproduction output includes:
provider_requests=1125
duplicate_tool_result_in_request dup_id_0001: observed=2228 expected>=2
REPRODUCED
Version
0.76.0
Environment
OS/arch: Linux x86_64 Docker, using node:24-bookwormbased target image ; Interface: CLI;Provider/model: OpenAI Chat-compatible local mock provider, gpt-4
Description
Bug report
When the provider emits duplicate tool-call id
dup_id_0001, Crush appends duplicaterole=toolresults for that id into later OpenAI Chat request history. This can corrupt provider conversation state, trigger provider-side schema errors, or amplify retries.Before/after behavior
Before: the local mock provider emits two tool calls with the same id,
dup_id_0001.After: Crush sends duplicate tool-result history for
dup_id_0001in a subsequent provider request. In the latest local verification run, the reproducer observed 2228 duplicate entries for that id before timeout.Expected: Crush should reject, ignore, or coalesce duplicate tool-call ids before executing tools or appending results to provider history.
Minimal reproducible example
Prerequisites: Docker, Python 3, and the GitHub CLI (
gh) for the clone command below. The linked reproducer is self-contained and uses only Python standard-library modules plus Docker. It builds@charmland/crush@0.76.0from the public npm package, starts a local mock provider, and runs the CLI in an isolated workspace. The Docker run is limited to 2 CPUs and 4 GiB RAM by default.Complete self-contained reproducer: https://gist.github.com/N0zoM1z0/1ac3426608603aa4cd3249a40331614e
gh gist clone 1ac3426608603aa4cd3249a40331614e crush-duplicate-tool-id-history-pollution-reproducer cd crush-duplicate-tool-id-history-pollution-reproducer python3 crush-duplicate-tool-id-history-pollution.reproduce.pyTo reuse an already-built local image:
Expected successful reproduction output includes:
Version
0.76.0
Environment
OS/arch: Linux x86_64 Docker, using node:24-bookwormbased target image ; Interface: CLI;Provider/model: OpenAI Chat-compatible local mock provider, gpt-4