Commit Graph

66940 Commits

Author SHA1 Message Date
chengzhichao-xydt
dfeb15261d fix(agents): keep tool_search_code stderr tail UTF-16 safe (#104767)
* fix(agents): keep tool_search_code stderr tail UTF-16 safe

* test(agents): add real child-exit UTF-16 safety proof

* test(agents): focus stderr UTF-16 regression

---------

Co-authored-by: chengzhichao-xydt <chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 17:08:56 -07:00
Peter Steinberger
4e4cca2b99 fix(agents): surface utility-model narration failures and show the utility model in models status (#104770)
* fix(agents): surface utility-model narration failures and show the utility model in models status

Narration failures were verbose-only, so a dead utility-model credential
silently degraded channel progress drafts back to raw tool lines. The
narrator now warns once per turn when it disables after consecutive
failures, naming the utility model + auth profile, and openclaw models
status renders the resolved utility model (config / provider default /
disabled) in human and JSON output.

Formatting verified remotely via Testbox format:check (hook bypassed:
no node_modules in this worktree).

Fixes #104764

* fix(models): include the utility model in models status auth analysis

The utility ref was rendered but not registered as a provider use, so
missing utility-provider auth kept --check green (Codex review P2).
Utility completions ride the plain API path like image models.

Formatting verified remotely via Testbox format:check.

* fix(models): register the utility model only for otherwise-unused providers

main's route analysis reports per-model issues; a same-provider utility
ref duplicated route reports (CI: incompatible-routes test). Shared
providers are already analyzed via their configured uses; utility-only
providers still enter the analysis so they stay visible to status.

Formatting verified remotely via Testbox format:check.

* fix(models): always analyze the utility model route in models status

Same-provider dedupe hid the documented incident class: an OAuth-healthy
primary says nothing about the utility model's plain API route (Codex
review round 3). The openai route-test fixture now pins utilityModel off
so route tests stay focused on their configured models, and a dedicated
test covers the OAuth-primary/api-key-utility scenario.

Formatting verified remotely via Testbox format:check.

* fix(models): give the utility model full text-route analysis in models status

Uses without codex fallback previously skipped per-model route
resolution (image-auth scoping), so the utility ref never produced
missing-auth route issues. Route scope is now explicit: text uses get
evaluateModelAuth, image uses keep provider-wide availability.

Formatting verified remotely via Testbox format:check.

* fix(models): probe the configured utility model and document source labels

--probe candidates now include the utility ref so utility-only providers
probe the actual configured model instead of an arbitrary catalog entry
(Codex review round 4). Inline comment records that the status resolver
never falls back to the primary, so 'provider default' labels are exact.

Formatting verified remotely via Testbox format:check.

* fix(models): canonicalize the reported utility ref and dedupe route diagnostics

utilityModel accepts aliases, so status now reports the resolved
provider/model instead of the raw alias, and identical route issues from
overlapping primary/utility refs collapse to one entry (Codex round 5).

Formatting verified remotely via Testbox format:check.
2026-07-11 17:08:19 -07:00
Igor Guarisma
4147345bcd feat(android): show agent avatar instead of first-letter badge (#103248)
* feat(android): render configured agent avatars

Co-authored-by: Igor Guarisma <igor.guarisma@gmail.com>

* style(android): satisfy avatar ktlint

* style(android): fix avatar test formatting

* style(android): align avatar test indentation

* fix(android): initialize safe image client first

* chore(android): sync avatar i18n inventory

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 17:07:46 -07:00
Ted Li
dbe53d6212 fix(cli): avoid zero usage cost during cache refresh (#103998)
* fix(cli): wait for usage-cost cache refresh

* test(cli): type usage cost handler response

* fix(cli): honor usage-cost timeout budget

* fix(cli): give usage audit a complete settle budget

* fix(cli): wait through stale usage caches

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 17:07:37 -07:00
RickLin
c749d714e9 fix(config): register zod locale explicitly so packaged builds keep real validation messages (#104026)
zod@4 declares sideEffects:false, so bundlers tree-shake the classic entry's
implicit config(en()) locale registration and every issue in a built dist
falls back to the bare "Invalid input" message. Users hit unactionable
errors like "agents.defaults: Invalid input" (#89445, #103956) and
message-text-keyed issue filtering in validation.ts diverges from source
behavior. Register the English locale explicitly next to the config schemas;
zod keeps its config on globalThis, so one call covers the whole process.

Fixes #104014
2026-07-12 08:06:58 +08:00
wuqxuan
dfa9569ca7 fix(google): exclude versioned Gemini client header from memory identity (#104759)
* fix(google): exclude versioned Gemini client header from memory identity

* test(google): focus memory identity coverage

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 17:06:21 -07:00
Peter Steinberger
0fbd28476e fix: fence live updater maintenance (#104802) 2026-07-11 17:05:35 -07:00
qingminlong
c59e24647c fix(agents): reject invalid goal token budgets (#104534)
* fix(agents): reject invalid goal token budgets

* test(agents): update goal assertion for session accessor

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 17:05:04 -07:00
mushuiyu886
d886059520 fix(line): preserve Unicode boundaries in recipient errors (#104109) 2026-07-11 17:03:58 -07:00
xingzhou
57c2d65ad5 fix(apps): reconcile slow Apple chat replies (#97722)
* fix(apps): reconcile slow chat replies

Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>

* style(apps): wrap wait diagnostic

* chore(apps): refresh native i18n inventory

* chore: leave changelog release-owned

* style(apps): normalize Swift modifiers

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 17:01:40 -07:00
llagy007
7027d2c227 fix(browser): reject non-page json new targets (#104129)
* fix(browser): reject non-page json new targets

* fix(browser): adopt only validated raw CDP targets

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:58:43 -07:00
Peter Steinberger
3f7c78319a fix(ci): enforce Docker E2E boundary guard (#104797) 2026-07-11 16:56:46 -07:00
Peter Steinberger
02dc25a1b5 feat(gateway): persist operator approvals (#103579)
* feat(gateway): persist operator approvals

* fix(gateway): preserve approval ids exactly

* fix(gateway): enforce approval reviewer bindings

* fix(gateway): reconcile durable approvals with exec revocation hardening

Map #103515 semantics onto the durable lifecycle: resolutionSource and
one-shot consumeAskFallback stay process-local record facts, trusted
auto-review resolves through the durable CAS as a runtime resolver, and
lost races settle with the winner's operator source. Also: derive the
audience walker cap from the store cap, report APPROVAL_ALREADY_RESOLVED
for a resolve that loses the CAS race, document approval.get/resolve in
the protocol docs, and regenerate Swift models on the new base.

* fix(approvals): validate resolver kind

* docs(approvals): explain malformed verdict denial

* fix(protocol): regenerate approval models after rebase

* chore: leave changelog entry to release generation

* fix(gateway): preserve approval registration boundaries

* test(gateway): prove multi-device approval races

* test(gateway): keep approval order assertion stable

* docs: index operator approval architecture

* fix(protocol): bound approval declaration exports
2026-07-11 16:53:49 -07:00
qingminlong
8e216310b8 fix(parallel): stop MCP search when initialized ack fails (#104554)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-12 07:52:23 +08:00
Peter Steinberger
f929fd9865 feat(ui): chat background-tasks rail detects running work eagerly and shows live tool activity (#104776)
* feat(ui): detect background tasks eagerly and show live tool activity in the chat rail

Track tool-start counts and the last tool name per task run in the task
registry, persist them on task_runs, and expose them as additive optional
TaskSummary fields. The chat background-tasks rail now loads its snapshot
eagerly so the collapsed toggle badge detects running work immediately,
and rows show a live elapsed timer, tool-use count, and the tool
currently in use (run duration for finished rows).

Issue: #104775

* fix(ui): translate background-task activity strings and refresh generated protocol models

The singular tool-use label is literal ("1 tool use") because several
locales legitimately drop the count placeholder in singular forms, which
fail-closes the placeholder guard in the i18n sync. Locale bundles are
the authenticated ui:i18n:sync output (fallbacks=0); GatewayModels.swift
is protocol:gen:swift output for the new TaskSummary fields.

* chore(ui): refresh i18n raw-copy baseline after rebase
2026-07-11 16:51:48 -07:00
Peter Steinberger
447c9868c3 fix(cron): make condition watcher authoring reliable (#104787)
* fix(cron): teach agents condition watcher contract

* chore: defer cron note to release closeout
2026-07-11 16:50:39 -07:00
Peter Steinberger
fb08d3b631 ci: avoid retries in frozen UI tests (#104790) 2026-07-11 16:46:04 -07:00
Harjoth Khara
b2cd0e428d fix(workboard): preserve active claim error precedence (#104065)
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
2026-07-12 07:45:25 +08:00
Peter Steinberger
5c7e1341ab fix: fail crabbox lease-claim refusals (#104789) 2026-07-11 16:44:47 -07:00
NianJiu
d1f614c9e6 feat(android): add system share target (#104571)
* feat(android): receive system shares safely

Co-authored-by: NianJiuZst <3235467914@qq.com>

* fix(android): preserve share FIFO in composer

* fix(android): serialize cross-task share imports

* chore(android): sync share import i18n

* fix(android): retry shares in resumed composer

* chore(android): sync resumed share i18n

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:44:34 -07:00
moguangyu5-design
1a1d7aa45e fix(scripts): make clawlog default behavior match documented usage (#104059)
* fix(scripts): make clawlog default behavior match documented usage

Running scripts/clawlog.sh with no arguments was printing the usage help
instead of the documented default behavior (last 50 lines from the past 5
minutes). Remove the early no-args help branch so the defaults are used.

Also validate that options requiring a value (-n, -l, -c, -s, -o) actually
receive one, preventing an unbound-variable abort under set -u.

Fixes #104058

* fix(scripts): preserve dash-prefixed operands in clawlog and add regression tests

ClawSweeper review feedback on #104059 noted that the missing-value
guards introduced in the previous commit also rejected valid operands
beginning with a dash (e.g., search text '-failed' or an output file
named '-debug.log'). Narrow the guards so they only reject genuinely
missing operands, not dash-prefixed values.

Add focused regression coverage in test/scripts/clawlog.test.ts for:
- no-argument default behavior
- missing values for -n/-l/-c/-s/-o
- acceptance of dash-prefixed operands
- --help still printing usage

Related: #104058

* test(scripts): isolate clawlog command execution

* test(clawlog): use managed temp directories

---------

Co-authored-by: moguangyu5-design <moguangyu5-design@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-12 07:44:25 +08:00
ZOOWH
fb702e5cae fix(memory): harden embedding batch workflows (#103472)
* fix(memory): harden embedding batch workflows

Co-authored-by: 徐闻涵0668001344 <xu.wenhan1@xydigit.com>
Co-authored-by: ben.li <li.yang6@xydigit.com>

* test(memory): align batch redaction expectation

* test(memory): tighten batch dependency types

* refactor(google): clarify batch state control flow

* test(google): make batch stage fallback lint-safe

* build(plugin-sdk): refresh memory batch surface budget

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: ben.li <li.yang6@xydigit.com>
2026-07-11 16:43:58 -07:00
Peter Steinberger
847460600f feat(macos): redesign device-code sign-in (#104766)
* feat(macos): redesign device-code sign-in

* chore(i18n): refresh native string inventory

* fix(macos): localize device-code completion action

* fix(auth): preserve device-code fallback copy

* fix(xai): report browser launch accurately
2026-07-11 16:43:21 -07:00
Vincent Koc
0ea7b2a543 ci(perf): pin optimized Kova evaluator (#104772) 2026-07-12 07:42:35 +08:00
Vincent Koc
2adaf8b3b5 test(auth): type session update options 2026-07-12 07:40:35 +08:00
Vincent Koc
a426213f0a fix(auth): use canonical session accessor 2026-07-12 07:40:35 +08:00
Vincent Koc
7edc4f11b6 test(auth): align session update mocks 2026-07-12 07:40:35 +08:00
Vincent Koc
6bba7eaa0d test(telegram): match private login reply fixture 2026-07-12 07:40:35 +08:00
Vincent Koc
a7764d4366 fix(auth): preserve Codex login profile identity 2026-07-12 07:40:35 +08:00
Eva
030fa62b79 fix(telegram): preserve codex login profile identity 2026-07-12 07:40:35 +08:00
Peter Steinberger
ecd7d6c787 docs(windows): link latest Hub installers (#104774) 2026-07-11 16:39:32 -07:00
Peter Steinberger
0ae352a648 fix(test): reclassify sqlite flip proof harness (#104784) 2026-07-11 16:38:37 -07:00
Vyctor H. Brzezowski
ff6dc43b8b feat(ui): paginate sidebar sessions (#104617)
* feat(ui): paginate sidebar sessions

* fix(ui): collapse sidebar session pages

* fix(ui): space sidebar pagination from footer

* fix(ui): keep required sidebar sessions visible

* fix(ui): reuse translated sidebar actions

* fix(ui): hide ineffective sidebar collapse

* fix(ui): advance sidebar pagination past required rows
2026-07-11 20:35:10 -03:00
Leon Ma
1338b0d0bc fix(ai/gemini): coerce numeric/boolean enum values to strings (#104567)
* fix(ai/gemini): coerce numeric/boolean enum values to strings

Gemini Function Calling only accepts string enum values (see
https://ai.google.dev/gemini-api/docs/function-calling). Tool schemas
containing enum: [1,2,3] or enum: [true,false] were passed through
unchanged, causing Gemini to reject the entire request with 400
TYPE_STRING errors at 'function_declarations[X].parameters...enum[N]'.

Extend cleanSchemaForGeminiWithDefs to coerce enum and const values
via a shared helper (number/boolean/bigint -> string, drop null and
non-coercible entries, de-duplicate), and retype the containing schema
as 'string' since Gemini's validator only accepts enum on string-typed
schemas.

Adds seven vitest cases covering numeric/boolean enum, const, nested
schemas, and dedup/empty-after-coercion edge cases.

Fixes #104566

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ai/gemini): make enum retyping property-order independent

Address @clawsweeper review on #104567:

The initial patch set cleaned.type = 'string' inline while visiting the
'enum' or 'const' key. For schemas where 'type' appears AFTER 'enum' /
'const' in the input object — a valid ordering since JSON Schema property
order is not semantic — a later 'type' pass would overwrite the coercion
via the tail 'cleaned[key] = value' branch, and Gemini would still receive
an invalid declaration.

Move the type override out of the loop: track whether any enum/const was
coerced via a boolean flag, and force cleaned.type = 'string' after the
loop finishes. This makes the fix independent of input property order.

Also add curly braces around single-line ifs to satisfy check-lint
(eslint(curly)) which failed on the previous commit.

New reversed-order regression tests added.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ai): preserve Gemini enum schema types

---------

Co-authored-by: leonme <leonme@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:34:31 -07:00
Peter Steinberger
2776bda1ea fix(checks): cover newly added files in the changed-format lane (#104771) 2026-07-11 16:29:25 -07:00
Artur
cc419f8f47 fix(telegram): prevent unlisted API calls from stalling delivery (#104526)
* fix(telegram): bound unlisted API requests

* test(telegram): prove unlisted request aborts

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:28:36 -07:00
RickLin
f0676a7a79 fix(cron): delete successful on-exit deleteAfterRun jobs (#104550)
* fix(cron): delete successful on-exit deleteAfterRun jobs

deleteAfterRun finalization was restricted to at schedules, so a successful
on-exit job with deleteAfterRun=true was retained as a disabled job instead
of being removed (the gateway exit watcher durably disables on-exit jobs
BEFORE firing as replay protection, and no later transition deleted them).
Successful delete-after-run now covers every one-shot schedule kind; the
pre-fire disable ordering is unchanged. Regression covers deletion on ok,
retention without the flag, and retention on failure through the same force
path the exit watcher fires.

Fixes #104518

* test(cron): prove durable on-exit finalization

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:26:43 -07:00
Peter Steinberger
7778663322 test(telegram): preserve projection fixture identities (#104777) 2026-07-11 16:26:26 -07:00
Yuval Dinodia
7cd2e482d1 fix(agents): lease session worktrees during runs (#103263)
* fix(agents): lease managed worktrees across a run with an exclusive removal claim

A resumed session's managed worktree could be removed mid-run by idle GC, a
separate remover, or a manual worktrees.gc, destroying in-flight work; a session
whose worktree was already removed was admitted anyway and ran without its
checkout.

Runs now take a shared run-lease and removal takes an exclusive claim, both held
as rows in the shared state_leases table so admission and removal serialize
through one BEGIN IMMEDIATE transaction. Lease rows carry an opaque token plus
pid and process start time; dead or reused-pid owners are pruned inside the same
transaction. Removal claims before any cleanliness or snapshot work and rejects
while a live run lease exists; a run releases only its own token. The worktree id
is resolved from the session binding or by containment over both the run cwd and
workspace dir, so a workspace-only child still leases. The git worktree lock is
retained as a secondary raw-Git guard and refcounted per process so a parent and
a same-process child do not unlock each other.

* fix(agents): serialize worktree git lock cleanup

* test(agents): seed worktree races in sqlite

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:26:22 -07:00
Peter Steinberger
724e90c3c8 feat(ui): redesign /btw side chat into a floating multi-turn panel (#104749)
* feat(ui): redesign /btw side chat into floating multi-turn panel

The inline BTW side-result card becomes a floating Side chat panel pinned
to the thread column: multi-turn conversation with markdown answers, a
follow-up input that carries the previous side answer as context, and
header actions to clear (retires the pending run) or close (conversation
and pending run survive; an arriving answer reopens the panel).

* fix(ui): carry prior side question in follow-ups; display typed question structurally

Review follow-ups: the /btw follow-up command now embeds both the previous
side question and answer (answers alone lose the referents), and the panel
shows the user's typed question via structured state (pending record
correlated by runId) instead of parsing a marker out of user-controlled
command text.

* fix(ui): stop pending side runs on clear and keep rejected follow-up drafts

Trash now sends a targeted chat.abort for the pending /btw run (the retire
set still suppresses its late events if the abort races), the follow-up
input is hidden while disconnected like other slash sends, and a rejected
detached send restores the typed follow-up into the panel input.

* test(ui): attach side-chat panel test container for restore assertion

* fix(ui): block side-chat follow-ups while pending; notify rejection on pre-send exits

A follow-up while a question is pending would retire the in-flight run and
silently drop its answer, so the panel input is disabled until the answer
lands. The rejection callback now fires on every non-accepted path of the
guarded detached send (early exits, guard dedupe, rejected acks), not just
rejected acknowledgements.

* fix(ui): render side-chat questions verbatim

Pending and turn questions are already display-ready (chat-send strips
composer commands, the server echo carries no /btw prefix, panel follow-ups
pass structured text); re-extracting corrupted questions that begin with a
command token.
2026-07-11 16:22:58 -07:00
Peter Steinberger
8fea7f5011 fix(docs): reject translated heading loss 2026-07-11 19:20:40 -04:00
Peter Steinberger
453c049ac8 test(telegram): table-drive funnel-parity chunk boundaries for streamed final pagination (#104760)
The behavior fix landed on main in #104608 (fe3884a7ad): draft-stream final
pagination now routes markdownSource previews through markdownToTelegramChunks,
the same chunker the durable reply funnel uses (delivery.replies.ts
buildChunkTextResolver), and the overflow-pagination golden was re-recorded on
safe boundaries there. This adds the missing parity regression coverage:
table-driven cases for mid-word, mid-entity, mid-tag, and fenced code-block
page boundaries assert the streamed pages equal the durable chunker's HTML
chunks exactly, and that retained/current page snapshots carry the durable
plainText projection so an HTML-parse 400 degrades both funnels identically.

Formatting proof: Testbox tbx_01kx9n9q7rksfxgr5baj0esw57 check:changed format
lane passed on this diff (local worktree has no node_modules for the hook).
2026-07-11 16:12:48 -07:00
llagy007
c059d7d062 fix(browser): refresh target after guarded existing-session actions (#104094)
* fix(browser): refresh target after guarded existing-session actions

* test(browser): isolate guarded target cases

* test(browser): mock safe replacement URL resolution

* fix(browser): refresh target after guarded evaluate

* style(browser): format guarded action matrix

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:11:48 -07:00
Peter Steinberger
6852016b51 feat(ui): cron automation-ideas gallery and Create & run now in quick-create (#104761)
* feat(ui): cron automation-ideas gallery and Create & run now in quick-create

The cron page gains a curated automation-ideas gallery (six starter
automations that pre-fill the quick-create wizard with editable prompt,
name, schedule, and delivery presets); it stays expanded until the first
job exists, then collapses to a slim band. The quick-create wizard's
final step adds Create & run now, which chains cron.run (force) on the
job id returned by cron.add so a fresh automation shows output
immediately instead of waiting for its first scheduled tick.

Also hardens scripts/control-ui-i18n.ts against models wrapping JSON
replies in Markdown code fences.

Closes #104746

* chore(ui): regenerate locale bundles for cron suggestions strings

Generated via authenticated ui:i18n:sync (claude-sonnet-4-6) for the new
cron.suggestions/quickCreate keys; keeps the locale structural-alignment
gate green.

* fix(ui): deliver watchdog suggestion results via notify preset

Codex review: the night-watch idea promised alerts but pre-selected the
isolated preset, which maps to deliveryMode none, so results were never
delivered. Use notify and reword the prompt to an explicit one-line
verdict (renamed key so the locale pipeline retranslates).

* chore(ui): regenerate locale bundles for watchdog suggestion

* chore(ui): refresh raw-copy baseline after rebase
2026-07-11 16:10:00 -07:00
llagy007
4057d4b623 fix(browser): keep direct CDP version diagnostics (#104678) 2026-07-11 16:09:42 -07:00
NIO
c04c85b6f3 fix(mattermost): bound stalled inbound media header waits (#104575)
* fix(mattermost): bound stalled inbound media header waits

* test(mattermost): always close stalled media server

* test(mattermost): satisfy server cleanup lint

---------

Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 16:09:08 -07:00
Peter Steinberger
c3a8e60adc fix: doctor repairs Voice Call SQLite schema upgrades (#104752)
* fix(doctor): repair voice-call SQLite schema

* chore: keep release notes in PR body

* test(plugin-sdk): update public surface budget
2026-07-11 16:05:59 -07:00
Peter Steinberger
a4cd5625da fix(docs): localize visible MDX attributes 2026-07-11 19:03:01 -04:00
pick-cat
3d3796243f docs(gateway): add launchd supervisor loop troubleshooting (#89816)
* docs(gateway): add launchd supervisor loop troubleshooting

* docs(gateway): warn before disabling node LaunchAgent

* chore: trigger CI after rebase onto main

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(gateway): modernize launchd recovery runbook

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 15:59:12 -07:00
Peter Steinberger
a68b4f4083 fix(docs): preserve fenced examples in translations 2026-07-11 18:58:37 -04:00