Skip to content

Merge upstream v1.37.3 - #1

Merged
iazrael merged 27 commits into
mainfrom
feat/merge-upstream-v1.37.3
Sep 9, 2026
Merged

Merge upstream v1.37.3#1
iazrael merged 27 commits into
mainfrom
feat/merge-upstream-v1.37.3

Conversation

@iazrael

@iazrael iazrael commented Sep 8, 2026

Copy link
Copy Markdown
Owner

概要

合并上游 siteboon/claudecodeui v1.37.3 到本 fork 的 main 分支。

主要变更

规模

19 个提交,129 个文件,+4269 / -1107。

测试计划

  • 本地构建通过(npm run build
  • PM2 重启后核心功能正常(chat / workspace / 设置页)
  • Codex 会话可正常创建与对话

🤖 Generated with Claude Code

blackmammoth and others added 27 commits September 7, 2026 10:41
Reduce npm notice output and stream release logs so package listings do not obscure failures.

Print saved npm errors after failed releases to recover diagnostics lost to truncated output.

Quote release inputs so custom names retain spaces and literal characters.
…oon#1265)

The OpenCode provider authenticated any credentials file entry, so a
subscriber who connected OpenCode Go (`opencode-go` in auth.json) counted
as logged in - but the curated catalog had no `opencode-go/*` entries at
all. The connected-provider filter then found no Go options, fell back to
the full catalog, and left Go users staring at a list of models their CLI
rejects while every model they pay for was missing. See siteboon#840, which was
closed without a fix while the bug persists on main.

Add the 27 `opencode-go/<model-id>` entries `opencode models --verbose`
reports, with the reasoning-variant values from the same output wired as
effort metadata so the existing effort picker resolves them through
`--variant`. Labels follow the catalog's existing style; the CLI's
marketing suffixes ("(2x usage)", "(New)") are dropped. `DEFAULT` stays on
the Zen default - the filter already relocates it for Go-only installs.

The docs also list `minimax-m2.5`, but the CLI does not report it, so it
is deliberately left out; the catalog mirrors CLI output.
…on#1249)

Question/answer cards split a multi-select answer on ", " unconditionally,
so a single option whose own label contains ", " ("Yes, always") rendered as
two chips, neither matching an option, both marked (custom).

Match one exact option first and split only when no option matches, which
leaves genuine multi-select answers unchanged.
…eboon#1238)

* feat(chat): recall sent messages with arrow keys in the composer

ArrowUp in an empty composer recalls messages previously sent in the
open chat, newest first; ArrowDown walks forward and finally restores
the draft. History is kept per chat scope (the same session-or-project
key drafts use) in localStorage, capped per scope and across scopes,
and recorded on send, queue, and slash-command execution. The arrows
keep their normal meaning while editing text or navigating the
command/mention menus.

Signed-off-by: Matthew McClintock <matthew@mcclintock.net>

* fix(chat): walk a stable history snapshot during recall

An append from another tab mid-recall shifted what ArrowDown landed on
and could displace the draft restore. Navigation now snapshots the
entries when recall starts and walks that array until recall ends.

Signed-off-by: Matthew McClintock <matthew@mcclintock.net>

* fix(chat): record queued messages before the session-switch return

Switching sessions while a queued message's attachments uploaded hit
the early return after persistence, so the message dispatched later
without ever entering input history. Record it right after persistence,
under the session it was queued for.

Signed-off-by: Matthew McClintock <matthew@mcclintock.net>

---------

Signed-off-by: Matthew McClintock <matthew@mcclintock.net>
Co-authored-by: blackmammoth <118998054+blackmammoth@users.noreply.github.com>
* feat: collapsible branches in the model picker

The picker lists every model of every installed provider in one flat
run - an OpenCode installation alone contributes ninety - so reaching
the provider below means scrolling past all of them.

Each branch now collapses by its heading, with the model count beside
it, and the state is remembered in localStorage. A search opens every
branch for as long as it runs, because a hit inside a collapsed one
would be invisible. A collapsed branch keeps one row ("N hidden - click
to show"): cmdk drops a group whose items are all gone, and a heading
that is no longer rendered cannot be clicked open again.

The rendering moved into its own component rather than growing the
440-line empty state further.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: persist the collapsed branches from an effect

React may run a state updater more than once or discard its result, so a
`localStorage` write inside one is not tied to committed state. The
updater only computes the next set now; an effect on it does the write.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: clear the model search when the picker closes

The search box is controlled, and closing the dialog only flipped `dialogOpen`.
A query stayed behind and came back the next time the picker opened - with
every provider branch expanded, because searching expands them. That undoes
the collapsing this picker exists for.

Closing now clears the query, through one function the four call sites share.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…oon#1223)

Node's explicit module extensions fell through to the default arm of the
language switch, so a .mts or .cts file opened in the editor with no
highlighting at all. The existing `ext.includes('ts')` check already
picks TypeScript for the two new TS extensions and JavaScript for the
other two, so the four cases are all that is missing.

Co-authored-by: Malte Buttjer <claude@buttjer.net>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eboon#1220)

* fix(sessions): keep an archived session archived across a rescan

createSession resets isArchived on every re-index, without checking whether
anything was written. The full rescan runs on each project-list fetch and
selects transcripts by birthtime, so it re-indexes every session created since
the last fetch -- and each of those loses its archive flag. Archiving a session
started since the sidebar was last loaded therefore never sticks, which covers
every session a user archives right after finishing it.

Reset only when the caller reports newer activity: an explicit timestamp that
is newer than the stored one, or no timestamp at all, which the statement
already treats as "now" when it writes updated_at. julianday() parses both the
column-default and ISO forms that occur in the column.

* fix(sessions): treat an omitted timestamp as activity on the upsert path too

Review catch: the ON CONFLICT branch compared excluded.updated_at, which is
already COALESCE(?, CURRENT_TIMESTAMP), so a call that passes no timestamp was
compared as "now" -- and CURRENT_TIMESTAMP resolves to whole seconds, so it is
not newer than a row written in the same second. An archived app-created row,
indexed for the first time without timestamps, therefore stayed archived while
its own updated_at said it was live. Same reasoning as the UPDATE branch, same
`? IS NULL` arm.

Two tests, one per direction, both checked against the unpatched query first:
the omitted-timestamp one fails without this change, and the stale-transcript
one pins that an older transcript is still not activity.

---------

Co-authored-by: Malte Buttjer <claude@buttjer.net>
Co-authored-by: blackmammoth <118998054+blackmammoth@users.noreply.github.com>
…iteboon#1192)

* feat(i18n): connect remaining hardcoded UI strings to translations

Wire user-facing strings that never went through i18n so they respond to
the language setting. Adds a new git namespace for the source control
panel and extends the settings, auth, chat and common namespaces for
skills, browser use, onboarding, the command palette, MCP server views,
the setup form, the version upgrade modal, the folder browser and a few
smaller screens. Template strings become interpolated t() calls, plural
counts use i18next _one/_other forms, labels stored in constant files
are resolved through t() at render time, and commit dates in the history
view follow the active locale instead of hard-coded en-US.

English keys cover every namespace touched here; other languages fall
back to English through the existing fallbackLng setting until their
translations are added.

* fix(i18n): address CodeRabbit full-review findings

- AuthContext: use the auth namespace with stable errors.* keys for
  session/status/login/registration/network messages
- CommandResultModal: translate the modal footer hint and Close button
- MergeWorktreeModal: refresh the untouched default message when the
  locale changes (tracked per worktree)
- gitPanelUtils: FILE_STATUS_LABELS now returns git:status.* translation
  keys, matching FileChangeItem's t() rendering
- BrowserUseSettingsTab: always show localized operation-failure messages
  instead of raw API error text; render the translated runtime-required
  fallback instead of the server's English status message

* feat(browser-use): error-code contract for browserUse API

Server now replies with the structured AppError envelope
{ success:false, error:{ code, message, details } } on every
browser-use endpoint (status, settings get/save, runtime install,
sessions list/stop/delete). Stable BROWSER_USE_* codes let clients map
errors to localized copy instead of rendering raw English API text.

- readApiJson throws ApiRequestError carrying code/details/status and
  accepts both legacy string and structured error envelopes
- BrowserUseSettingsTab and BrowserUsePanel translate failures through
  code->i18n-key tables with localized per-operation fallbacks
- add en browserUse.installFailed key used by the panel mapping

* fix(api): preserve legacy top-level details in readApiJson

Legacy envelopes ({ success:false, error:'msg', details }) lost their
top-level details because payload was empty for string errors. Fall back
to data.details and cover structured, legacy and bare envelopes with
tests.

* chore: align package-lock.json and package.json with upstream main

The PR carries no package-lock.json changes (reviewer request): the lockfile
now matches origin/main exactly.

---------

Co-authored-by: blackmammoth <118998054+blackmammoth@users.noreply.github.com>
…eboon#1162)

The project and session delete dialogs in `SidebarModals.tsx` render five
translation keys that exist in none of the locale catalogs:

- deleteConfirmation.archiveProject
- deleteConfirmation.archiveSession
- deleteConfirmation.archiveSessionNotice
- deleteConfirmation.archivedSessionNotice
- deleteConfirmation.deleteSessionPermanently

Each call site passes an inline English default, so the keys never surface
as missing at runtime — they silently render English in all 11 languages.

The effect is worst where it matters most. Both dialogs offer a safe action
and a destructive one side by side. The destructive button uses
`deleteConfirmation.deleteAllData`, which *is* translated everywhere, while
the safe "Archive" button falls back to English. A non-English user reading
the project dialog sees a localized "Delete all data permanently" next to an
English "Archive project", which is precisely the choice the dialog exists
to make clear.

Adds the five keys to all 11 locales, following the pattern of siteboon#896.

Note: `removeFromSidebar`, `allConversationsDeleted` and `cannotUndo` are
translated in every catalog but no longer referenced by any component — they
appear to be the pre-refactor names of these same strings. Left untouched
here to keep this change additive; happy to remove them in a follow-up.
…oon#1274)

listPluginSkills read a plugin's commands/ or its skills/, whichever it
found first: the existence of a commands folder ended that plugin's turn,
so a plugin shipping both contributed only its commands. The commands
reader also takes .md only, so a plugin whose commands are in another
agent's format lost both halves at once -- nothing matched, and the
`continue` walked past the skills sitting beside them. The CLI offers both
halves for the same plugin, so those skills work in the terminal and are
absent from the slash menu, with nothing to say why.

Read both folders. The skills branch already skips a plugin with no
skills/, and the menu dedupes by command, so a name present in both places
is still listed once.

Fixes siteboon#1273

Signed-off-by: Liran Funaro <liran.funaro@gmail.com>
…on#1159)

`getProviderModels` resolves to a `ProviderModelsDefinition` - `OPTIONS`
and `DEFAULT` - but the agent route read `.models` off it, so both
catalog handles came back undefined. A codex or opencode run that named
no model then died on `codexModels.DEFAULT` with a TypeError before the
provider was ever started, and the route reported it as a 500.

The route's own test mock encoded the same imaginary wrapper, which is
why nothing caught it. The mock now returns the real shape, and a new
test drives the codex path with no model and asserts the catalog default
reaches the runtime.
…ion model (siteboon#1207)

* fix(claude): ignore <synthetic> model placeholder when resolving session model

Claude Code stamps locally-synthesized rows in session JSONL files (API-error
placeholders and similar) with model: "<synthetic>". The session-model scan in
readClaudeSessionModelFromJsonl walks the JSONL backwards and takes the first
event carrying a model field, so when the last row happens to be such a
placeholder, "<synthetic>" is surfaced as the session's active model. The UI
then adopts it, sends it back as the model for the next turn, and the SDK
issues a real API request with model "<synthetic>", which the upstream API
rejects with 404 model_not_found.

Treat angle-bracketed values as placeholders and skip them, falling back to
earlier events or the provider default.

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

* fix(claude): skip placeholder hits inside content-part scan

Move the placeholder filter into extractClaudeModelFromMessageContent so a
placeholder in an earlier content part no longer stops the scan before a
later part carrying the real model tag. Add regression tests.

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

* fix(claude): filter placeholders per candidate inside text extraction

A placeholder "set model to <synthetic>" stdout hit no longer shadows a
real <model> tag later in the same text. Filtering now lives at each
candidate site in extractClaudeModelFromTextContent, so the content-part
scan needs no extra checks. Regression tests cover both content shapes.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…wn (siteboon#1157)

A Conversations row was a link and a chevron: you could open the session
and nothing else. Renaming it, copying its provider session id, forking
it or archiving it meant finding it again under its project. This gives
that list the controls the Projects list already has, from one component
rather than a second copy of the markup.

Re-ported onto the layout siteboon#1206 landed, which moved
src/components/sidebar to src/modules/sidebar and flattened
view/subcomponents. Nothing of the previous version survived as a diff,
so this is that change rewritten against the new tree, with two
differences worth naming:

- fork is included. It did not exist when this was first written, and it
  is the third action the Conversations row was missing. The fork path
  reads only a session's id, provider and owning project, which is
  exactly what a recents row knows, so no session object is invented.
- PROVIDER_LABELS moves to utils/sidebarProjectFormatting, beside the
  other row formatting, rather than being exported from a component.

The shape is unchanged from the version that was reviewed down to ~116
net lines: the mechanism is extracted, not duplicated.

  SessionOptions        the options menu and the inline rename that
                        replaces it, with the outside-click dismissal
                        that belongs to it
  useProviderSessionIdCopy
                        the id behind "copy id": fetched when the menu
                        opens rather than per row, dropped when it
                        closes, sequenced so a late reply is discarded
  SessionRowActions     the nine members a row needs, named once;
                        SidebarProjectListProps composes it rather than
                        restating it, and the call site passes one prop

SidebarSessionItem is 208 lines lighter for giving those up; the
Conversations row spends 103 to gain all four actions and two states.

Callers keep only what is genuinely theirs. The Conversations row shows
the amber attention dot and the processing spinner, but not the green
"touched recently" dot — in a list ordered by recency it would be on
nearly every row and say nothing. A row whose project is unknown gets no
rename, since a rename is keyed by project; it is withheld rather than
guessed.

Also fixes a bug the second list exposed: nothing refetched the recents
feed after a rename or a delete, so the row you had just acted on kept
its old title or stayed in the list. Both now patch in place, which also
preserves the pages loaded past the first that refetching page zero
would discard.

Build, both typechecks and lint are clean; 380 client tests and 394
server tests pass, including six new ones asserting what the
Conversations row resolves for the shared controls.

Signed-off-by: Liran Funaro <liran.funaro@gmail.com>
Co-authored-by: blackmammoth <118998054+blackmammoth@users.noreply.github.com>
* feat(codex): expose GPT-6 Astra model

* chore(codex): upgrade runtime for GPT-6 Astra
* feat(codex): expose GPT-6 Astra model

* chore(codex): upgrade runtime for GPT-6 Astra

* fix(codex): replace unsupported default approval policy

Use on-request to prevent startup failures while preserving workspace sandboxing.

Update permission descriptions and test both new and resumed Codex sessions.

---------

Co-authored-by: Simos Mikelatos <simosmik@gmail.com>
Release 1.37.3

# Conflicts:
#	README.md
#	docs/README.md
#	docs/README.upstream.md
#	package-lock.json
#	package.json
#	server/modules/database/repositories/sessions.db.ts
#	server/modules/providers/list/codex/codex-runtime.provider.ts
#	src/modules/auth/AuthLoadingScreen.tsx
#	src/modules/chat/hooks/useChatComposerState.ts
#	src/modules/chat/modals/CommandResultModal.tsx
#	src/modules/chat/modals/ModelLibraryPanel.tsx
#	src/modules/chat/tools/BashCommandDisplay.tsx
#	src/modules/chat/transcript/ProviderSelectionEmptyState.tsx
#	src/modules/i18n/locales/de/settings.json
#	src/modules/i18n/locales/en/chat.json
#	src/modules/i18n/locales/en/settings.json
#	src/modules/i18n/locales/es/settings.json
#	src/modules/i18n/locales/fr/settings.json
#	src/modules/i18n/locales/it/settings.json
#	src/modules/i18n/locales/ja/settings.json
#	src/modules/i18n/locales/ko/settings.json
#	src/modules/i18n/locales/ru/settings.json
#	src/modules/i18n/locales/tr/settings.json
#	src/modules/i18n/locales/zh-CN/settings.json
#	src/modules/i18n/locales/zh-TW/settings.json
#	src/modules/plugins/PluginTabContent.tsx
#	src/modules/sidebar/SidebarSessionItem.tsx
#	src/modules/task-master/NextTaskBanner.tsx
- README.md: strip unresolved conflict markers (fork Chinese README kept;
  upstream body already mirrored in docs/README.upstream.md)
- NextTaskBanner: restore upstream i18n (t() for priority titles, start
  task, view details/all, complete/no-pending labels) lost to fork-side
  conflict resolution; drop now-unused setup-block leftovers
- SidebarSessionItem: remove dead PROVIDER_LABELS import
- PluginTabContent: merge duplicate useTranslation calls
…8n completions

- i18n: restore 148 missing settings keys in en/settings.json; complete tasks:banner and priorities translations in zh-CN/tasks.json; internationalize review button
- api: drop dead src/utils/api.js (449 lines) and extractResponseError; adopt readApiJson in PluginsContext with non-JSON fallback protection
- providers: consolidate resolveModelEffort across claude/codex/opencode; direct ws.send in codex runtime
- db: fix sessions upsert unarchiving for legacy NULL timestamps with integration tests
- chat: unify composer programmatic input updates via updateInput
- sidebar: eliminate duplicate PROVIDER_LABELS via getProviderDisplayName
- tests: relocate NextTaskBanner test under task-master/tests with I18nextProvider
- Guard keyboard offset by active editable element: when no input/textarea
  is focused, force --keyboard-height to 0 to eliminate initial load glitches
- Add focusin, visibilitychange, and pageshow listeners to clear stale offset
  when returning from background apps or restoring tabs
- Schedule two-stage focusout cleanup (50ms and 350ms) to ensure keyboard
  height drops to 0 and resets lingering window scroll
New sessions now read the provider's configured permission mode from the
user preference store instead of a separate permissionMode-last-* key,
removing the dual-channel drift between the settings page and composer.
Unsent choices stay in an in-memory draft map until the session id is
established, then persist under the session-scoped key.
…ge rows

Codex engine 0.153.4 (brought in by 5ad4b31) stopped writing legacy
event_msg/user_message rows; the typed prompt now arrives as an
event_msg/item_completed whose item is a UserMessage. The history
reader only matched the legacy shape, so every codex session opened
after the upgrade lost its user messages from history and exports
(assistant rows were unaffected: response_item parsing unchanged).

Parse the new UserMessage items with the same turn-anchoring discipline
as the legacy branch (first prompt of a turn anchors it, so edit/fork
cuts stay addressable). Injected context (AGENTS.md, plugin lists)
rides unnamed response_item user messages and stays excluded, same as
before. UserMessage items only appear as item_completed (never
item_started), so there is no double-count risk.

Regression test mirrors a real 0.153.4 rollout turn sequence; verified
against the production rollout file: 3 user prompts + 11 assistant
rows recovered, no injected-context leakage.
Claude's permissions tab now shows the same permission-mode radio cards
the other agents have, covering all five modes from the capability
catalog (default, auto, acceptEdits, bypassPermissions, plan). The mode
persists in the claudePermissions preference, which the composer already
reads as the new-session default, so claude chats no longer always start
in 'default'.

- ClaudePermissionsState gains a permissionMode field, normalized on
  load and written on save by the settings controller
- In-chat permission grants keep the stored mode instead of wiping it
- Mode card copy added to en/zh-CN; other locales fall back to en
- Drop the unused duplicate settings/types/types.ts
…mission descriptions

The composer permission menu reuses codex.descriptions.* copy for every
provider, and the default/acceptEdits texts described codex's sandbox
semantics ("commands run automatically inside the workspace sandbox"),
which is wrong for claude, whose default mode asks for approval. Rewrite
them in en/zh-CN/zh-TW as provider-neutral wording that stays true for
every provider. Also translate the auto mode label and description into
zh-CN, which had been left in English.
…r of the mode card

The checkbox and the bypassPermissions mode card converged on the same
runtime state (both ended up as the SDK's bypassPermissions), and the
checkbox's hard override silently defeated per-session mode choices —
only plan mode took precedence. The mode card is now the single entry
point; existing stored flags are deliberately left inert rather than
migrated.

- ClaudePermissionsState/ClaudeSettings drop skipPermissions; the legacy
  claude-settings hydrate no longer copies it
- chat.send no longer sends skipPermissions for claude, so a stale
  stored flag can never override the mode again (server untouched)
- toClaudePermissionMode moves to chatStorage so the storage reader and
  the settings controller share one normalization
- The in-app terminal's bypass toggle seeds from the stored default
  permission mode instead of the removed flag
- Cursor keeps its checkbox; its settings surface is unchanged
@iazrael
iazrael merged commit 7ccda82 into main Sep 9, 2026
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.