Optional request extension fields had no size bound. A very large field
could exceed the runtime's string limit while the request was serialized,
which failed every attempt, including retries.
- Bound the extension payload carried by one request (8 MiB by default,
configurable) and send the remainder with later requests.
- If the combined request still cannot be serialized, send the base
request without extension fields and log which fields were omitted.
- Add regression tests and update the related references and decision note.
* feat(approval): localize prompts and allow consent for one turn
* test(approval): update cast baseline and Web PTC prompt pin
* fix(approval): cover PTC language and clarify turn consent
* fix(approval): group turn consent under allow menu
* fix(approval): keep turn consent in memory only
* test(snapshot): refresh owners for merged justification guidance
* fix(approval): drop duplicate allow-once from the allow menu
* refactor(approval): derive turn consent expiry from one check
Replace the turn approval bookkeeping (revocation state, incremental log
cursor, and turn-signal listeners) with one side-effect-free expiry check
over the log and lifetime flags. Events now only withdraw open
presentations early; the expiry check decides whether an answer or turn
consent still applies, including for Sessions without event publication.
* refactor(approval): drop one-turn consent and keep localization only
Remove the allowed-turn answer, the live turn-consent state, the allow
menu, and their tests, notes, and recorded Web scenario. The approval
service returns to the master request path; the request keeps the
localized displayReason for presentation, and the card resolves it in
the active UI language.
* test(snapshots): record the localized justification schema text in new scenarios
---------
Co-authored-by: Turtle <[email protected]>
Co-authored-by: Kaige-Gao <[email protected]>
The Web bundle now mounts `time-context` and `schedule` and leaves the
`ui-schedule` client row enabled, so the Automation tasks page, the Session
reminder catalog, and the `schedule_*` tools work without an overlay. Both
packages join the bundle's declared dependencies, and the example overlay is
deleted: re-inserting its rows would mount both Host plugins twice.
The fixed-rate floor drops from 300 to 60 seconds across the Host decoder,
the create and update validation, the model-visible tool schema, and the
client's per-unit floors.
Web recorded sessions and header sidecars are refreshed for the readings
`time-context` now appends; `normalizeWebSessionVolatiles` tokenizes their
sampled instant, browser zone, and elapsed duration so replay stays stable.
* feat(desktop): distinguish account quota alerts from API key notices
* docs: condense architecture introduction within existing budget
(cherry picked from commit 01aacaee4d48fd7f4d00acb5e23758084d3f224e)
* test(desktop): cover quota notice lifecycle and remove unknown casts
(cherry picked from commit 7fd09407c482d1f0e30f24ba0ebf23cd844ac9a1)
* chore(account-quota): align tests, catalogs, and bilingual pairs with the account plugin
* fix(ui-settings-account): pass the Platform retry label to the account top-up overlay
* fix(chat): preserve quota failures and global notices
* fix(account): keep active top-up pages across quota notices
* fix(account): share platform page ownership and release quota holds
* fix(account): defer notices behind the shared Platform page
The single native Platform view covers the document, so a quota failure that arrives while a Settings-opened page shows is deferred rather than retained: the newest failure still replaces an older notice and its Modal appears after the viewer returns if still current. Top up remains the retained case, releasing its hold and page request with the entry.
* test(account): align quota fixture with bonus actions
* fix(account): preserve quota notice and Platform return focus
* fix(account): preserve onboarding return reads and failed notices
* docs(account): clarify page opener return ownership
* test(account): drive bonus acknowledgement retry deterministically
---------
Co-authored-by: liyao <[email protected]>
Co-authored-by: lsdsjy <[email protected]>
Log tool changes as developer messages independently of model capability,
fold committed headers and updates into Session.toolHistory(), and let the
LLM runtime construct deferred and retained declarations for routes that
declare a toolUpdate mode. DeepSeek serializes updates as system-role
tool_addition/tool_removal blocks with defer_loading and the tool-changes
beta header. An unchanged restored tool continues the series and is
re-offered through its recorded blocks instead of rewriting declarations.
Replace the obsolete rejection tests with wire assertions and add coverage
for the session fold, loop emission, route projection, config validation,
and the beta header. Chat and Trajectory changes land separately.
* feat(i18n): key translation pairing records by heading section
Replace the whole-file blob hashes in *.i18n.yaml with one entry per
heading section that still holds language-specific content, keyed by the
English heading-slug path. Blocks identical across the pair (generated
table rows, code blocks, shared regions) stay out of the hash, so edits to
different sections and identical regenerations merge with Git's default
text merge, including on GitHub.
Remove the dsh-translation-pairing merge driver, its resolver command and
installer wiring; gen-translation-brief recovers last-confirmed text from
commit history. Migrate all active records, archive the superseded Agent
Note, and record the replayed conflict reduction in the new one.
* test(i18n): refresh translation prompt snapshot for the terminology note
* feat(i18n): exclude only code blocks and generated regions from pairing records
Replace byte-equality masking with an explicit rule: a record hash skips
fenced code blocks and BEGIN/END GENERATED regions, the two kinds of
content the pairing gate already requires to be identical on both sides.
Move generated data into regions written into both pages:
gen-config-catalog renders both languages and the record with per-package
regions labelled by code identifiers; gen-module-graph keeps its table in a
region; gen-doc-graphs splices the event matrix regions into the Chinese
page. Add a generic region splice beside the region grammar.
Review caught two real gaps in the previous commit.
The rename dialog's no-op check compared the localized display title, so
on an automatically titled Workspace the seeded prefill equalled it and
Rename stayed disabled — the user had no direct way to pin the visible
name. The dialog now seeds the draft with the label on screen while the
stored title decides whether confirming saves, and the duplicate check
excludes the target by Workspace identity instead of by title, since the
seeded label equals the row's own displayed title.
The registry took the automatic title from the canonical path, so a
symlink at the first-use path titled the Workspace after its target and
the default lost its localized label. The title now comes from the
requested directory's final segment; the path stays canonical.
Also: drop a rotting file count and the stale localized-path wording from
the Agent Notes, record the two cwd-derived Session label fallbacks as
known consequences, trim the WorkspaceBrowser memo comment to the
non-obvious part, and remove the deleted request type from the
hand-maintained catalog LINK_MAP.
The first-use directory name and its stored title came from the Client's
startup language, so one product decision carried two incompatible
requirements: a path addressed by shell commands, tool arguments, @path
references, and session logs, and a label a reader reads.
The controller now owns the fixed `default-workspace` directory name and
the registry titles the Workspace after that same segment, so
`workspace.initializeDefault` takes no request at all —
WorkspaceInitializeDefaultRequest and its name validation are gone, and
the registry resolver returns a path instead of a path and title. No
language reaches the Host.
Browser consumers label a Workspace through the new
`workspaceDisplayTitle`: a title still equal to the automatic one reads
as the localized default name from the `common` namespace, every other
title reads verbatim. The Workspace browser resolves it once at the store
read, so rows, hover card, search meta, and the rename and delete dialogs
inherit it; the hero picker and the Conversation chip resolve it too.
Confirming a rename stores the text on screen, which is what stops a row
following the language.
* fix(plugin-manager): bound pnpm runs so a silent child cannot hold the profile lock
A pnpm child that prints its completion line and then never exits holds the
profile write lock until the host is restarted, so every other session and host
on that profile fails after its own lock timeout (issue #4981). execa resolves
its promise only once the piped stdio has ended, so a run now completes on the
process's own exit and drains the pipes under a bound instead of awaiting their
end.
A service run whose captured output stays silent for idleTimeoutMs is
terminated, reports timedOut alongside its exit status, and is not retried on
the next registry; a run with inherited descriptors captures nothing and is
left unbounded for its operator.
* test(plugin-manager): type the mocked child and await the drain descendants
The inherited-descriptor fixture no longer needs its legacy `as unknown` cast,
because the mocked child is typed for the fields the operations read; the
retired baseline entry is pruned (verify-no-unknown-casts).
The drain specs wait for the killed descendant to exit before removing its
directory: Windows refuses to delete a directory a live process still holds.
* test(plugin-manager): stop the drain descendant deterministically
The descendant that holds the pipes now ends on a stop file the teardown
writes, and on its own after a minute, so the fixture leaves no process behind
when a kill cannot be delivered. Windows stops it through taskkill, and the
spec waits for the exit before removing the directory it ran in.
* test(plugin-manager): run the drain descendant from its own script file
Windows cannot carry a nested `-e` script through another spawn argument: the
descendant failed to start there, so the fixture asserted on a process that was
already gone. The descendant now runs from a script file the fixture writes.
* test(plugin-manager): cover the bounded drain everywhere and stage the real descendant on POSIX
Windows does not stage a descendant that keeps the inherited pipes open past
the direct child's exit, so the real-process cases run on POSIX only. The
mocked child now models execa's own completion rule — its promise settles only
once both pipes end — which covers the bounded drain on every platform and
still fails without the raw-exit race.
* test(plugin-manager): cover the drain failure path without a child process
An output consumer failure that the bounded drain cuts short is staged with the
mocked child as well, so the per-file coverage gate also holds where the
real-process cases are skipped.
* fix(plugin-manager): terminate the whole tree of a stalled run
A lifecycle script is a child of pnpm, so signalling pnpm alone left an
approved postinstall writing into the profile after the operation reported a
timeout and released the lock. A stalled run now stops its process group (or
Windows process tree) and waits for it to disappear before the caller restores
the files. The cut tail's close is told apart from a reading failure by where it
happened, so a failure from before the cut still surfaces.
* docs(plugin-manager): regenerate the config catalog after the rebase
The English catalog gains `idleTimeoutMs` from the regenerator, and the Chinese
counterpart carries the same interface line so the pair stays consistent.
* fix(plugin-manager): terminate a run's tree through execa, keeping exit cleanup
Spawning a service run with `detached` made execa skip the handler that kills
the child when the manager exits, so a crashed manager left the pnpm tree
running in the profile. `killDescendants` spawns the run as its own process
group while keeping that cleanup, and it addresses the tree on every kill path,
including cancellation. The module now only decides the probe target and waits
for quiescence.