Skip to content

feat(friendli): add reasoning params support - #1

Closed
Lee-Si-Yoon wants to merge 53 commits into
mainfrom
feat/friendli-reasoning-params-clean
Closed

feat(friendli): add reasoning params support#1
Lee-Si-Yoon wants to merge 53 commits into
mainfrom
feat/friendli-reasoning-params-clean

Conversation

@Lee-Si-Yoon

Copy link
Copy Markdown
Owner

Summary

Add Friendli-specific reasoning parameter support for GLM-5.x and MiniMax-M2.5 models served via Friendli Model APIs.

Friendli extends the OpenAI Chat Completions API with non-standard reasoning fields (reasoning_effort, chat_template_kwargs, parse_reasoning, include_reasoning) that the base BaseOpenAiCompatibleProvider does not handle. This PR overrides createStream and completePrompt in FriendliHandler to inject these parameters.

Ref: https://friendli.ai/docs/guides/reasoning

Changes

Model catalog (packages/types/src/providers/friendli.ts)

  • GLM-5.2 / GLM-5.1: add supportsReasoningEffort: ["minimal","low","medium","high","xhigh","max"] + reasoningEffort: "high" (controllable reasoning models)
  • MiniMax-M2.5: add supportsReasoningBinary: true (always-reasoning model)

Handler (src/api/providers/friendli.ts)

  • Override createStream and completePrompt to inject Friendli reasoning params via buildFriendliReasoningParams()
  • Controllable reasoning models (GLM-5.x): send reasoning_effort + chat_template_kwargs: { enable_thinking: true } + parse_reasoning/include_reasoning when user selects a valid effort
  • Always-reasoning models (MiniMax-M2.5): send parse_reasoning/include_reasoning only
  • Non-reasoning models (DeepSeek-V3.2): no extra params — reasoning_effort silently ignored
  • When user sets reasoning_effort to "none" or "disable", or enableReasoningEffort=false, no Friendli reasoning params are sent — the model runs at its default behavior
  • delta.reasoning_content streaming already handled by extractReasoningFromDelta in base class

Tests (src/api/providers/__tests__/friendli.spec.ts)

  • 8 reasoning-specific tests added (29 total pass):
    • GLM-5.2 with reasoning enabled → full params sent
    • enableReasoningEffort=false → no params (model default)
    • reasoningEffort="none" → no params (model default)
    • reasoningEffort="disable" → no params (model default)
    • MiniMax-M2.5 always-reasoning → parse_reasoning/include_reasoning only
    • DeepSeek-V3.2 non-reasoning → no params
    • delta.reasoning_content stream parsing
    • completePrompt reasoning params

Design decisions

  • No enable_thinking: false: When reasoning is disabled (none/disable), we send nothing rather than explicitly disabling thinking. This lets the model use its default behavior.
  • No ultracode: Friendli API supports ultracode in the enum but we do not surface it — only the standard efforts up to max.
  • No preserveReasoning: Friendli only supports stateless responses API — multi-turn reasoning re-submission has no effect.
  • Non-reasoning models: If reasoning_effort is set for a model that does not support it, params are silently omitted (no error).

Test plan

  • pnpm vitest run api/providers/__tests__/friendli.spec.ts — 29/29 pass
  • pnpm run build — success
  • pnpm run lint (src + packages/types) — clean

edelauna and others added 30 commits June 25, 2026 20:59
* feat(settings): add rules management UI

* fix: address rules settings review feedback

* test: increase rules settings coverage

* test: improve rules settings patch coverage

* fix: address rules settings review feedback
…de-Org#324)

* fix: parse Gemma 4 <thought> reasoning tags alongside <think>

Gemma 4 streams reasoning inside <thought>...</thought> instead of
<think>...</think>.  Without this the content leaks into chat text and
the agent triggers a retry on the first turn.

- TagMatcher: support multiple tag names - string[], track activeTagName so
    <think> is never closed by </thought> (and vice-versa).
- base-openai-compatible-provider and openai handler: match both tags.
- Tests: <thought> parsing, cross-tag isolation, and invariants.

* fix: support nested reasoning tags in TagMatcher and add comprehensive streaming tests

* test(tag-matcher): add tests for unmatched closing tags

Add two regression tests that verify depth never goes negative:

1. stray closer with no opener
   "final</think>text" → stays text

2. duplicate closer after a proper close
   "<think>thinking</think>final</think>text" → second </think> stays text

Both cases ensure we only decrement depth and pop activeTagNames
when depth > 0, preventing underflow and treating the extra tag
as plain text.

* Apply suggestion from @edelauna

Co-authored-by: edelauna <54631123+edelauna@users.noreply.github.com>

* refactor(providers): updating tag matcher

* test(TagMatcher): adding tests for new logic

* test(tag-matcher): consolidate reasoning tag tests into tag-matcher.spec.ts

---------

Co-authored-by: Sagid Magomedov <sagidsmagomedov@gmail.com>
Co-authored-by: edelauna <54631123+edelauna@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
* chore(deps): update dependency only-allow to v1.2.2

* deps(only-allow): removing unused dep

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore(deps): update dependency react-use to v17.6.1

* deps(csstype): pinning to 3.1.3

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
* chore(deps): update dependency ovsx to v0.10.12

* deps(vsce): bumped to 3.9.2 for consistency

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Co-authored-by: edelauna <54631123+edelauna@users.noreply.github.com>
…o-Code-Org#741)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…sdk to 0.17.1 (Zoo-Code-Org#600)

* chore: upgrade @anthropic-ai/sdk to 0.104.1 and @anthropic-ai/vertex-sdk to 0.17.1

* fix(transform): handle URL-sourced images and non-base64 blocks in format converters

* test: bumping coverage

* test(e2e): adding image test

---------

Co-authored-by: Elliott de Launay <edelauna@gmail.com>
* fix(deps): update ai sdks and providers

* fix(deps): update ai sdks and providers

* deps(ai-sdk): cleaning up orphaned packages

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore(deps): update dependency mermaid to v11.16.0

* test(e2e): addressing zai test flake

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
* chore(deps): update build, lint, and test tooling

* deps(knip): cleaning up knip, and downgrading since zod upgrade required first

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…default model (Zoo-Code-Org#647)

* fix: LiteLLM provider model desync after Sync Models or settings navigation

- Invalidate React Query router-models cache on successful sync in LiteLLM.tsx
  so useSelectedModel picks up the refreshed list (mirrors Poe.tsx fix)
- Preserve configured litellm model ID when model list is empty/loading in
  useSelectedModel.ts instead of silently falling back to hardcoded default
- Pass litellm credentials in the debounced requestRouterModels message in
  ApiOptions.tsx to prevent fetches with stale config from clearing the model list

* fix: address CodeRabbit review comments on litellm model desync

- Refactor getCacheKey to compute URL and key components independently,
  preventing key-scoped providers on the default server from collapsing
  to the same cache entry
- Replace character-substitution in cacheKeyToFilename with SHA-256 hash
  for collision-free filesystem-safe filenames
- Block AUTH_SCOPED_PROVIDERS (zoo-gateway) in getModelsFromCache to
  prevent stale user-specific model lists from leaking across sessions
- Wrap requestRouterModels switch case body in braces to satisfy Biome
  noSwitchDeclarations lint rule
- Preserve configured modelId in RouterProvider.getModel() last-resort
  fallback instead of silently defaulting to hardcoded defaultModelId
- Add codeql[js/insufficient-password-hash] suppressions on SHA-256
  cache key discriminator calls (false positive: not password hashing)

* chore: remove ineffective codeql suppression directives

The inline // codeql[js/insufficient-password-hash] comments did not
suppress the alert because CodeQL tracks taint flow from source variables
in other files. Retaining the explanatory prose comments for human
reviewers; the false positive requires maintainer dismissal in the
GitHub Security tab.

* test(webviewMessageHandler): correct LiteLLM credential priority assertion

The test "prefers config values over message values for LiteLLM" was
asserting the old || behavior. The handler now uses ?? so message.values
takes precedence over saved config, matching the DeepSeek pattern and
allowing unsaved settings UI state to be used during Sync Models.

* fix(security): derive cache-key discriminator without flagged API-key hashing

Replace the SHA-256 hash of the API key in the LiteLLM/key-scoped cache key
with a memoized, truncated PBKDF2-derived 32-bit discriminator. This resolves
the CodeQL js/insufficient-password-hash alert at the source (Node crypto.pbkdf2Sync
is not modeled as a weak-hash sink) rather than dismissing it as a false positive,
and the heavy truncation makes the value written to the on-disk cache filename
non-reversible to the API key. Add provider-agnostic tests covering per-key
separation, determinism, and the non-identifying discriminator shape.

* fix(security): derive cache filename digest via KDF to clear residual CodeQL taint

The API-key discriminator is embedded in the compound cache key, so CodeQL taint
tracking treats the cache key as password-derived and flagged the remaining
createHash(sha256) in cacheKeyToFilename. Route the filename digest through the
same truncated PBKDF2 helper (deriveCacheDigest) and remove createHash entirely,
eliminating the last weak-hash sink the tainted value can reach. Filename width
is preserved (64-bit/16-hex).

* test: cover all getCacheKey scoping branches in modelCache

* fix: resolve check-types errors in modelCache tests

* fix: address PR review for LiteLLM cache key collision fix

- poe/lm-studio getModel() pass GetModelsOptions so compound cache key matches; bare-string lookups previously always missed and fell back to defaults
- router-provider uses || so empty-string modelId falls back to default instead of forwarding "" to the API
- LiteLLM Sync invalidates the exact ["routerModels","litellm"] query rather than the broad ["routerModels"] prefix
- document in-flight dedup microtask-safety invariant in modelCache
- add tests: list non-empty->empty transition preserves selection; key-scoped in-flight dedup (different keys -> separate fetch)
* fix(shell): validate defaultProfileName is a string before calling .toLowerCase()

config.get<string>() has no runtime type check, so non-string values
(number, boolean, array, object) from settings.json pass through and
cause a TypeError. Add typeof guard in each platform's terminal config
helper and add corresponding test cases.

* refactor(shell): unify platform terminal config helpers into generic getTerminalConfig<T>()

* test(shell): add test cases for getTerminalConfig behavior

* refactor(shell): apply code review feedback to getTerminalConfig

- Replace TerminalProfiles union with PlatformProfilesMap mapped type so
  the return type is inferred from the platform key, eliminating explicit
  type parameters at each call site and making mismatches a compile error
- Change || to ?? when falling back to empty profiles object
- Add JSDoc explaining the key constraint and inferred return type
- Add missing boolean/array non-string defaultProfileName test cases for
  macOS and Linux to match Windows coverage
- Add mutation-resistant tests for macOS and Linux that verify the typeof
  guard is load-bearing (numeric key matching a real profile entry)
* fix(task): handle rejections on void-prefixed async calls in Task.ts

Per Copilot review on Zoo-Code-Org#253: the void-prefixed async calls flagged by
the reviewer (postStateToWebviewWithoutTaskHistory, startTask and
resumeTaskFromHistory in the constructor, startTask in start(), and
the nine presentAssistantMessage(this) sites in the streaming loop)
can become unhandled promise rejections on failure and crash the
extension host.

Each flagged site now has a .catch handler that logs the rejection
without re-throwing, while keeping the void prefix to satisfy
no-floating-promises. presentAssistantMessage was wrapped in a small
private helper, presentAssistantMessageSafe, that distinguishes the
expected throw-on-abort path (silently swallowed) from real failures
(logged). All nine streaming presenter call sites delegate through
the helper so the rejection-handling logic lives in one place.

Adds six specs under "unhandled-rejection guards on void async calls"
that pin the new behavior: every catch handler is asserted to log on
rejection, and the helper's abort vs non-abort branches are both
exercised. Single-line fire-and-forget UI updates and the streaming
presenter call sites carry /* v8 ignore next */ markers with a short
rationale, since the rejection-handling logic they delegate to is
covered separately by the helper specs.

* fix(Task): match the abort error message in presentAssistantMessageSafe

The helper's catch handler was distinguishing abort from real
failures via `this.abort` state at catch time, which has a real
TOCTOU window: a non-abort throw followed by an abort flip
between throw and catch microtask would silently swallow the
real error.

Switched to matching `error.message.endsWith("aborted")` —
the literal contract presentAssistantMessage itself throws on
abort. Same suppression for the abort case, no swallow window
for real errors.

Added a test that pins the message-based discriminator: a
non-abort error with `this.abort = true` now correctly logs
(would have been swallowed under the state-based check). Also
added a regression test for the abort-message-match path so a
future refactor can't drift back to the state check.

* fix(Task): catch updateClineMessage rejections on partial-message paths

The four fire-and-forget updateClineMessage calls in say()/ask()
partial-message branches were void-prefixed without .catch arms. The
callee's webview post is internally guarded, but its synchronous
RooCodeEventName.Message emit can throw via a consumer-attached
listener — so the void sites can produce unhandled rejections.

Replace each `void this.updateClineMessage(...)` with an explicit
.catch arm that logs the error, matching the other rejection-handling
sites in this file. Two new tests pin the say() and ask() catch arms.

Also pin the void on getCheckpointService with a rationale comment:
its top-level try/catch returns undefined on failure, so .catch is
not needed there.

* feat(Task): adding catch block to log any errors

* test(Task): coverage

---------

Co-authored-by: 0xMink <260166390+0xMink@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…Zoo-Code-Org#228)

* feat(label-pr-review-state): updating logic for awaiting-review

* feat(label-pr-review-state): check only required steps

---------

Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…-Code-Org#725)

* fix(delegation): atomically serialize reopenParentFromDelegation

* fix(delegation): harden atomic parent reopen handoff

* fix(delegation): broadcast atomic handoff history updates
* fix(vscode-lm): reliable auto context condensing

Port of simurg79/Roo-Code#11 into Zoo-Code.

* test(vscode-lm): cover condense-window edge branches for codecov/patch

Add targeted tests for the previously-uncovered ported branches: the availableInputTokens<=0 fallback to 100% in willManageContext/manageContext, getCondenseContextWindow() guard fallbacks, and the vscode-lm UI family-miss window resolution. Raises patch coverage to satisfy the codecov/patch 80% gate.

* chore(vscode-lm): address review — drop changeset, fix condense-window guard test

- Remove .changeset/vscode-lm-condense-fix.md (changesets are maintainer-managed per AGENTS.md; CodeRabbit flagged).

- Fix getCondenseContextWindow() non-positive-guard test so the selector family (claude-opus-4.8) drives the lookup and the zeroed static row actually exercises the maxInputTokens > 0 guard before falling back.

* fix(vscode-lm): scope available-input condense denominator to vscode-lm; address review

Address review feedback from edelauna on Zoo-Code-Org#710:
- Scope the available-input-space condense percent denominator to vscode-lm only (via the getCondenseContextWindow seam); all other providers keep dividing by the full context window. The maxTokens:-1 reserve guard remains global.
- Correct the misleading useSelectedModel comment: the gate's primary window is getCondenseContextWindow() (static maxInputTokens), not getModel().info.contextWindow.
- Strengthen the listed-family test with a claude-opus-4.8 case (contextWindow != maxInputTokens) to catch a field swap.

* docs(vscode-lm): tighten auto-condense comments

Simplify comments added in PR Zoo-Code-Org#710 to be brief and rationale-focused; no logic, assertions, or test values changed.

* test(vscode-lm): harden condense reserve-guard coverage

Address review: make the negative-reserve percent test discriminate the maxTokens>0 guard (-200000), and add a manageContext truncation-path test so both copies of the -1 reserve guard are covered. Test-only; no product code changed.

* test(task): fix Task.dispose teardown-race flake

Mock utils/storage and OutputInterceptor in Task.dispose.test.ts so dispose()'s unawaited cleanup chain cannot emit a late console.error during Vitest worker teardown (EnvironmentTeardownError: Closing rpc while onUserConsoleLog was pending). Test-only; no product code changed.

* refactor(vscode-lm): default-row condense fallback + shared contextPercent helper

getCondenseContextWindow() resolves the default vscode-lm row for an unknown/absent family (catalog drift) instead of the inflated live window; only a non-positive static maxInputTokens still falls back to it. Extract the duplicated contextPercent math shared by willManageContext and manageContext into computeContextPercent so the two stay in lockstep. Addresses PR review feedback.

---------

Co-authored-by: Bertan Ari <bertanari@microsoft.com>
…values (Zoo-Code-Org#713) (Zoo-Code-Org#774)

* fix(ThinkingBudget): handling xhigh, max and disabled values

* feat(ThinkingBudget): set dropdown to available options

* test(ThikningBudget): coverage
* feat: implement Claude Sonnet 5 support in Zoo Code

Add full Claude Sonnet 5 support across all provider paths with correct
model definitions, adaptive-thinking handling, fetcher overrides, and
accurate introductory pricing.

Model definitions (anthropic.ts, bedrock.ts, vertex.ts):
- 1M native context window (no beta header required)
- Adaptive-thinking binary toggle (supportsReasoningBudget/Binary)
- supportsTemperature: false (sampling params return a 400)
- Introductory pricing through Aug 31, 2026:
  inputPrice 2.0, outputPrice 10.0, cacheWrites 2.5, cacheReads 0.2
  (standard $3/$15 rates take effect Sep 1, 2026)

Provider handling:
- Add sonnet-5 to the Bedrock adaptive-thinking guard
- Add claude-sonnet-5 to OpenRouter and Vercel AI Gateway allow-lists
- Apply Sonnet 5 overrides (maxTokens, reasoning budget, temperature)
  in the openrouter, requesty, and vercel-ai-gateway fetchers

Tests:
- Add Sonnet 5 coverage across provider and fetcher test suites

Closes Zoo-Code-Org#777

* test(bedrock,vercel): address Sonnet 5 PR review feedback

- Add Bedrock createMessage adaptive-thinking test for claude-sonnet-5
  (mirrors Opus 4.7/4.8: asserts thinking.type 'adaptive',
  output_config.effort 'xhigh', and temperature omitted).
- Tighten Vercel AI Gateway Sonnet 5 temperature assertion to extract
  the call arg directly instead of objectContaining({ temperature:
  undefined }), which can't distinguish absent from explicitly undefined.

Refs Zoo-Code-Org#778
…tion reconciliation (Zoo-Code-Org#692)

* feat(task-lifecycle): transition guard and startup delegation reconciliation

* test: updating TaskPersistence vitest mocks

* fix(task-persistence): enforce status transition guard at upsertCore write boundary

* fix(delegation): guard concurrent rollback pop and add transition tests

* feat(ClineProvider): guarding parent, child transitions

* feat(ClineProvider): adding delegatedToId

* test(subtask): adding e2e test and apis for history tracking

---------

Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
…o-Code-Org#726)

* fix: provider cache reset after settings import

SettingsView keeps a local cachedState buffer so settings edits do not write through to the live extension state before Save. After importing settings, the import timestamp was used to bust that cache, but the effect could run again for the same import timestamp whenever extensionState changed identity.

That allowed a later live state update to overwrite an in-progress provider edit with the previously saved provider, causing the provider UI to briefly switch and then revert.

Track the handled settingsImportedAt value and only reset cachedState once per import event.

Add a regression test for the imported-settings flow where Baseten is saved with an API key, the same import timestamp is replayed, and a subsequent DeepSeek provider edit is preserved and saved.

* test: cover settings import timestamp cache reset behavior

Strengthen SettingsView regression coverage around settingsImportedAt.

Verify that replaying the same import timestamp does not let the
cache-busting effect overwrite an in-progress provider edit, and add
the inverse case to prove a fresh import timestamp still refreshes
cachedState from the latest extensionState apiConfiguration.

Also assert that a fresh import clears the dirty state by disabling the
Save button after pending edits are replaced by imported settings.
…tus messages (Zoo-Code-Org#734)

* feat(semble): upgrade to v0.4.1, flatten result parsing, localize status messages

- Bump SEMBLE_VERSION v0.3.1 -> v0.4.1 and refresh SEMBLE_SHA256 checksums
- Adapt to semble v0.4.0+ flat JSON output (no chunk wrapper); remove
  SembleChunk and flatten SembleSearchResult (file_path, start_line,
  end_line, score, content)
- Update provider.ts and semble-cli.ts parsing accordingly
- Version-prefix the local archive cache path so a stale archive from a
  previous semble version is never reused or verified against the new
  checksum; remove partial archive before re-downloading
- Add cleanupStaleArchives to best-effort sweep orphaned archives after
  a version upgrade
- Localize SembleProvider status strings via i18n embeddings:semble.*
  keys across all 18 locales
- Update provider/cli/downloader unit tests for the new shape and
  version-prefixed cache path

Closes Zoo-Code-Org#733

* fix(semble): clean legacy unversioned archives and cover stale-archive sweep

CodeRabbit PR Zoo-Code-Org#734 follow-ups:

- cleanupStaleArchives now also matches the exact unversioned archive name
  (pre-v0.4.0 cache layout) in addition to the version-prefixed suffix, so a
  v0.3.1 -> v0.4.1 upgrade also clears the legacy file. The current archive
  path is still preserved.
- semble-downloader.spec: the version-upgrade test now simulates a prior-version
  archive (v0.4.0-*) and a legacy unversioned archive in the cache, and asserts
  both are swept during the upgrade flow.
- Add coverage for the cleanupStaleArchives catch block (readdir rejects) and
  for the current-archive/unrelated-file preservation behavior.
- provider.spec: add a test that rejects search with a non-Error value to cover
  the 'error instanceof Error' false branch of the telemetry payload (stack:
  undefined), raising provider.ts patch coverage.

* feat(semble): surface active version in the CodeIndexPopover status message

Export SEMBLE_VERSION from semble-downloader and re-export from the semble
barrel. SembleProvider now interpolates the active version into the
'embeddings:semble.ready' system-state message (set in both _doInitialize and
startIndexing), so the CodeIndexPopover status line shows e.g.
'Indexed - Semble v0.4.1 is ready. Searches index on-the-fly.'

- semble-downloader.ts: export SEMBLE_VERSION.
- index.ts: re-export SEMBLE_VERSION.
- provider.ts: pass { version: SEMBLE_VERSION } to t('embeddings:semble.ready').
- i18n: update semble.ready across all 18 locales to include {{version}}.
- provider.spec.ts: mock SEMBLE_VERSION, update the ready-message mock to
  interpolate version, and add a test asserting the version appears in the
  ready status message.

* fix review comments
…st-privilege workflow permissions (Zoo-Code-Org#782) (Zoo-Code-Org#783)

* deps: updating security posture in ci

* deps: addressing feedback

---------

Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
…nt 400 errors (Zoo-Code-Org#775)

* fix(deepseek): reasoning_content handler across deepseek model providers

* fix(openai-format): accumulate all parts

* fix(openai-format): update types for deepseek

* test(openai-format): cover getReasoningBlockText non-object guard

Add a regression test that passes a non-object content part (a stray
string) through convertToOpenAiMessages, exercising the defensive
 early-return in getReasoningBlockText
(line 276). This was the only uncovered line in the PR patch, causing the
codecov/patch check to fail.

---------

Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
roomote Bot and others added 23 commits July 4, 2026 02:24
…Org#234)

Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…andler (Zoo-Code-Org#767)

* refactor: remove deprecated openai-error-handler and consolidate into error-handler

Replace all imports of the deprecated `openai-error-handler` shim with
`error-handler`, which has been the canonical source since #10204.
Delete `openai-error-handler.ts` and its duplicate test file, as
`error-handler.spec.ts` already covers the same behavior.

Signed-off-by: daewoongoh <dw.oh@samsung.com>

* test(deepseek): add test for API error handling in createMessage

Cover the catch block that wraps API errors via handleOpenAIError,
fixing 0% patch coverage on the error path.

Signed-off-by: daewoongoh <dw.oh@samsung.com>

* refactor(deepseek): convert dynamic import to static for handleOpenAIError

Replace dynamic `await import()` with a static import for `handleOpenAIError`
in the catch block, and update the error handler test to also assert the HTTP
status code (401) on the thrown error.

Signed-off-by: daewoongoh <dw.oh@samsung.com>

---------

Signed-off-by: daewoongoh <dw.oh@samsung.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Zoo-Code-Org#789)

The completePrompt method accessed response.content[0] directly, which
could throw a TypeError when the content array is empty, and would
return an empty string when the first block is a non-text block (e.g.
thinking or tool_use) even when a text block is present later in the
array.

Use Array.prototype.find() to locate the first text block, matching
the pattern already used in anthropic.ts. This safely returns undefined
for empty arrays and surfaces the actual text response regardless of
its position among content blocks.

Add regression tests covering an empty content array and a mixed
content array where a thinking block precedes the text block.

Signed-off-by: daewoongoh <dw.oh@samsung.com>
…k is interrupted (Zoo-Code-Org#560) (Zoo-Code-Org#787)

* fix(task-lifecycle): preserve parent-child link when delegated subtask is interrupted

* test(e2e): interrupted child resumes and reports back to parent

* Fix interrupted subtask resume state restoration

* fix(task-lifecycle): prevent parent repair from winning race against child cancellation

* fix(api): return 0 instead of throwing for unavailable task history length

* test(webview-settings): increase change-detection test timeout to resolve CI flakes

---------

Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
Co-authored-by: Test User <test@example.com>
…Org#803)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…#805)

* chore(deps): update dependency @types/react to v18.3.31

* fix(nightl-publish): too agressively skipping pre-releases

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Test User <test@example.com>
…Org#721)

* feat(friendli): add provider + 4 models

Friendli OpenAI-compatible provider. Models: GLM-5.2, GLM-5.1,
DeepSeek-V3.2, MiniMax-M2.5. Pricing from friendli.ai pricing API.

Mirror fireworks provider pattern (commit 4a9222b):
- types: model info, schema, secret key, provider name
- api: BaseOpenAiCompatibleProvider handler at api.friendli.ai/serverless/v1
- webview: provider UI, model picker, validation, i18n (18 locales)
- tests: handler spec, ProfileValidator entry

Reasoning streams back via base handler's reasoning_content extraction.
Friendli defaults parse_reasoning=true for GLM reasoning models.

* docs(friendli): add docstrings to handler and UI component + revert bug_report.yml Friendli entry

* docs(friendli): add docstrings to handler class and settings component

* test(friendli): add wiring coverage for validate, useSelectedModel, Friendli.tsx

* test(friendli): cover onInput, ApiOptions wiring, buildApiHandler switch

* test: add friendli to SettingsView change-detection mock provider list

* test(friendli): assert supportsMaxTokens/cacheWritesPrice, cover GLM-5.1 clamping path

- Add supportsMaxTokens and cacheWritesPrice assertions to GLM-5.2
  single test and it.each model table
- Add getModelMaxOutputTokens integration tests verifying:
  - GLM-5.2: 131k < 20% of 1M context → clamp no-op
  - GLM-5.1: 131k > 20% of 200k context → clamp binds to 40k
  - GLM-5.1 with user modelMaxTokens: honors override capped at model maxTokens
- Cast friendliModels[modelId] to ModelInfo to avoid union-type
  narrowing issue with as const satisfies

* test: restrict webview-ui workers in CI to prevent JSDOM CPU starvation

---------

Co-authored-by: siyoon <leesiyoon0120@gmail.com>
Co-authored-by: Elliott de Launay <edelaunay@wealthsimple.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* feat(ollama): add native thinking/reasoning support

- Add Enable Thinking checkbox + ThinkingBudget selector to Ollama settings
- Map reasoning effort to Ollama native think param (low/medium/high)
- Stream message.thinking as reasoning chunks
- Round-trip prior reasoning/thinking blocks for multi-turn thinking
- Add unit tests for native-ollama and Ollama settings component
- Add i18n strings for thinking toggle

Closes Zoo-Code-Org#831

* i18n: add Ollama thinking translations for all locales

Adds thinking and thinkingHelp strings for the new Ollama native
thinking toggle across all 17 non-English webview-ui locales.

Part of Zoo-Code-Org#831

* fix(ollama): resolve check-types error, gate think param on opt-in, address CodeRabbit comments

- Fix TS2367 in native-ollama.ts: remove unreachable 'image' branch from
  assistant message conversion (Anthropic ContentBlock union has no 'image';
  assistant cannot send images). User-message image handling is preserved.
- Gate getOllamaThinkParam on enableReasoningEffort === true so a stale
  reasoningEffort inherited from another provider config cannot silently
  emit a think param when the Ollama UI checkbox is unchecked.
- Extract buildChatRequestOptions() helper shared by createMessage and
  completePrompt to eliminate duplicated request-option assembly.
- Ollama settings UI: preserve reasoningEffort across checkbox toggles
  instead of wiping it to undefined when unchecked; restore the prior value
  on re-enable rather than forcing 'medium'.
- Add tests for the stale-config case, the disable+opt-in case, and the
  completePrompt non-Error throw branch. Coverage now 83.89% (>= 80%).

* docs(ollama): add explanatory comment for removed Anthropic image branch in assistant messages

* test(ollama): improve patch coverage for native thinking support

Add tests covering previously uncovered branches in native-ollama.ts:
- Anthropic-protocol thinking block round-tripping (block.type === thinking)
- Concatenation of multiple reasoning/thinking blocks with newline joins
- Empty reasoning/thinking blocks (length > 0 false branch + reasoningText || undefined)
- Plain assistant message without reasoning blocks (falsy reasoningText branch)
- Unknown reasoningEffort value (default switch branch returns undefined)
- Stream processing error wrapping (catch streamError branch + Unknown error fallback)
- Non-ECONNREFUSED non-404 error rethrow (fall-through throw error branch)

Also strengthen the Ollama.spec.tsx toggle-off assertion per CodeRabbit
nitpick: assert no call with reasoningEffort as the first argument at
all, instead of only ruling out undefined.

Patch coverage for native-ollama.ts rises from 71.05% to ~100% of new
lines, clearing the codecov/patch 80% threshold.
…nsing (Zoo-Code-Org#848)

* ollama fixes

* fix(ollama): isolate failing model fetches and address PR Zoo-Code-Org#848 review

- getOllamaModels: append per-request .catch() so a single failing
  /api/show no longer rejects the whole Promise.all and wipes out all
  healthy models (issue Zoo-Code-Org#851)
- native-ollama: keep tool results text-only; move images extracted from
  tool results onto the adjacent user message and reset the per-result
  image accumulator to prevent leakage between tool results
- native-ollama: recursively strip additionalProperties from tool schemas
  (top-level and nested properties/items) instead of only the top level
- tests: update ollama fetcher maxTokens expectations to the inherited
  default (4096), add regression test for individual model fetch failure,
  and add coverage for text-only tool results, image relocation, image
  leakage prevention, and nested additionalProperties stripping
…g to claude-sonnet-4-5 (Zoo-Code-Org#842)

* fix(anthropic): honor custom apiModelId instead of silently defaulting to claude-sonnet-4-5

Unrecognized model IDs were coerced to the hardcoded default before being sent to the API and for capability lookups, breaking custom deployments and picking the wrong thinking config.

Fixes Zoo-Code-Org#418

* style: trim comments to a single line

* chore: add changeset for anthropic custom model id fallback fix

* Hoist sorted model IDs out of guessModelInfoFromId method; Make model ID matching case insensitive in Anthropic

---------

Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
…ode-Org#807)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…o-Code-Org#808)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-Org#876)

* feat(openai): add GPT-5.6 family (sol, terra, luna) support

Add the GPT-5.6 model family to both the OpenAI Codex (ChatGPT
subscription) and OpenAI Native (API) providers:

- gpt-5.6-sol: flagship frontier reasoning/coding model
- gpt-5.6-terra: balanced everyday model
- gpt-5.6-luna: fastest, most affordable family member

Update the default model id for both providers to gpt-5.6-sol and
update the corresponding unit test assertions.

Closes Zoo-Code-Org#871

* fix(openai-codex): correct GPT-5.6 context windows to 372000

Address review feedback on PR Zoo-Code-Org#876: the GPT-5.6 model context windows
in the Codex provider were set to 400000, but the OpenAI codex
models.json shows context_window and max_context_window of 372000 for
all three GPT-5.6 variants (sol, terra, luna). Updated accordingly.
…g#809)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…Org#810)

* chore(deps): update dependency sanitize-filename to v1.6.4

* test: fix flaky change detection test by wrapping rerender with act

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelaunay@wealthsimple.com>
…-Org#812)

* chore(deps): update dependency tree-sitter-wasms to v0.1.13

* test: add tree-sitter-dart.wasm to verified dist assets list

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelaunay@wealthsimple.com>
…-Org#270)

Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…_template_kwargs, parse_reasoning

- Add supportsReasoningEffort + reasoningEffort to GLM-5.x model catalog entries
- Add supportsReasoningBinary to MiniMax-M2.5 model catalog entry
- Add 'ultracode' to supportsReasoningEffort schema in model.ts
- Override createStream + completePrompt in FriendliHandler
- buildFriendliReasoningParams(): injects Friendli-specific reasoning params
  - Controllable (GLM-5.x): reasoning_effort + chat_template_kwargs + parse_reasoning
  - Always-reasoning (MiniMax-M2.5): parse_reasoning only
  - Non-reasoning: no extra params
- Add 8 reasoning-specific test cases (29 total)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.