Skip to content

fix: mimo-parallel-tool-call-policy (2/2) - #1130

Open
myk1yt wants to merge 24 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b12-mimo-enforcement-v2
Open

fix: mimo-parallel-tool-call-policy (2/2)#1130
myk1yt wants to merge 24 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b12-mimo-enforcement-v2

Conversation

@myk1yt

@myk1yt myk1yt commented Aug 4, 2026

Copy link
Copy Markdown

Stack Position

Description

스크린샷 2026-07-27 175105 스크린샷 2026-07-27 174745

Full Feature Description

  • Feature Branch: fix/mimo-parallel-tool-call-policy
  • Feature Name: MiMo Parallel Tool Call Policy
  • Purpose: Resolves the problem where MiMo's OpenAI-compatible stream can send two or more tool calls, ghost calls with no name or arguments, and incomplete JSON arguments even in situations where only a single call should be safely processed. Prevents ambiguous side-effect ordering, executes only structurally valid calls, and preserves the existing parallel behavior of other providers.
  • Full Change Description: The shared root B05a provides OpenAI-compatible schema conversion and provider-neutral reasoning/strict controls. B12 connects MiMo model capability, request-level parallel control, streamed native call parsing, ghost quarantine, max-one retention policy, task execution guard, and privacy-safe telemetry. Under the single-call policy, if there is exactly one valid call, only that call is executed; if there are two or more, none are auto-selected and all are returned as typed error results so the model resubmits only one.
  • Impact Scope: Affects mimo.ts, mimo.ts, NativeToolCallParser.ts, ToolCallRetentionPolicy.ts, task policy wiring, and TelemetryService.ts. B05a is shared between this chain and the openai-compatible-strict-reasoning chain.
  • Errors and Edge Cases: Only terminated ghosts with neither name nor arguments are silently removed. If name or argument bytes are present, malformed calls are not erased from history and produce exactly one error result. When two or more valid side-effect calls arrive, none are auto-executed. Telemetry records only counts and policy metadata, not tool names, call IDs, arguments, commands, paths, or prompts. Known parallel provider behavior for OpenAI/Anthropic families is not regressed.
  • Testing Method: Run B05a's schema conversion tests and B12's MiMo single/parallel/malformed/ghost stream fixture, native parser, retention policy, telemetry, and task policy tests. Manually replay a MiMo response with two tool calls, verifying that zero executions and two error results are produced, that a single call is executed exactly once, and that ghosts are removed before history.

Why Split Into 17 PRs

Instead of submitting this feature as a single unified PR, it was split into individual PRs because as code size grows, safely reviewing a PR becomes very difficult. The feature was broken into mutually exclusive individual PRs so that each can be reviewed independently.

What This PR Specifically Changes

Adds MiMo capability/request conversion, stream parser, ghost quarantine, malformed call retention, max-one execution guard, and payload-free telemetry. Does not change other provider behavior or cost calculation.

Included Files

  • packages/types/src/providers/mimo.ts
  • packages/telemetry/src/TelemetryService.ts
  • src/api/providers/mimo.ts
  • src/core/assistant-message/NativeToolCallParser.ts
  • src/core/assistant-message/ToolCallRetentionPolicy.ts
  • task policy wiring and direct tests

Exclusion Scope

  • Request/cost changes for other providers
  • Duplicate changes to B05a settings/UI/request shaping
  • Session report and repair script
  • Changes that log prompt or tool argument payloads to telemetry
  • All items in the common removal rules

Summary by CodeRabbit

  • New Features

    • Added automatic tool-call policies that adapt parallel or single-call behavior based on model and provider capabilities.
    • Added an optional strict tool-schema setting for compatible OpenAI-based providers.
    • Added support for provider-specific tool-call controls and improved compatibility fallbacks.
  • Bug Fixes

    • Prevented empty or malformed streamed tool calls from being executed or retained.
    • Improved handling of unsupported provider options and tool-schema validation.
    • Clarified command timeout and working-directory validation.
  • Documentation

    • Added localized descriptions for strict tool schemas and provider limitations.

Zoo (VP) and others added 21 commits August 2, 2026 10:02
…penAI Compatible provider

- Add openAiToolStrictMode boolean to provider settings (profile-scoped, default false)
- Add strict toggle checkbox in OpenAICompatible settings UI
- BaseProvider.convertToolsForOpenAI now accepts strictMode parameter
  - strictMode=true: strict:true + hardened schema
  - strictMode=false: strict:false + best-effort original schema
  - MCP tools: always strict:false regardless of setting
- Wire setting into all 4 openai.ts request paths
- Fix reasoning effort unsafe cast, add xhigh and max values
- Make parallel_tool_calls conditional on tools being present
# Conflicts:
#	src/core/tools/error-interception/StructuralValidator.ts
# Conflicts:
#	src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts
#	src/core/assistant-message/__tests__/presentAssistantMessage-parser-dedup.integration.spec.ts
#	src/core/assistant-message/presentAssistantMessage.ts
# Conflicts:
#	src/core/assistant-message/__tests__/presentAssistantMessage-parser-dedup.integration.spec.ts
MimoHandler was passing raw tool schemas to the API without the
strict mode conversion that all other OpenAI-compatible providers use.
This caused tool call errors due to missing required/strict fields.

- Call this.convertToolsForOpenAI(tools) instead of raw assignment
- Adds strict: true, required properties, additionalProperties: false
An id-less argument-continuation chunk belongs to the most recent id chunk
seen at its index. When a provider reuses index 0 with a NEW id (a disguised
second parallel call), the new call's id chunk was dropped but its id-less
argument fragments were still kept and concatenated into the FIRST call's
accumulator, corrupting its JSON.

Track dropped indexes in filterToFirstToolCall state and drop subsequent
id-less fragments for those indexes. Also rewrite the function docblock,
which referenced a non-existent error-interception retry loop.
The parseErrors/parseFailures docblocks claimed presentAssistantMessage
routes recorded failures to an INVALID_JSON_ARGUMENTS error-interception
pattern. No such routing exists on this codebase; describe the actual
lifecycle (consumed via the consume* APIs, cleared on new API request).
Comment-only change, no behavior difference.
parseErrors/parseFailures static maps accumulated an entry per malformed
tool call and were never cleared in production (the consume* APIs have no
production callers), slowly leaking for the extension-host lifetime.

Add NativeToolCallParser.clearParseFailures() and call it in
Task.recursivelyMakeClineRequests alongside clearAllStreamingToolCalls()/
clearRawChunkState(), where other per-stream state is reset. The consume*
APIs keep working for tests.
MiMo sends tools through convertToolsForOpenAI(), which attaches a strict
flag to every function tool. An OpenAI-compatible endpoint that doesn't
support structured outputs rejects the request with a 400 and the turn
fails outright.

Mirror the existing parallel_tool_calls fallback: detect schema-rejection
errors narrowly (400 status plus a mention of strict/additionalProperties
in a tools context, so unrelated 400s like MiMo's missing-reasoning_content
rejection are not retried) and retry once with the original schemas and no
strict flag.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds tool-call policy resolution, strict schema handling, parser failure classification, ghost-call quarantine, enforcement telemetry, provider-specific fallbacks, settings controls, and extensive tests.

Changes

Tool-call policy and enforcement

Layer / File(s) Summary
Policy and telemetry contracts
packages/types/..., packages/telemetry/..., src/api/index.ts, src/core/task/__tests__/tool-call-policy.spec.ts
Adds model capability metadata, resolved policy types, telemetry events, provider defaults, and policy-resolution tests.
Provider tool handling
src/api/providers/...
Adds strict schema conversion, conditional parallel-call parameters, MiMo retry fallbacks, first-call stream filtering, and typed provider tests.
Parser failures and call retention
src/core/assistant-message/...
Adds typed parser failures, streaming state controls, ghost classification, executable-call selection, privacy-preserving telemetry, and tests.
Task quarantine and policy integration
src/core/task/Task.ts
Applies resolved policies to requests and context flows, clears parser state, quarantines empty streamed calls, and records enforcement metadata.
Settings and tool contracts
webview-ui/src/components/settings/providers/OpenAICompatible.tsx, webview-ui/src/i18n/locales/*, src/core/tools/ExecuteCommandTool.ts, src/shared/tools.ts
Adds the strict-schema setting and localized descriptions. Restricts execute_command.timeout to optional numbers.
Validation configuration
codecov.yml, src/eslint-suppressions.json
Makes patch coverage checks informational and reorders the suppression manifest.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

Suggested labels: awaiting-review

Suggested reviewers: taltas, navedmerchant

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the implementation and testing, but it omits the required issue link and pre-submission checklist sections. Add an approved issue link and complete the pre-submission checklist, documentation-impact section, and other required template sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the MiMo parallel tool-call policy fix and matches the primary changes.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/telemetry/src/TelemetryService.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/types/src/__tests__/provider-settings.test.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/types/src/model.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 22 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (10)
scripts/find-dup-json-keys.js-156-160 (1)

156-160: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Replace process.exit with process.exitCode to avoid truncated output.

process.exit terminates the process before pending asynchronous stdout writes flush. When a caller pipes the output (for example node scripts/find-dup-json-keys.js src | tee report.txt), console.log writes to a pipe are asynchronous, so the last lines can be lost. Set process.exitCode instead and let Node exit after the stream drains.

♻️ Proposed fix
 console.log(found === 0 ? "OK: no duplicate keys found" : `TOTAL: ${found} duplicate key occurrence(s)`)
-process.exit(found === 0 ? 0 : 1)
+process.exitCode = found === 0 ? 0 : 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/find-dup-json-keys.js` around lines 156 - 160, Update the final
status handling in the duplicate-key script to assign the computed success or
failure code to process.exitCode instead of calling process.exit, while
preserving the existing console.log message and exit-code values so pending
output can flush before Node terminates.
scripts/find-dup-json-keys.js-141-159 (1)

141-159: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Count parse errors separately from duplicate keys.

Line 150 increments found for a parse error. Line 159 then reports the same counter as duplicate key occurrence(s). A file that fails to parse is reported as a duplicate key, which misstates the result. Track the two conditions in separate counters, and keep the non-zero exit status when either counter is non-zero.

♻️ Proposed fix
 let found = 0
+let parseErrors = 0
 for (const target of process.argv.slice(2)) {
 	for (const file of walk(target)) {
 		const text = fs.readFileSync(file, "utf8")
 		let dups
 		try {
 			dups = findDuplicates(text)
 		} catch (e) {
 			console.log(`${file}: PARSE ERROR ${e.message}`)
-			found++
+			parseErrors++
 			continue
 		}
 		for (const d of dups) {
 			console.log(`${file}: duplicate key "${d.key}" at line ${d.line}`)
 			found++
 		}
 	}
 }
-console.log(found === 0 ? "OK: no duplicate keys found" : `TOTAL: ${found} duplicate key occurrence(s)`)
+console.log(
+	found === 0 && parseErrors === 0
+		? "OK: no duplicate keys found"
+		: `TOTAL: ${found} duplicate key occurrence(s), ${parseErrors} parse error(s)`,
+)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/find-dup-json-keys.js` around lines 141 - 159, The code uses a single
counter `found` for both parse errors and duplicate key occurrences, causing the
final message to misreport parse errors as duplicate key occurrences. Create a
separate counter variable for parse errors and keep `found` for duplicate keys
only. In the catch block where the parse error is handled, increment the parse
error counter instead of `found`. Update the final console.log message to report
parse error count and duplicate key count separately, and ensure the non-zero
exit status is triggered if either counter is non-zero.
docs/260730_0001_session_branch-cleanup/173230_execution-plan.md-73-75 (1)

73-75: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Allow for an empty commit during verification.

The execution report records 17 output commits because one of the 18 input commits became empty. This gate still requires exactly 18 commits. Expect feature-only commits and document that the count may be 17 when Git drops an empty commit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260730_0001_session_branch-cleanup/173230_execution-plan.md` around
lines 73 - 75, Update the verification expectation for the git log so it allows
17 or 18 feature-only commits, documenting that Git may drop one empty commit
during replay. Keep the exclusions for unrelated commits unchanged.
webview-ui/src/i18n/locales/hi/settings.json-968-969 (1)

968-969: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the new locale strings.

These locale files display English text to users of their selected language. Replace both new values with translations for the target locale.

  • webview-ui/src/i18n/locales/hi/settings.json#L968-L969: add Hindi translations.
  • webview-ui/src/i18n/locales/id/settings.json#L968-L969: add Indonesian translations.
  • webview-ui/src/i18n/locales/it/settings.json#L968-L969: add Italian translations.
  • webview-ui/src/i18n/locales/ja/settings.json#L968-L969: add Japanese translations.
  • webview-ui/src/i18n/locales/ko/settings.json#L968-L969: add Korean translations.
  • webview-ui/src/i18n/locales/nl/settings.json#L968-L969: add Dutch translations.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/hi/settings.json` around lines 968 - 969, The new
strictToolSchemas and strictToolSchemasDescription keys contain English text but
need to be translated into the target languages for users. In
webview-ui/src/i18n/locales/hi/settings.json (lines 968-969), replace the
English values with Hindi translations. In
webview-ui/src/i18n/locales/id/settings.json (lines 968-969), add Indonesian
translations. In webview-ui/src/i18n/locales/it/settings.json (lines 968-969),
add Italian translations. In webview-ui/src/i18n/locales/ja/settings.json (lines
968-969), add Japanese translations. In
webview-ui/src/i18n/locales/ko/settings.json (lines 968-969), add Korean
translations. In webview-ui/src/i18n/locales/nl/settings.json (lines 968-969),
add Dutch translations. Each file should preserve the JSON structure with the
same keys but localized string values appropriate for its target language
audience.
webview-ui/src/i18n/locales/zh-TW/settings.json-995-996 (1)

995-996: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Translate the new strictToolSchemas settings into Traditional Chinese.

strictToolSchemas and strictToolSchemasDescription still show English text in webview-ui/src/i18n/locales/zh-TW/settings.json, so zh-TW users will see untranslated labels. Translate these entries or route them through the project translation workflow before merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/zh-TW/settings.json` around lines 995 - 996,
Translate the `strictToolSchemas` and `strictToolSchemasDescription` entries in
the zh-TW settings locale into Traditional Chinese, preserving the original
labels’ meaning and the description’s strict-mode and MCP-tool behavior.
src/core/assistant-message/NativeToolCallParser.ts-1191-1219 (1)

1191-1219: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Structural failures now log and store "[object Object]" as the legacy diagnostic string.

The three throw sites throw plain object literals, not Error instances. The catch block at Line 1249 computes error instanceof Error ? error.message : String(error). For these tagged objects String(error) returns "[object Object]". That value is written to parseErrors at Line 1257 and printed by console.error at Line 1251. Every missing-argument and invalid-shape failure therefore loses its human-readable diagnostic, which is the only purpose the legacy string channel still serves.

Add a message field to the tagged throws and prefer it when building errorMessage. This also removes the throw-literal pattern for the message path.

The test at src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts:409 only asserts the string is defined, so it does not detect this.

🐛 Proposed fix to keep a readable diagnostic string
 				if (!isPlainObject) {
 					throw {
 						__parserFailureKind: "invalid_argument_shape" as const,
+						message: `Tool '${resolvedName}' received arguments that are not a JSON object`,
 						toolName: resolvedName as string,
 						missingParameters: [],
 						emptyArguments: false,
 					}
 				}
 
 				const required = NativeToolCallParser.REQUIRED_PARAMETERS[resolvedName as string] ?? []
 				const missing = required.filter((p) => args[p] === undefined)
 				const isEmpty = Object.keys(args).length === 0
 
 				if (missing.length > 0) {
 					throw {
 						__parserFailureKind: "missing_required_arguments" as const,
+						message: `Tool '${resolvedName}' is missing required parameter(s): ${missing.join(", ")}`,
 						toolName: resolvedName as string,
 						missingParameters: missing,
 						emptyArguments: isEmpty,
 					}
 				}
 
 				// Required fields are present but the structural shape didn't match
 				// any known pattern in the switch above.
 				throw {
 					__parserFailureKind: "invalid_argument_shape" as const,
+					message: `Tool '${resolvedName}' received arguments with an unexpected shape`,
 					toolName: resolvedName as string,
 					missingParameters: [],
 					emptyArguments: isEmpty,
 				}

Then read that field in the catch block:

-			const errorMessage = error instanceof Error ? error.message : String(error)
+			const errorMessage =
+				error instanceof Error
+					? error.message
+					: typeof error === "object" && error !== null && typeof (error as { message?: unknown }).message === "string"
+						? (error as { message: string }).message
+						: String(error)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/assistant-message/NativeToolCallParser.ts` around lines 1191 - 1219,
Add a readable message field to all tagged structural-failure throws in
NativeToolCallParser, including invalid_argument_shape and
missing_required_arguments cases, describing the tool and failure. Update the
catch block’s errorMessage construction to prefer the tagged message before
falling back to Error.message or String(error), so parseErrors and console.error
retain useful diagnostics.
webview-ui/src/i18n/locales/tr/settings.json-968-969 (1)

968-969: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the new strict-schema strings.

The new values are English in three non-English locale files.

  • webview-ui/src/i18n/locales/tr/settings.json#L968-L969: Add Turkish translations.
  • webview-ui/src/i18n/locales/vi/settings.json#L968-L969: Add Vietnamese translations.
  • webview-ui/src/i18n/locales/zh-CN/settings.json#L968-L969: Add Simplified Chinese translations.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/tr/settings.json` around lines 968 - 969,
Translate the strictToolSchemas and strictToolSchemasDescription values from
English into the target languages in
webview-ui/src/i18n/locales/tr/settings.json lines 968-969,
webview-ui/src/i18n/locales/vi/settings.json lines 968-969, and
webview-ui/src/i18n/locales/zh-CN/settings.json lines 968-969. Preserve the
existing keys and meaning, including strict schema behavior, provider support
limitations, and the non-strict handling of MCP tools.
webview-ui/src/i18n/locales/ca/settings.json-968-969 (1)

968-969: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the new locale values.

These locale-specific catalogs add English labels and descriptions. Users of these locales will see English text in the strict-schema setting.

  • webview-ui/src/i18n/locales/ca/settings.json#L968-L969: add Catalan translations.
  • webview-ui/src/i18n/locales/pl/settings.json#L968-L969: add Polish translations.
  • webview-ui/src/i18n/locales/pt-BR/settings.json#L968-L969: add Brazilian Portuguese translations.
  • webview-ui/src/i18n/locales/ru/settings.json#L968-L969: add Russian translations.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/ca/settings.json` around lines 968 - 969,
Translate the strictToolSchemas and strictToolSchemasDescription values in
webview-ui/src/i18n/locales/ca/settings.json lines 968-969 into Catalan,
webview-ui/src/i18n/locales/pl/settings.json lines 968-969 into Polish,
webview-ui/src/i18n/locales/pt-BR/settings.json lines 968-969 into Brazilian
Portuguese, and webview-ui/src/i18n/locales/ru/settings.json lines 968-969 into
Russian, preserving the existing keys and meaning.
webview-ui/src/i18n/locales/de/settings.json-967-969 (1)

967-969: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the new locale values.

The new labels remain in English in non-English locale files.

  • webview-ui/src/i18n/locales/de/settings.json#L967-L969: Translate both values to German.
  • webview-ui/src/i18n/locales/es/settings.json#L967-L969: Translate both values to Spanish.
  • webview-ui/src/i18n/locales/fr/settings.json#L967-L969: Translate both values to French.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/de/settings.json` around lines 967 - 969,
Translate both strictToolSchemas and strictToolSchemasDescription in
webview-ui/src/i18n/locales/de/settings.json (lines 967-969) into German,
webview-ui/src/i18n/locales/es/settings.json (lines 967-969) into Spanish, and
webview-ui/src/i18n/locales/fr/settings.json (lines 967-969) into French,
preserving the existing JSON keys and meaning.
src/api/providers/__tests__/mimo.spec.ts-693-695 (1)

693-695: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass valid metadata without a double assertion.

ApiHandlerCreateMessageMetadata requires taskId. This cast hides that contract from the test. Pass a test task ID directly.

Proposed fix
-			const stream = handler.createMessage("System prompt", messages, {
-				tools,
-			} as unknown as ApiHandlerCreateMessageMetadata)
+			const stream = handler.createMessage("System prompt", messages, {
+				taskId: "test-task",
+				tools,
+			})

Run pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 api/providers/__tests__/mimo.spec.ts after the change.

As per coding guidelines, "Use double assertions only as a last resort and explain them with a comment."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/__tests__/mimo.spec.ts` around lines 693 - 695, Remove the
double assertion (as unknown as ApiHandlerCreateMessageMetadata) from the
metadata object passed to handler.createMessage. Instead, provide a properly
typed metadata object that includes the required taskId property along with the
existing tools property. This will ensure the test respects the
ApiHandlerCreateMessageMetadata contract rather than bypassing type safety.

Source: Coding guidelines

🧹 Nitpick comments (5)
scripts/find-dup-json-keys.js (3)

21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused isArray frame field.

parseArray never pushes a stack frame, because arrays have no keys to track. Every frame is therefore an object frame, and isArray is always false and never read. Drop the field and the comment reference to keep the frame shape honest.

♻️ Proposed fix
-	const stack = [] // each frame: { keys: Set<string>, isArray: bool }
+	const stack = [] // each frame: { keys: Set<string> } for the enclosing object

Also update the push at line 78:

stack.push({ keys: new Set() })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/find-dup-json-keys.js` at line 21, Remove the unused isArray field
from the stack frame comment and update the stack.push call to create frames
with only the keys Set. Keep the existing object-key tracking behavior
unchanged.

95-98: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Validate the : separator instead of advancing blindly.

Line 97 advances past one character without checking that the character is :. If the separator is absent, the scanner silently continues at the wrong offset and can report incorrect duplicates or incorrect line numbers. The surrounding code throws unexpected char for other malformed input, so an explicit check keeps the error behavior consistent.

♻️ Proposed fix
 			skipWs()
-			// expect ':'
-			i++
+			if (text[i] !== ":") {
+				throw new Error(`expected ':' after key "${key}" at line ${line}`)
+			}
+			i++
 			skipValue()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/find-dup-json-keys.js` around lines 95 - 98, Update the object-key
scanning logic around skipWs and skipValue to validate that the current
character is ':' before advancing. If it is not, throw the same unexpected-char
error used for other malformed input; otherwise advance and continue skipping
the value.

6-15: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Skip generated and VCS directories, and guard against symlink cycles.

walk descends into every directory. If a caller passes the repository root, the scan traverses node_modules, .git, dist, and out, which adds large amounts of work and reports duplicate keys in third-party files. fs.statSync also follows symlinks, so a symlink that points to an ancestor directory produces unbounded recursion.

Use withFileTypes to inspect entries without following symlinks, and skip known generated directories.

♻️ Proposed fix
+const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "out", "build", ".turbo"])
+
 function* walk(target) {
 	const stat = fs.statSync(target)
 	if (stat.isDirectory()) {
-		for (const entry of fs.readdirSync(target)) {
-			yield* walk(path.join(target, entry))
+		for (const entry of fs.readdirSync(target, { withFileTypes: true })) {
+			if (entry.isSymbolicLink()) continue
+			if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) continue
+			yield* walk(path.join(target, entry.name))
 		}
 	} else if (target.endsWith(".json")) {
 		yield target
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/find-dup-json-keys.js` around lines 6 - 15, Update walk to use
directory-entry metadata via readdirSync with withFileTypes enabled, avoid
descending through symlinked entries, and skip node_modules, .git, dist, and out
directories before recursion. Preserve yielding only .json files while
preventing ancestor-link cycles and unnecessary traversal.
src/core/task/Task.ts (1)

3024-3041: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated ghost-drop telemetry block.

The same ten-line block appears three times: Lines 3024-3041, Lines 3115-3132, and Lines 3516-3533. The only difference is the local variable name (ghostPolicy1, ghostPolicy2, ghostPolicy3), which exists only to avoid shadowing. Each copy re-resolves the policy and calls this.api.getModel() twice.

Extract one private method and call it from all three sites. All three copies also omit parallelToolCallsSent, while the policy-resolution event at Line 4561 sends it; decide the field once in the helper.

♻️ Proposed helper
private emitGhostDrop(): void {
	const policy = resolveToolCallPolicy(this.api.getModel().info, this.apiConfiguration.apiProvider)
	emitGhostDropTelemetry({
		taskId: this.taskId,
		provider: this.apiConfiguration.apiProvider ?? "unknown",
		model: this.api.getModel().id,
		policySource: policy.source,
		maxCallsPerTurn: policy.maxCallsPerTurn,
		enforcement: policy.enforcement,
		callCount: this.assistantMessageContent.filter(
			(b: AssistantMessageContent): b is ToolUse => b.type === "tool_use",
		).length,
		ghostDroppedCount: 1,
		errorResultCount: 0,
		parallelToolCallsRequested: policy.generation === "parallel",
	})
}

Then replace each site:

-											const ghostPolicy1 = resolveToolCallPolicy(
-												this.api.getModel().info,
-												this.apiConfiguration.apiProvider,
-											)
-											emitGhostDropTelemetry({
-												taskId: this.taskId,
-												provider: this.apiConfiguration.apiProvider ?? "unknown",
-												model: this.api.getModel().id,
-												policySource: ghostPolicy1.source,
-												maxCallsPerTurn: ghostPolicy1.maxCallsPerTurn,
-												enforcement: ghostPolicy1.enforcement,
-												callCount: this.assistantMessageContent.filter(
-													(b: AssistantMessageContent): b is ToolUse => b.type === "tool_use",
-												).length,
-												ghostDroppedCount: 1,
-												errorResultCount: 0,
-												parallelToolCallsRequested: ghostPolicy1.generation === "parallel",
-											})
+											this.emitGhostDrop()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/task/Task.ts` around lines 3024 - 3041, Extract the duplicated
ghost-drop telemetry logic into one private emitGhostDrop method in Task,
resolving the policy and model once within the helper and setting
parallelToolCallsSent consistently with the existing policy-resolution
telemetry. Replace all three inline blocks using ghostPolicy1, ghostPolicy2, and
ghostPolicy3 with calls to this helper, preserving the current telemetry values.
src/core/assistant-message/ToolCallRetentionPolicy.ts (1)

203-310: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Collapse the two identical telemetry emitters.

GhostDropTelemetryInput and MaxOneEnforcementTelemetryInput declare the same fields. emitGhostDropTelemetry and emitMaxOneEnforcementTelemetry have byte-identical bodies: the same hasInstance guard and the same property mapping into captureToolCallEnforcement. The two copies can drift when the event contract changes.

Define one input type and one emitter, then keep the two named exports as thin aliases so the call sites and tests stay unchanged.

♻️ Proposed consolidation
+export interface ToolCallEnforcementTelemetryInput {
+	/** The task identifier. */
+	taskId: string
+	/** The provider name (e.g. "mimo", "openai"). */
+	provider: string
+	/** The model ID. */
+	model: string
+	/** The resolved policy source. */
+	policySource: string
+	/** The resolved max-calls-per-turn limit. */
+	maxCallsPerTurn: 1 | "unbounded"
+	/** The resolved enforcement mode. */
+	enforcement: string
+	/** Total tool calls in the turn. */
+	callCount: number
+	/** How many ghosts were dropped in this turn. */
+	ghostDroppedCount: number
+	/** How many error results were emitted in this turn. */
+	errorResultCount: number
+	/** What the metadata requested for parallel tool calls. */
+	parallelToolCallsRequested: boolean
+	/** What was sent to the provider (if known). */
+	parallelToolCallsSent?: boolean
+}
+
+/**
+ * Emit a tool-call enforcement telemetry event.
+ *
+ * **Privacy:** This function emits ONLY counts and metadata. It does NOT emit
+ * the call ID, tool name, argument bytes, command strings, file paths, or any
+ * raw user data.
+ */
+function emitToolCallEnforcementTelemetry(input: ToolCallEnforcementTelemetryInput): void {
+	if (!TelemetryService.hasInstance()) {
+		return
+	}
+
+	const { taskId, ...properties } = input
+	TelemetryService.instance.captureToolCallEnforcement(taskId, properties)
+}
+
+export type GhostDropTelemetryInput = ToolCallEnforcementTelemetryInput
+export type MaxOneEnforcementTelemetryInput = ToolCallEnforcementTelemetryInput
+
+export const emitGhostDropTelemetry = emitToolCallEnforcementTelemetry
+export const emitMaxOneEnforcementTelemetry = emitToolCallEnforcementTelemetry
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/assistant-message/ToolCallRetentionPolicy.ts` around lines 203 -
310, Create a single consolidated input type and emitter function to replace the
duplicate GhostDropTelemetryInput, MaxOneEnforcementTelemetryInput,
emitGhostDropTelemetry, and emitMaxOneEnforcementTelemetry implementations.
Define one input interface with all the shared fields and one emitter function
with the single hasInstance check and captureToolCallEnforcement call, then
re-export the original type names and function names as type aliases and thin
wrapper functions pointing to the consolidated implementation to preserve
existing call sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/260730_0001_session_branch-cleanup/173200_debug-report.md`:
- Around line 3-10: The task summary in the report contradicts itself by
claiming Git mutations were VP-only while also describing a throwaway rebase.
Update the summary to identify who authorized and performed the temporary
branch/rebase operations, or remove/revise the rebase statement so it accurately
reflects the audit trail; keep the diagnostic and planning scope clear.

In `@docs/260730_0001_session_branch-cleanup/173230_execution-plan.md`:
- Around line 33-42: Remove the obsolete rebase command targeting
feat/error-interception-middleware-clean from Step 3, leaving only the corrected
sequence that checks out feat/error-interception-remote-src and rebases that
branch onto main.

In `@docs/260730_0001_session_branch-cleanup/181500_debug-dnd-ux-runbook.md`:
- Around line 319-325: Update the rollback command in the “7. Rollback” section
to check out feature/task-dnd-ux, or another explicitly documented recovery
branch, instead of feat/error-interception-middleware; keep the cleanup and
backup-preservation commands unchanged.
- Around line 248-251: Update the git cherry-pick instructions to use --ours for
src/core/webview/ClineProvider.ts, preserving the clean squash file rather than
the incoming contaminated commit. Instruct the reader to stage the file
afterward and verify that only the intended model and spec changes remain
staged.

In `@docs/260730_0001_session_branch-cleanup/184700_debug-report.md`:
- Line 90: Update the documented conflict resolution for StructuralValidator.ts
to remove only src/core/tools/error-interception/StructuralValidator.ts,
avoiding whole-directory restore or broad git rm commands, then verify the
unmerged-path list is clear before continuing the cherry-pick.
- Around line 103-104: Update the documented working-tree precondition around
the git status check to require an actually empty git status, removing the
exception for untracked docs/ files. Ensure the instructions also account for
untracked files that could block git switch -C, either by requiring a clean tree
before switching or by moving documents aside after checking for path
collisions.

In `@src/api/providers/lite-llm.ts`:
- Line 222: The LiteLLM and OpenAI-compatible request paths must forward the
resolved parallel tool-call policy instead of defaulting upstream behavior. In
src/api/providers/lite-llm.ts:222, add parallel_tool_calls to requestOptions
using metadata?.parallelToolCalls ?? true; in
src/api/providers/openai-compatible.ts:165, pass the same value through the
provider-namespaced providerOptions for streamText (or explicitly mark that
route as local-only enforcement). Add request-capture tests covering both true
and false values.

In `@src/api/providers/mimo.ts`:
- Around line 243-261: The completion fallback logic around the
chat.completions.create call must apply compatibility removals cumulatively:
replace the single nested retry with a bounded retry flow that removes each
rejected option at most once, allowing a parallel_tool_calls rejection to be
followed by strict-schema stripping. Ensure all non-retryable or exhausted
errors go through handleProviderError(error, "MiMo"), and add a regression test
covering parallel rejection followed by strict-schema rejection.

In `@src/core/assistant-message/ToolCallRetentionPolicy.ts`:
- Around line 159-198: Integrate selectExecutableCall into the production
tool-call execution path before any calls execute, using the current turn’s
calls and maxCallsPerTurn policy. Execute only executableCallId, prevent all
rejectedCallIds from running, and convert each rejected ID into an error result
so enforcement telemetry is triggered; preserve unbounded and single-valid-call
behavior.

In `@src/core/task/Task.ts`:
- Around line 2998-3045: When removing a ghost block from
assistantMessageContent in both ghost-drop paths at src/core/task/Task.ts lines
2998-3045 and 3499-3535, also decrement currentStreamingContentIndex when
ghostIndex is less than it; apply this reconciliation alongside the existing
array splice, or centralize both paths through a shared helper.

In `@webview-ui/src/i18n/locales/en/settings.json`:
- Around line 1043-1051: Remove the duplicate strictToolSchemas and
strictToolSchemasDescription entries from the modelInfo locale object, retaining
exactly one pair with the existing translations.

---

Minor comments:
In `@docs/260730_0001_session_branch-cleanup/173230_execution-plan.md`:
- Around line 73-75: Update the verification expectation for the git log so it
allows 17 or 18 feature-only commits, documenting that Git may drop one empty
commit during replay. Keep the exclusions for unrelated commits unchanged.

In `@scripts/find-dup-json-keys.js`:
- Around line 156-160: Update the final status handling in the duplicate-key
script to assign the computed success or failure code to process.exitCode
instead of calling process.exit, while preserving the existing console.log
message and exit-code values so pending output can flush before Node terminates.
- Around line 141-159: The code uses a single counter `found` for both parse
errors and duplicate key occurrences, causing the final message to misreport
parse errors as duplicate key occurrences. Create a separate counter variable
for parse errors and keep `found` for duplicate keys only. In the catch block
where the parse error is handled, increment the parse error counter instead of
`found`. Update the final console.log message to report parse error count and
duplicate key count separately, and ensure the non-zero exit status is triggered
if either counter is non-zero.

In `@src/api/providers/__tests__/mimo.spec.ts`:
- Around line 693-695: Remove the double assertion (as unknown as
ApiHandlerCreateMessageMetadata) from the metadata object passed to
handler.createMessage. Instead, provide a properly typed metadata object that
includes the required taskId property along with the existing tools property.
This will ensure the test respects the ApiHandlerCreateMessageMetadata contract
rather than bypassing type safety.

In `@src/core/assistant-message/NativeToolCallParser.ts`:
- Around line 1191-1219: Add a readable message field to all tagged
structural-failure throws in NativeToolCallParser, including
invalid_argument_shape and missing_required_arguments cases, describing the tool
and failure. Update the catch block’s errorMessage construction to prefer the
tagged message before falling back to Error.message or String(error), so
parseErrors and console.error retain useful diagnostics.

In `@webview-ui/src/i18n/locales/ca/settings.json`:
- Around line 968-969: Translate the strictToolSchemas and
strictToolSchemasDescription values in
webview-ui/src/i18n/locales/ca/settings.json lines 968-969 into Catalan,
webview-ui/src/i18n/locales/pl/settings.json lines 968-969 into Polish,
webview-ui/src/i18n/locales/pt-BR/settings.json lines 968-969 into Brazilian
Portuguese, and webview-ui/src/i18n/locales/ru/settings.json lines 968-969 into
Russian, preserving the existing keys and meaning.

In `@webview-ui/src/i18n/locales/de/settings.json`:
- Around line 967-969: Translate both strictToolSchemas and
strictToolSchemasDescription in webview-ui/src/i18n/locales/de/settings.json
(lines 967-969) into German, webview-ui/src/i18n/locales/es/settings.json (lines
967-969) into Spanish, and webview-ui/src/i18n/locales/fr/settings.json (lines
967-969) into French, preserving the existing JSON keys and meaning.

In `@webview-ui/src/i18n/locales/hi/settings.json`:
- Around line 968-969: The new strictToolSchemas and
strictToolSchemasDescription keys contain English text but need to be translated
into the target languages for users. In
webview-ui/src/i18n/locales/hi/settings.json (lines 968-969), replace the
English values with Hindi translations. In
webview-ui/src/i18n/locales/id/settings.json (lines 968-969), add Indonesian
translations. In webview-ui/src/i18n/locales/it/settings.json (lines 968-969),
add Italian translations. In webview-ui/src/i18n/locales/ja/settings.json (lines
968-969), add Japanese translations. In
webview-ui/src/i18n/locales/ko/settings.json (lines 968-969), add Korean
translations. In webview-ui/src/i18n/locales/nl/settings.json (lines 968-969),
add Dutch translations. Each file should preserve the JSON structure with the
same keys but localized string values appropriate for its target language
audience.

In `@webview-ui/src/i18n/locales/tr/settings.json`:
- Around line 968-969: Translate the strictToolSchemas and
strictToolSchemasDescription values from English into the target languages in
webview-ui/src/i18n/locales/tr/settings.json lines 968-969,
webview-ui/src/i18n/locales/vi/settings.json lines 968-969, and
webview-ui/src/i18n/locales/zh-CN/settings.json lines 968-969. Preserve the
existing keys and meaning, including strict schema behavior, provider support
limitations, and the non-strict handling of MCP tools.

In `@webview-ui/src/i18n/locales/zh-TW/settings.json`:
- Around line 995-996: Translate the `strictToolSchemas` and
`strictToolSchemasDescription` entries in the zh-TW settings locale into
Traditional Chinese, preserving the original labels’ meaning and the
description’s strict-mode and MCP-tool behavior.

---

Nitpick comments:
In `@scripts/find-dup-json-keys.js`:
- Line 21: Remove the unused isArray field from the stack frame comment and
update the stack.push call to create frames with only the keys Set. Keep the
existing object-key tracking behavior unchanged.
- Around line 95-98: Update the object-key scanning logic around skipWs and
skipValue to validate that the current character is ':' before advancing. If it
is not, throw the same unexpected-char error used for other malformed input;
otherwise advance and continue skipping the value.
- Around line 6-15: Update walk to use directory-entry metadata via readdirSync
with withFileTypes enabled, avoid descending through symlinked entries, and skip
node_modules, .git, dist, and out directories before recursion. Preserve
yielding only .json files while preventing ancestor-link cycles and unnecessary
traversal.

In `@src/core/assistant-message/ToolCallRetentionPolicy.ts`:
- Around line 203-310: Create a single consolidated input type and emitter
function to replace the duplicate GhostDropTelemetryInput,
MaxOneEnforcementTelemetryInput, emitGhostDropTelemetry, and
emitMaxOneEnforcementTelemetry implementations. Define one input interface with
all the shared fields and one emitter function with the single hasInstance check
and captureToolCallEnforcement call, then re-export the original type names and
function names as type aliases and thin wrapper functions pointing to the
consolidated implementation to preserve existing call sites.

In `@src/core/task/Task.ts`:
- Around line 3024-3041: Extract the duplicated ghost-drop telemetry logic into
one private emitGhostDrop method in Task, resolving the policy and model once
within the helper and setting parallelToolCallsSent consistently with the
existing policy-resolution telemetry. Replace all three inline blocks using
ghostPolicy1, ghostPolicy2, and ghostPolicy3 with calls to this helper,
preserving the current telemetry values.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 90bdef99-32b2-43cd-9266-8de44a444825

📥 Commits

Reviewing files that changed from the base of the PR and between 7918f6b and b526a57.

📒 Files selected for processing (66)
  • docs/260730_0001_session_branch-cleanup/170000_debug-report.md
  • docs/260730_0001_session_branch-cleanup/173200_debug-report.md
  • docs/260730_0001_session_branch-cleanup/173230_execution-plan.md
  • docs/260730_0001_session_branch-cleanup/175300_code-report.md
  • docs/260730_0001_session_branch-cleanup/181500_debug-dnd-ux-runbook.md
  • docs/260730_0001_session_branch-cleanup/182225_code-report.md
  • docs/260730_0001_session_branch-cleanup/184700_debug-report.md
  • docs/260803_0002_session_6-branch-bug-fix-verification/173927_code-environment-feedback.md
  • docs/260803_0002_session_6-branch-bug-fix-verification/174043_code-causal-chain-environment-feedback.md
  • docs/260803_0002_session_6-branch-bug-fix-verification/174704_code-search-environment-feedback.md
  • docs/260803_0002_session_6-branch-bug-fix-verification/175017_code-vitest-environment-feedback.md
  • docs/260803_0002_session_6-branch-bug-fix-verification/175046_code-pnpm-environment-feedback.md
  • docs/260803_0002_session_6-branch-bug-fix-verification/175057_code-report.md
  • packages/telemetry/src/TelemetryService.ts
  • packages/types/src/__tests__/provider-settings.test.ts
  • packages/types/src/model.ts
  • packages/types/src/provider-settings.ts
  • packages/types/src/providers/mimo.ts
  • packages/types/src/telemetry.ts
  • scripts/find-dup-json-keys.js
  • src/api/index.ts
  • src/api/providers/__tests__/base-provider.spec.ts
  • src/api/providers/__tests__/mimo.spec.ts
  • src/api/providers/__tests__/openai.spec.ts
  • src/api/providers/base-openai-compatible-provider.ts
  • src/api/providers/base-provider.ts
  • src/api/providers/deepseek.ts
  • src/api/providers/friendli.ts
  • src/api/providers/kenari.ts
  • src/api/providers/lite-llm.ts
  • src/api/providers/lm-studio.ts
  • src/api/providers/mimo.ts
  • src/api/providers/openai-compatible.ts
  • src/api/providers/openai.ts
  • src/api/providers/opencode-go.ts
  • src/api/providers/openrouter.ts
  • src/core/assistant-message/NativeToolCallParser.ts
  • src/core/assistant-message/ToolCallRetentionPolicy.ts
  • src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts
  • src/core/assistant-message/__tests__/ToolCallRetentionPolicy-telemetry.spec.ts
  • src/core/assistant-message/__tests__/ToolCallRetentionPolicy.spec.ts
  • src/core/prompts/tools/native-tools/execute_command.ts
  • src/core/task/Task.ts
  • src/core/task/__tests__/tool-call-policy.spec.ts
  • src/core/tools/ExecuteCommandTool.ts
  • src/eslint-suppressions.json
  • src/shared/tools.ts
  • webview-ui/src/components/settings/providers/OpenAICompatible.tsx
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ko/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json

Comment on lines +3 to +10
## Task Summary
Analyze the contaminated `feat/error-interception-middleware` branch, classify the 39
local-only commits into "keep" vs "contamination", verify cherry-pick/rebase feasibility
against current `main`, and produce a VP-executable recovery plan. **Per Debug-mode rule 7
(No Git/Version Control Commands) and search-protocol commit-control rules, all git
mutations (branch, cherry-pick, rebase, push, reset) are reserved for the VP.** This report
is diagnostic + planning only. A throwaway dry-run rebase was performed to detect conflicts
and the working tree was restored to its original state afterward.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Make the execution-status statement consistent.

Lines 3-10 state that Debug mode performed analysis only and that Git mutations were VP-only. Lines 9-10 also state that this task performed a throwaway rebase. Creating a temporary branch and running a rebase are Git mutations, even when the branch is later deleted. Identify the authorized executor or revise the report so the audit trail accurately describes what occurred.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260730_0001_session_branch-cleanup/173200_debug-report.md` around lines
3 - 10, The task summary in the report contradicts itself by claiming Git
mutations were VP-only while also describing a throwaway rebase. Update the
summary to identify who authorized and performed the temporary branch/rebase
operations, or remove/revise the rebase statement so it accurately reflects the
audit trail; keep the diagnostic and planning scope clear.

Comment on lines +33 to +42
## Step 3 — Rebase the feature series onto main
```powershell
git rebase --onto main d27153a25 feat/error-interception-middleware-clean
# (clean branch is at main; instead rebase the remote source series)
```
**Corrected command** (rebase the source series, landing on the clean branch name):
```powershell
git checkout feat/error-interception-remote-src
git rebase --onto main d27153a25 feat/error-interception-remote-src
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the obsolete rebase command.

Line 35 rebases feat/error-interception-middleware-clean, which was created at main and does not contain the remote feature series. It does not apply d27153a25..5c8c495e0. Keep only the corrected sequence that checks out feat/error-interception-remote-src and rebases that branch onto main.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260730_0001_session_branch-cleanup/173230_execution-plan.md` around
lines 33 - 42, Remove the obsolete rebase command targeting
feat/error-interception-middleware-clean from Step 3, leaving only the corrected
sequence that checks out feat/error-interception-remote-src and rebases that
branch onto main.

Comment on lines +248 to +251
During `git cherry-pick` the conflicted file is the *new* commit applying onto remote HEAD, so:
```powershell
git checkout --theirs src/core/webview/ClineProvider.ts # keep remote 0453c3a70 version
git add src/core/webview/ClineProvider.ts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Use --ours to preserve the clean squash file.

During git cherry-pick 78ba8218e, --theirs selects the incoming commit. It does not select myk1yt/feature/task-dnd-ux. The incoming ClineProvider.ts comes from contaminated history and can reintroduce unrelated TaskRegistry, shell, or stats changes. Use git checkout --ours src/core/webview/ClineProvider.ts, then verify that only the model and spec changes remain staged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260730_0001_session_branch-cleanup/181500_debug-dnd-ux-runbook.md`
around lines 248 - 251, Update the git cherry-pick instructions to use --ours
for src/core/webview/ClineProvider.ts, preserving the clean squash file rather
than the incoming contaminated commit. Instruct the reader to stage the file
afterward and verify that only the intended model and spec changes remain
staged.

Comment on lines +319 to +325
## 7. Rollback
If verification fails before Step 6:
```powershell
git cherry-pick --abort # if mid-cherry-pick
git checkout feat/error-interception-middleware # or any other working branch
git branch -D feature/task-dnd-ux-clean
# original feature/task-dnd-ux + contaminated-backup remain untouched

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Correct the rollback branch.

Line 323 checks out feat/error-interception-middleware, which is unrelated to this DND cleanup. If rollback is required, this command places the operator on the wrong feature branch. Use feature/task-dnd-ux or another explicitly documented recovery branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260730_0001_session_branch-cleanup/181500_debug-dnd-ux-runbook.md`
around lines 319 - 325, Update the rollback command in the “7. Rollback” section
to check out feature/task-dnd-ux, or another explicitly documented recovery
branch, instead of feat/error-interception-middleware; keep the cleanup and
backup-preservation commands unchanged.

| `src/__tests__/single-open-invariant.spec.ts` | deleted/modified on both sides (main's test suite changes vs stacked-branch deletion) | Not touched by §2.1 commits — no conflict expected in practice |
| `src/eslint-suppressions.json` | BOM churn on the contaminated branch vs main baseline | Avoided entirely by not picking the 4 cleanup commits |
| `webview-ui/playwright-ct.config.ts`, `zoo-hero-dark.png` | binary/config conflicts from stacked ancestors only | Not touched by §2.1 — no conflict expected |
| `615dfbacc` → `src/core/tools/error-interception/StructuralValidator.ts` | file absent on cleaned branch | Cherry-pick will conflict (modify/delete). **Resolution: skip this hunk** (`git restore --source=HEAD -- src/core/tools/error-interception` or just don't stage that path); the StructuralValidator normalization hunk belongs to the error-interception PR, not this one |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Resolve only the conflicted file.

The documented conflict is limited to src/core/tools/error-interception/StructuralValidator.ts. Restoring the whole directory may fail when the directory is absent from upstream/main, and leaving the path unstaged leaves the cherry-pick unresolved. The git rm -r command is also broader than necessary. Remove only src/core/tools/error-interception/StructuralValidator.ts, then verify the unmerged-path list before continuing.

Also applies to: 114-116

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/260730_0001_session_branch-cleanup/184700_debug-report.md` at line 90,
Update the documented conflict resolution for StructuralValidator.ts to remove
only src/core/tools/error-interception/StructuralValidator.ts, avoiding
whole-directory restore or broad git rm commands, then verify the unmerged-path
list is clear before continuing the cherry-pick.

Comment thread src/api/providers/lite-llm.ts Outdated
include_usage: true,
},
tools: this.convertToolsForOpenAI(metadata?.tools),
tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For AI SDK version 6.0.218, what streamTextrequest option orproviderOptionsentry forwardsparallel_tool_callswhen usingcreateOpenAICompatible?

💡 Result:

When using the createOpenAICompatible provider in AI SDK version 6.0.218, the parallel_tool_calls option is passed via the providerOptions field using the provider's name as the key [1][2]. If you configured your provider with a name (e.g., name: 'my-provider'), you should set the option under providerOptions.my-provider [1]. Because the OpenAI Compatible provider forwards these options directly to the underlying API request body, you can include parallel_tool_calls as a property within that namespaced object [1]: const { text } = await streamText({ model: myProvider('model-id'), providerOptions: { 'my-provider': { parallel_tool_calls: false, // or true }, }, }); Note that the key used within providerOptions must match the name defined in your createOpenAICompatible configuration [1]. If you encounter issues where the option is not being forwarded, ensure that the provider name used in the configuration matches the key in your providerOptions exactly [1]. While the official @ai-sdk/openai provider uses a specific TypeScript interface (OpenAILanguageModelResponsesOptions) to type these options, the @ai-sdk/openai-compatible provider treats these entries as generic configuration to be passed to the API [1][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- files ---\n'
git ls-files | rg '(^|/)lite-llm\.ts$|(^|/)openai-compatible\.ts$|resolveToolCallPolicy|parallelToolCalls|parallel_tool_calls' || true

printf '\n--- lite-llm relevant section ---\n'
FILE=$(git ls-files | rg '(^|/)lite-llm\.ts$' | head -n1 || true)
if [ -n "$FILE" ]; then
  wc -l "$FILE"
  rg -n "parallelToolCalls|parallel_tool_calls|convertToolsForOpenAI|requestOptions|streamText|options" "$FILE" -C 3
fi

printf '\n--- openai compatible relevant section ---\n'
FILE2=$(git ls-files | rg '(^|/)openai-compatible\.ts$' | head -n1 || true)
if [ -n "$FILE2" ]; then
  wc -l "$FILE2"
  rg -n "parallelToolCalls|parallel_tool_calls|convertToolsForOpenAI|streamText|providerOptions|options" "$FILE2" -C 4
fi

printf '\n--- policy definition/usages ---\n'
rg -n "resolveToolCallPolicy|parallelToolCalls|parallel_tool_calls" . --glob '!node_modules' --glob '!dist' --glob '!build' -C 2 || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- files ---'
git ls-files | rg '(^|/)lite-llm\.ts$|(^|/)openai-compatible\.ts$|resolveToolCallPolicy|parallelToolCalls|parallel_tool_calls' || true

printf '%s\n'
printf '%s\n' '--- lite-llm relevant section ---'
FILE=$(git ls-files | rg '(^|/)lite-llm\.ts$' | head -n1 || true)
if [ -n "$FILE" ]; then
  wc -l "$FILE"
  rg -n "parallelToolCalls|parallel_tool_calls|convertToolsForOpenAI|requestOptions|streamText|options" "$FILE" -C 3
fi

printf '%s\n'
printf '%s\n' '--- openai compatible relevant section ---'
FILE2=$(git ls-files | rg '(^|/)openai-compatible\.ts$' | head -n1 || true)
if [ -n "$FILE2" ]; then
  wc -l "$FILE2"
  rg -n "parallelToolCalls|parallel_tool_calls|convertToolsForOpenAI|streamText|providerOptions|options" "$FILE2" -C 4
fi

printf '%s\n'
printf '%s\n' '--- policy definition/usages ---'
rg -n "resolveToolCallPolicy|parallelToolCalls|parallel_tool_calls" . --glob '!node_modules' --glob '!dist' --glob '!build' -C 2 || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 417


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- lite-llm.ts outline ---'
ast-grep outline src/api/providers/lite-llm.ts --view compact || true

printf '%s\n'
printf '%s\n' '--- lite-llm.ts relevant lines 180-245 ---'
sed -n '180,245p' src/api/providers/lite-llm.ts | nl -ba -v180

printf '%s\n'
if command -v wc >/dev/null 2>&1; then
  wc -l src/api/providers/openai-compatible.ts
fi
ast-grep outline src/api/providers/openai-compatible.ts --view compact || true

printf '%s\n'
printf '%s\n' '--- openai-compatible.ts relevant lines 120-185 ---'
sed -n '120,185p' src/api/providers/openai-compatible.ts | nl -ba -v120

printf '%s\n'
printf '%s\n' '--- policy definitions/usages ---'
rg -n "resolveToolCallPolicy|parallelToolCalls|parallel_tool_calls" . --glob '!node_modules' --glob '!dist' --glob '!build' -C 2 || true

printf '%s\n'
printf '%s\n' '--- AI SDK openai compatible imports/options in target files ---'
rg -n "createOpenAICompatible|openai-compatible|streamText|providerOptions|requestOptions" src/api/providers/lite-llm.ts src/api/providers/openai-compatible.ts -C 3

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- lite-llm.ts relevant lines 180-245 ---'
sed -n '180,245p' src/api/providers/lite-llm.ts | awk '{printf "%6d\t%s\n", NR+179, $0}'

printf '%s\n'
printf '%s\n' '--- openai-compatible.ts file length ---'
wc -l src/api/providers/openai-compatible.ts

printf '%s\n'
printf '%s\n' '--- openai-compatible.ts relevant lines 120-185 ---'
sed -n '120,185p' src/api/providers/openai-compatible.ts | awk '{printf "%6d\t%s\n", NR+119, $0}'

printf '%s\n'
printf '%s\n' '--- policy definitions/usages ---'
rg -n "resolveToolCallPolicy|parallelToolCalls|parallel_tool_calls" . --glob '!node_modules' --glob '!dist' --glob '!build' -C 2 || true

printf '%s\n'
printf '%s\n' '--- AI SDK provider options in target files ---'
rg -n "createOpenAICompatible|openai-compatible|streamText|providerOptions|requestOptions" src/api/providers/lite-llm.ts src/api/providers/openai-compatible.ts -C 3 || true

printf '%s\n'
printf '%s\n' '--- lock/package references for AI SDK versions ---'
rg -n '"`@ai-sdk/openai-compatible`"|`@ai-sdk/openai-compatible`|ai-sdk.v7|ai-sdk|version.*6.0|6\.0\.218' package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- openai-compatible.ts relevant creation section ---'
sed -n '1,130p' src/api/providers/openai-compatible.ts | awk '{printf "%6d\t%s\n", NR, $0}'
printf '%s\n'
sed -n '153,181p' src/api/providers/openai-compatible.ts | awk '{printf "%6d\t%s\n", NR+152, $0}'

printf '%s\n'
printf '%s\n' '--- LiteLLM tests mentioning parallel_tool_calls or policy ---'
rg -n "parallel_tool_calls|parallelToolCalls|parallelToolCalls" src/api/providers/__tests__/lite-llm.spec.ts || true

printf '%s\n'
printf '%s\n' '--- OpenAI-compatible handler tests mentioning parallel_tool_calls or policy ---'
rg -n "parallel_tool_calls|parallelToolCalls" src/api/providers/__tests__/openai-compatible.spec.ts || true

printf '%s\n'
printf '%s\n' '--- package lock refs for AI SDK compatible package ---'
rg -n '"`@ai-sdk/openai-compatible`"|`@ai-sdk/openai-compatible`|ai-sdk.v7|ai-sdk|6\.0\.218' package.json pnpm-lock.yaml package-lock.json 2>/dev/null | head -n 80 || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 9362


Forward metadata.parallelToolCalls on both request paths.

metadata.parallelToolCalls is available from resolveToolCallPolicy, but both LiteLLM and OpenAI-compatible providers omit it. This lets the upstream request use parallel tool calls even when the chosen policy is single-call.

  • src/api/providers/lite-llm.ts#L222-L222: Add parallel_tool_calls: metadata?.parallelToolCalls ?? true to requestOptions.
  • src/api/providers/openai-compatible.ts#L165-L165: Add a provider-namespaced option under streamText({ model: languageModel, providerOptions: { [config.providerName]: { parallel_tool_calls: metadata?.parallelToolCalls ?? true } } }), or mark this route as local-only enforcement.
  • Add request-capture tests covering parallel_tool_calls: true and parallel_tool_calls: false.
📍 Affects 2 files
  • src/api/providers/lite-llm.ts#L222-L222 (this comment)
  • src/api/providers/openai-compatible.ts#L165-L165
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/lite-llm.ts` at line 222, The LiteLLM and OpenAI-compatible
request paths must forward the resolved parallel tool-call policy instead of
defaulting upstream behavior. In src/api/providers/lite-llm.ts:222, add
parallel_tool_calls to requestOptions using metadata?.parallelToolCalls ?? true;
in src/api/providers/openai-compatible.ts:165, pass the same value through the
provider-namespaced providerOptions for streamText (or explicitly mark that
route as local-only enforcement). Add request-capture tests covering both true
and false values.

Comment thread src/api/providers/mimo.ts
Comment on lines +159 to +198
export function selectExecutableCall(input: SelectExecutableCallInput): SelectExecutableCallResult {
const { calls, maxCallsPerTurn } = input

if (maxCallsPerTurn === "unbounded") {
// Parallel-capable providers: no local enforcement needed.
const firstValid = calls.find((c) => c.hasNativeArgs && !c.isPartial)
return {
executableCallId: firstValid?.callId,
rejectedCallIds: [],
reason: "unbounded-policy",
}
}

// Single-call policy: collect all structurally valid, non-partial calls.
const validCandidates = calls.filter((c) => c.hasNativeArgs && !c.isPartial)

if (validCandidates.length === 0) {
return {
executableCallId: undefined,
rejectedCallIds: [],
reason: "no-valid-candidates",
}
}

if (validCandidates.length === 1) {
return {
executableCallId: validCandidates[0].callId,
rejectedCallIds: [],
reason: "single-valid-candidate",
}
}

// Two or more valid candidates under single-call policy:
// execute NEITHER automatically. All receive error results.
return {
executableCallId: undefined,
rejectedCallIds: validCandidates.map((c) => c.callId),
reason: "multiple-valid-calls-under-single-policy",
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find production callers of selectExecutableCall and emitMaxOneEnforcementTelemetry.
set -euo pipefail

rg -nP --type=ts -C4 '\b(selectExecutableCall|emitMaxOneEnforcementTelemetry)\s*\(' -g '!**/__tests__/**'

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
fd -a 'ToolCallRetentionPolicy\.ts|Task\.ts' . | sed 's#^\./##'

echo
echo "== all references to symbols =="
rg -n 'selectExecutableCall\b|emitMaxOneEnforcementTelemetry\b|maxCallsPerTurn|multiple-valid-calls-under-single-policy|single-valid-candidate|unbounded-policy|no-valid-candidates' . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || true

echo
echo "== ToolCall retention policy outline =="
if [ -f src/core/assistant-message/ToolCallRetentionPolicy.ts ]; then
  ast-grep outline src/core/assistant-message/ToolCallRetentionPolicy.ts --view compact || true
  echo
  wc -l src/core/assistant-message/ToolCallRetentionPolicy.ts
  sed -n '1,260p' src/core/assistant-message/ToolCallRetentionPolicy.ts
fi

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 19328


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
fd -a 'ToolCallRetentionPolicy\.ts|Task\.ts' . | sed 's#^\./##'

echo
echo "== all references to symbols =="
rg -n 'selectExecutableCall\b|emitMaxOneEnforcementTelemetry\b|maxCallsPerTurn|multiple-valid-calls-under-single-policy|single-valid-candidate|unbounded-policy|no-valid-candidates' . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || true

echo
echo "== ToolCall retention policy outline =="
if [ -f src/core/assistant-message/ToolCallRetentionPolicy.ts ]; then
  ast-grep outline src/core/assistant-message/ToolCallRetentionPolicy.ts --view compact || true
  echo
  wc -л src/core/assistant-message/ToolCallRetentionPolicy.ts
  sed -n '1,260p' src/core/assistant-message/ToolCallRetentionPolicy.ts
fi

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 10030


Wire selectExecutableCall into the execution path.

selectExecutableCall is only covered by unit tests; no non-test production site calls it. Under maxCallsPerTurn === 1, add the selection step before executing tool calls so two valid side-effecting calls do not both run automatically, and route rejectedCallIds to error results to trigger enforcement telemetry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/assistant-message/ToolCallRetentionPolicy.ts` around lines 159 -
198, Integrate selectExecutableCall into the production tool-call execution path
before any calls execute, using the current turn’s calls and maxCallsPerTurn
policy. Execute only executableCallId, prevent all rejectedCallIds from running,
and convert each rejected ID into an error result so enforcement telemetry is
triggered; preserve unbounded and single-valid-call behavior.

Comment thread src/core/task/Task.ts
Comment on lines +1043 to +1051
"strictToolSchemas": "Strict tool schemas",
"strictToolSchemasDescription": "Enables strict mode for function tool schemas, ensuring tool outputs match the schema exactly. Some providers may not support strict mode. MCP tools are always kept non-strict regardless of this setting.",
"gemini": {
"freeRequests": "* Free up to {{count}} requests per minute. After that, billing depends on prompt size.",
"pricingDetails": "For more info, see pricing details.",
"billingEstimate": "* Billing is an estimate - exact cost depends on prompt size."
}
},
"strictToolSchemas": "Strict tool schemas",
"strictToolSchemasDescription": "Enables strict mode for function tool schemas, ensuring tool outputs match the schema exactly. Some providers may not support strict mode. MCP tools are always kept non-strict regardless of this setting."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove one duplicate key pair.

modelInfo declares strictToolSchemas and strictToolSchemasDescription twice. Biome reports noDuplicateObjectKeys. Retain one pair only.

🧰 Tools
🪛 Biome (2.5.5)

[error] 1043-1043: The key strictToolSchemas was already declared.

(lint/suspicious/noDuplicateObjectKeys)


[error] 1044-1044: The key strictToolSchemasDescription was already declared.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/i18n/locales/en/settings.json` around lines 1043 - 1051,
Remove the duplicate strictToolSchemas and strictToolSchemasDescription entries
from the modelInfo locale object, retaining exactly one pair with the existing
translations.

Source: Linters/SAST tools

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 4, 2026
@myk1yt
myk1yt force-pushed the pr/b12-mimo-enforcement-v2 branch from 132fb08 to ccf2331 Compare August 4, 2026 20:40
Patch coverage checks were blocking 10+ PRs with 80%/70% thresholds.
Changed to informational: true so patch coverage is reported but not
a required status check.
@github-actions github-actions Bot added has-conflicts PR has merge conflicts with the base branch and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/core/task/Task.ts (1)

3024-3041: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract one ghost-drop helper instead of three copies.

The ghost-drop policy resolution and telemetry payload are duplicated at Lines 3024-3041, Lines 3115-3132, and Lines 3516-3533. Each copy re-resolves the policy into a numbered local (ghostPolicy1, ghostPolicy2, ghostPolicy3) and calls this.api.getModel() twice. The block-removal and re-index logic at Lines 3002-3021 is also duplicated at Lines 3503-3513. One private method keeps the three sites in step and removes the numbered names.

♻️ Proposed helper
private emitGhostDrop(): void {
	const policy = resolveToolCallPolicy(this.api.getModel().info, this.apiConfiguration.apiProvider)
	emitGhostDropTelemetry({
		taskId: this.taskId,
		provider: this.apiConfiguration.apiProvider ?? "unknown",
		model: this.api.getModel().id,
		policySource: policy.source,
		maxCallsPerTurn: policy.maxCallsPerTurn,
		enforcement: policy.enforcement,
		callCount: this.assistantMessageContent.filter(
			(b: AssistantMessageContent): b is ToolUse => b.type === "tool_use",
		).length,
		ghostDroppedCount: 1,
		errorResultCount: 0,
		parallelToolCallsRequested: policy.generation === "parallel",
	})
}

private removeGhostBlock(callId: string): void {
	const ghostIndex = this.streamingToolCallIndices.get(callId)
	if (ghostIndex === undefined) {
		return
	}
	this.assistantMessageContent.splice(ghostIndex, 1)
	for (const [cid, idx] of this.streamingToolCallIndices.entries()) {
		if (idx > ghostIndex) {
			this.streamingToolCallIndices.set(cid, idx - 1)
		}
	}
	this.streamingToolCallIndices.delete(callId)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/task/Task.ts` around lines 3024 - 3041, In the Task class, extract
the repeated ghost-drop telemetry into a private emitGhostDrop method using one
resolved policy and model reference, then replace the three duplicated telemetry
blocks with calls to it. Also extract the repeated block removal and
streamingToolCallIndices re-indexing into a private removeGhostBlock(callId)
method, and update both removal sites to use it while preserving their existing
behavior.
src/api/providers/__tests__/mimo.spec.ts (1)

127-133: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Explain each double assertion with a comment.

These casts build a reasoning content block that the Anthropic MessageParam union does not contain. The cast is a reasonable last resort, but the coding guidelines require a comment that states the reason. The same applies at Line 215, Line 228, Line 346-357, and Line 693-695, where taskId is omitted from ApiHandlerCreateMessageMetadata.

📝 Proposed comment for this site
+						// Double assertion: `reasoning` is a provider-specific block
+						// that the Anthropic MessageParam union does not model, and
+						// convertToR1Format must still handle it.
 						{
 							type: "reasoning" as const,
 							text: "Let me think...",
 						} as unknown as Anthropic.Messages.MessageParam["content"][number],

As per coding guidelines: "Use double assertions only as a last resort and explain them with a comment."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/__tests__/mimo.spec.ts` around lines 127 - 133, Explain
every double assertion in mimo.spec.ts with an adjacent comment stating why it
is necessary: document that the reasoning content block is intentionally used
despite not existing in Anthropic’s MessageParam union, and document the omitted
taskId casts on the referenced metadata objects. Apply this consistently at the
shown assertion sites without changing the test behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@codecov.yml`:
- Line 1: Convert the line endings in codecov.yml from CRLF to LF throughout the
file, without changing its YAML content.

In `@src/api/providers/openai.ts`:
- Around line 375-377: Update the O3-family request construction in the relevant
non-streaming and streaming paths around the tool conversion and
parallel_tool_calls assignments. Only include parallel_tool_calls when
metadata.tools is supplied and resolves to tools; omit the field when tools is
undefined, matching the existing guard used in the other provider paths and
preserving the configured boolean when tools are present.

In `@src/eslint-suppressions.json`:
- Around line 237-255: The no-explicit-any suppression counts must not increase
in src/eslint-suppressions.json. In opencode-go.spec.ts and
qwen-code-native-tools.spec.ts, replace the newly introduced any usages with
appropriate types or unknown plus type guards, then restore both entries to
their previous suppression counts.

---

Nitpick comments:
In `@src/api/providers/__tests__/mimo.spec.ts`:
- Around line 127-133: Explain every double assertion in mimo.spec.ts with an
adjacent comment stating why it is necessary: document that the reasoning
content block is intentionally used despite not existing in Anthropic’s
MessageParam union, and document the omitted taskId casts on the referenced
metadata objects. Apply this consistently at the shown assertion sites without
changing the test behavior.

In `@src/core/task/Task.ts`:
- Around line 3024-3041: In the Task class, extract the repeated ghost-drop
telemetry into a private emitGhostDrop method using one resolved policy and
model reference, then replace the three duplicated telemetry blocks with calls
to it. Also extract the repeated block removal and streamingToolCallIndices
re-indexing into a private removeGhostBlock(callId) method, and update both
removal sites to use it while preserving their existing behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c65700bd-2679-4183-a842-ed38158ddb9d

📥 Commits

Reviewing files that changed from the base of the PR and between 7918f6b and 5e99ad8.

📒 Files selected for processing (45)
  • codecov.yml
  • packages/telemetry/src/TelemetryService.ts
  • packages/types/src/__tests__/provider-settings.test.ts
  • packages/types/src/model.ts
  • packages/types/src/provider-settings.ts
  • packages/types/src/providers/mimo.ts
  • packages/types/src/telemetry.ts
  • src/api/index.ts
  • src/api/providers/__tests__/base-provider.spec.ts
  • src/api/providers/__tests__/mimo.spec.ts
  • src/api/providers/__tests__/openai.spec.ts
  • src/api/providers/base-openai-compatible-provider.ts
  • src/api/providers/base-provider.ts
  • src/api/providers/mimo.ts
  • src/api/providers/openai.ts
  • src/core/assistant-message/NativeToolCallParser.ts
  • src/core/assistant-message/ToolCallRetentionPolicy.ts
  • src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts
  • src/core/assistant-message/__tests__/ToolCallRetentionPolicy-telemetry.spec.ts
  • src/core/assistant-message/__tests__/ToolCallRetentionPolicy.spec.ts
  • src/core/prompts/tools/native-tools/execute_command.ts
  • src/core/task/Task.ts
  • src/core/task/__tests__/tool-call-policy.spec.ts
  • src/core/tools/ExecuteCommandTool.ts
  • src/eslint-suppressions.json
  • src/shared/tools.ts
  • webview-ui/src/components/settings/providers/OpenAICompatible.tsx
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ko/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
🚧 Files skipped from review as they are similar to previous changes (36)
  • src/shared/tools.ts
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • packages/types/src/providers/mimo.ts
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • src/core/tools/ExecuteCommandTool.ts
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • src/core/assistant-message/tests/ToolCallRetentionPolicy.spec.ts
  • src/core/assistant-message/tests/NativeToolCallParser.spec.ts
  • packages/types/src/model.ts
  • packages/types/src/provider-settings.ts
  • packages/telemetry/src/TelemetryService.ts
  • packages/types/src/tests/provider-settings.test.ts
  • src/core/assistant-message/tests/ToolCallRetentionPolicy-telemetry.spec.ts
  • packages/types/src/telemetry.ts
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • src/api/providers/tests/base-provider.spec.ts
  • webview-ui/src/i18n/locales/ru/settings.json
  • src/core/prompts/tools/native-tools/execute_command.ts
  • src/api/index.ts
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/components/settings/providers/OpenAICompatible.tsx
  • webview-ui/src/i18n/locales/ko/settings.json
  • src/core/task/tests/tool-call-policy.spec.ts
  • webview-ui/src/i18n/locales/pl/settings.json
  • src/api/providers/mimo.ts
  • src/core/assistant-message/NativeToolCallParser.ts
  • src/core/assistant-message/ToolCallRetentionPolicy.ts

Comment thread codecov.yml
comment:
layout: "diff, flags, components"
behavior: default
coverage:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use LF line endings.

YAMLlint reports wrong new line character: expected \n at Line 1. Convert codecov.yml from CRLF to LF so the lint check passes.

🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 1-1: wrong new line character: expected \n

(new-lines)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@codecov.yml` at line 1, Convert the line endings in codecov.yml from CRLF to
LF throughout the file, without changing its YAML content.

Source: Linters/SAST tools

Comment on lines +375 to 377
tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false),
tool_choice: metadata?.tool_choice,
parallel_tool_calls: metadata?.parallelToolCalls ?? true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Apply the same parallel_tool_calls guard in the O3-family paths.

Lines 173-175 and 244-246 omit parallel_tool_calls when no tools are supplied. The O3-family paths still send it unconditionally at Line 377 and Line 411, while tools there can resolve to undefined for the same reason. An OpenAI-compatible endpoint that rejects the field without tools still fails when the model id contains o1, o3, or o4.

🔧 Proposed fix for the streaming O3 path (apply the same change at Line 409-411)
 				tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false),
 				tool_choice: metadata?.tool_choice,
-				parallel_tool_calls: metadata?.parallelToolCalls ?? true,
+				// Only send parallel_tool_calls when tools are present; some
+				// OpenAI-compatible providers reject this field without tools.
+				...(metadata?.tools && metadata.tools.length > 0
+					? { parallel_tool_calls: metadata?.parallelToolCalls ?? true }
+					: {}),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false),
tool_choice: metadata?.tool_choice,
parallel_tool_calls: metadata?.parallelToolCalls ?? true,
tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false),
tool_choice: metadata?.tool_choice,
// Only send parallel_tool_calls when tools are present; some
// OpenAI-compatible providers reject this field without tools.
...(metadata?.tools && metadata.tools.length > 0
? { parallel_tool_calls: metadata?.parallelToolCalls ?? true }
: {}),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/openai.ts` around lines 375 - 377, Update the O3-family
request construction in the relevant non-streaming and streaming paths around
the tool conversion and parallel_tool_calls assignments. Only include
parallel_tool_calls when metadata.tools is supplied and resolves to tools; omit
the field when tools is undefined, matching the existing guard used in the other
provider paths and preserving the configured boolean when tools are present.

Comment on lines +237 to +255
"api/providers/__tests__/opencode-go.spec.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 11
}
},
"api/providers/__tests__/openrouter.spec.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 34
}
},
"api/providers/__tests__/poe.spec.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 1
}
},
"api/providers/__tests__/qwen-code-native-tools.spec.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 5
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Git status and diff stat:"
git status --short
git diff --stat src/eslint-suppressions.json || true

echo
echo "Relevant suppression entries (old vs new):"
git diff --no-color src/eslint-suppressions.json | sed -n '200,280p' || true

echo
echo "File sizes and target lines:"
wc -l src/eslint-suppressions.json
sed -n '220,265p' src/eslint-suppressions.json | nl -ba -v220

echo
echo "Search for ESLint suppression manifest usage:"
rg -n '"src/eslint-suppressions.json"|eslint-suppressions|eslint: ' -S .

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 334


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib
p=pathlib.Path('src/esprit-suppressions.json')
PY

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 159


Keep the no-explicit-any suppression counts from increasing.

src/eslint-suppressions.json suppression counts must not increase. The entries for opencode-go.spec.ts and qwen-code-native-tools.spec.ts raise baseline @typescript-eslint/no-explicit-any counts, so remove the new any usages with typed replacements or unknown plus type guards, then restore the previous suppression counts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/eslint-suppressions.json` around lines 237 - 255, The no-explicit-any
suppression counts must not increase in src/eslint-suppressions.json. In
opencode-go.spec.ts and qwen-code-native-tools.spec.ts, replace the newly
introduced any usages with appropriate types or unknown plus type guards, then
restore both entries to their previous suppression counts.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

has-conflicts PR has merge conflicts with the base branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant