Commit Graph

1303 Commits

Author SHA1 Message Date
程序员阿江(Relakkes) fe10efb65f feat(desktop): add the OpenCode Go subscription provider
OpenCode Go binds the wire format to the URL path and translates nothing
between formats, so one provider record serves /chat/completions, /messages
and /responses depending on the model, each accepting a different credential
header. A record carries only one apiFormat, so presets can now declare
ordered per-model prefix rules (modelApiFormats) and the proxy resolves the
effective format from the request body. Only the exceptions are listed;
anything unmatched keeps the provider's format, which is the endpoint with the
broadest compatibility. A preset with rules is authoritative for apiFormat,
because a value written by a cc-switch import or the edit form would otherwise
silently disable every rule and point the CLI straight at the upstream.

The gateway also rejects any request without a stable per-conversation
x-opencode-session, so presets can declare upstreamHeaders with $SESSION_ID
and $VERSION placeholders. The id is the one the CLI already sends; it is
redacted in traces the same way the credential is, since it also names the
local transcript files.

Title generation builds its own upstream request and bypassed all of the
above, which left AI titles failing for every provider that needs local
request handling; it now goes through the same proxy the CLI uses.

Verified against the live gateway: glm/kimi reach /chat/completions,
minimax/qwen/union-alpha reach /messages, grok/gpt reach /responses, each
with the credential that endpoint accepts.
2026-09-17 22:00:02 +08:00
程序员阿江(Relakkes) 404422487b fix(desktop): keep AskUserQuestion answers, and lock the composer while it waits
The card renders from the transcript, but it can only be answered through the
live permission request. Two ways that mismatch bit users:

An unanswered question whose request is gone — the renderer was away when it
arrived, and the bounded 30-minute pending-permission window then reclaimed the
CLI — rendered as a fully editable form whose Submit and "chat about this"
buttons were silently dead, with nothing saying why. The card now reports the
question as expired and delivers the answers as an ordinary message instead,
which is the only channel left once nothing is waiting.

Answers also lived only in component state, so switching tabs (ContentRouter
mounts only the active tab) or scrolling the card out of the virtualized window
threw them away. They now live in a store slice keyed by session and toolUseId,
along with the two terminal states only this renderer knows about, so a remount
cannot resurrect an answerable form and deliver the same answer twice.

While a question is pending the composer no longer takes a message at all: the
model is blocked inside the tool call, so a queued message cannot reach it until
the question resolves, and reading it as "I already replied" is how questions
got abandoned. The round button stays Stop, which aborts the question and
unlocks the composer.

Tested: cd desktop && bunx vitest run (5889 passed; the one failure is the
pre-existing computerUseWorker case that needs adapters/node_modules)
Tested: cd desktop && bun run lint && bun run build
2026-09-17 21:37:51 +08:00
程序员阿江(Relakkes) c421907195 fix(desktop): count session tokens as new input+output, not cache hits
The context panel treated every cache read as spend and measured tok/s
against decode time without TTFT, so a 270k-window DeepSeek session
read as 40M tokens at 512 tok/s.
2026-09-17 17:15:20 +08:00
程序员阿江(Relakkes) 02d7228b44 release: v0.6.4 2026-09-17 12:08:14 +08:00
程序员阿江(Relakkes) a4bb7c5284 fix(desktop): stop cold session lists from scanning every JSONL
A building or empty local index used to fall through to a full transcript scan, so opening the sidebar or traces page could sit on GET /api/sessions until the 120s client timeout. Serve partial SQLite rows instead, look up trace titles by session id, and treat client disconnects as 499.
2026-09-17 02:08:53 +08:00
程序员阿江(Relakkes) 35736f3f3a fix(desktop): route teammate permission prompts to the lead session
Print-mode team leads were treating mailbox permission asks as ordinary chat, so desktop members hung forever with no approval UI. Forward those requests through the existing can_use_tool host prompt and label them with the teammate name.
2026-09-16 22:27:15 +08:00
程序员阿江(Relakkes) 88ea66f86e fix(desktop): stop the test-connection button from collapsing on long errors
The test-connection row is a flex row where the result text kept its default
`min-width: auto`, so a long upstream error forced every pixel of overflow onto
the button. Because `size="sm"` pins the height at h-6, the label wrapped and
the button rendered as a squashed two-line block.

Pin the button with `shrink-0 whitespace-nowrap` and let the error text take the
remaining width (`min-w-0 flex-1 break-words`) so long messages, including
unbreakable URLs, wrap inside the row.
2026-09-16 22:21:06 +08:00
程序员阿江(Relakkes) 0676c194e8 fix(desktop): restore window dragging on the sidebar and workspace headers
The sidebar shell's permanent compositing layer dropped macOS app-region
hits around the traffic lights. The workspace header slot also marked its
leftover titlebar space as no-drag, so the empty strip could not move the
window on either platform.
2026-09-15 20:50:06 +08:00
程序员阿江(Relakkes) 8a9e3800a7 fix(desktop): hide short-project fold control and shrink context meter
The fold button was shown whenever a project was marked expanded, including
short lists auto-expanded by history paging. Keep it only above the 6-session
threshold. Replace the composer percentage chip with a compact ring.
2026-09-15 20:21:49 +08:00
程序员阿江(Relakkes) 3e160f7e7a release: v0.6.3 2026-09-15 18:06:59 +08:00
程序员阿江(Relakkes) a4cd0306fa fix(desktop): keep ngrok start consent validation in sync with the shared version
PUBLIC_ACCESS_CONSENT_VERSION moved to 2 when remote provider management
landed, but the Electron IPC validator still accepted only the literal 1.
The renderer sends the constant, so publicAccessStart was rejected as an
invalid payload in the preload guard and again in the main-process handler,
before PublicAccessManager saw the request at all. That rejection bypasses
the manager's error classification, so the settings page could only show the
generic "operation failed" line while the state stayed disabled: public
access could not be enabled, and autoStart could never become eligible
because consent v2 was never persisted.

Validate against the shared constant instead of a copy of its value, and
stop pinning the stale literal in the tests that let this drift through:
capabilities.test.ts and electronHost.test.ts now send the constant and
reject the previous, next and non-numeric versions.
2026-09-15 17:54:59 +08:00
程序员阿江(Relakkes) 7eb466228c feat(settings): restore authoritative Agent Teams control in General 2026-09-15 17:24:29 +08:00
程序员阿江(Relakkes) 79f6e58b40 revert(desktop): drop the curated skill packages from the skills market
The skills tab mounted the connector catalog as a featured row above the
skill market, and that row was the only place the five curated packages
(frontend design, canvas design, generative art, webapp testing, MCP
builder) were offered. Stop mounting it, so the tab renders the market
alone and those packages have no entry point left in the UI.

The featured slot on Market and MarketHome stays in place, and the
catalog, bundle lock and installer under src/services/connectors are
untouched: restoring the row is a change to one branch of ExtensionMarket,
packages already installed keep working, and their mentions still resolve.
The ExtensionMarket test now asserts the tab does not mount the catalog
again.
2026-09-15 17:24:29 +08:00
程序员阿江(Relakkes) 16e58b92c0 feat(usage): show session token totals and generation speed in the context panel
The context panel could say what was in the window but nothing about what the
session had spent: no total token count, no cache hit rate, no generation speed.
The numbers were already on the wire — translateCliUsage picked four token
buckets out of the CLI's result message and dropped duration_ms, duration_api_ms
and num_turns with them — and nothing anywhere accumulated how long the model
spent emitting tokens rather than waiting on prefill.

Measure that span where it happens: a decode span opens at the first generated
delta and closes at message_stop, rides the stream_event up to QueryEngine, and
accumulates in cost-tracker beside totalAPIDuration, including the project
config restore path so it survives a CLI restart. Tokens/sec is output over that
span, never over wall clock, which would divide by tool execution time.

The totals needed fixing before they were worth showing. Claude Code persists one
JSONL line per content block of a reply and repeats the whole usage object on
every one, so summing lines overstated real transcripts by 2.2x — and
chooseRicherUsage prefers the larger of two snapshots, so the inspector actively
selected the inflated one. The transcript readers and the renderer's history
summary now deduplicate on usageAccounting's key, the rule stats.ts and the
activity index already use.

The panel polls a usageOnly inspection mode while it is open and stops when it
closes: one get_session_usage control, no skills-directory scan, no transcript
re-read, and no request stacked behind one that has not answered.
2026-09-15 17:24:29 +08:00
程序员阿江(Relakkes) f0b27a1198 fix(desktop): restore tab strip and hide empty turn change cards 2026-09-14 13:52:39 +08:00
程序员阿江(Relakkes) 0e46f7707d feat(desktop): align composer reference icons and drop dead slash commands
The @ and / menus each carried their own icon fallback — skills rendered as a
sparkle in one and a box in the other — so the same entry changed shape
depending on which menu opened it. Both now share one vocabulary, and the @
menu shows the source labels the slash menu already had.

Brand icons were resolved against the document root, which only worked while
`base` was `/`; the packaged renderer loads from file://, where
`/connectors/x.svg` points outside the bundle. Connector rows also opened the
detail view and started installing in the same click — installation now
happens only from the detail action.

The fallback command list no longer offers commands the headless CLI cannot
run (`clear`, `vim`, `commit`, `pr`, …): selecting one only produced
"Unknown skill". Of the 59 compiled commands, 16 support the headless path a
desktop session drives. An unprioritised menu also opened on the CLI's
bundled skills (`update-config`, `debug`, `batch`), so desktop-owned commands
now lead instead.
2026-09-14 12:10:16 +08:00
程序员阿江(Relakkes) f0a016d3cf fix(desktop): keep the window gutter on the panel paper
With the workspace panel open, the titlebar's right section was paper right
up to a 16px notch of sidebar grey welded to the window's top-right corner.

The paper was painted by the window header, but the drag gutter — and, on
Windows, the window controls — are its transparent siblings inside the same
frame, so whatever their parent showed came through. Closed, that is
invisible: the whole strip is the sidebar's ground and the gutter just
continues it. Open, it is a strip of trough next to a white panel.

The frame now owns the ground for everything above the panel, so the header,
the gutter and the native controls share one surface.
2026-09-14 11:56:37 +08:00
程序员阿江(Relakkes) dac0bf5477 feat(desktop): open the whole output-target card, not just its icon
The trailing icon was the only hit area on an assistant output card, so
opening a generated file meant aiming at a 32px target on the far right while
the file name and path sat inert. The row body is now a real button — icon,
name, badge and path all open the target, with a pointer cursor, a row hover
fill and a keyboard focus ring, and a label that names the file.

The open-with control is untouched and still opens its menu without also
opening the file. Text inside the row is no longer drag-selectable, which is
how browsers treat buttons; the path stays copyable from the open-with menu.
2026-09-14 11:37:57 +08:00
程序员阿江(Relakkes) 57eac9fbd9 feat(desktop): open every file-tree pick as its own tab
A single click in the file tree opened a VSCode-style preview tab, and the
next click replaced it — picking four files left one tab, which read as a
hard limit. Every pick now opens a permanent tab (repeat picks of one path
still dedup in the store), and the blank-placeholder takeover extends from
blank browser tabs to the empty Files launcher, so the first pick fills the
launcher tab instead of stranding it.
2026-09-14 10:44:22 +08:00
程序员阿江(Relakkes) 1170f2ddc7 fix(desktop): route CJK filenames instead of ignoring their clicks
Output cards and Open-with menus for files like README-拍摄大纲.md rendered
fine but died silently on click: card creation reads real paths from the turn
checkpoint, while the click route ran parseFilePathRef whose segment charset
was ASCII-only, classifying the href as `ignored`.

parseFilePathRef now matches on a Unicode-tolerant segment set — the whole
string is already delimited by the markdown span or href, so the prose-bleed
concern that keeps the prose scanner ASCII-only does not apply.
2026-09-14 10:44:22 +08:00
程序员阿江(Relakkes) 91059414b2 feat(desktop): add next-question navigation to ask-user-question prompts
Multi-question prompts get a Next button at the end of the action bar,
disabled until the question on screen is answered. Picking a single-select
option advances on its own, matching ChatGPT's ask_user_input card;
multi-select, free text and the last question stay put, and nothing
auto-submits.
2026-09-14 10:42:17 +08:00
程序员阿江(Relakkes) f7fcbc2d8a merge: integrate local main with skills and connectors 2026-09-14 10:14:37 +08:00
程序员阿江(Relakkes) c4a4178f18 feat(desktop): unify skills and connectors with managed installation and mentions 2026-09-14 10:14:15 +08:00
程序员阿江(Relakkes) f5dd740faa fix(network): allow long AI timeouts and default to 30 minutes 2026-09-14 09:12:59 +08:00
程序员阿江(Relakkes) 8d7b5ea56b feat(h5): persist pairing and add secure mobile settings
Add mobile provider and General settings while preserving desktop behavior.
Harden remote credential handling, ngrok session ownership and consent upgrades.
2026-09-14 00:38:55 +08:00
程序员阿江(Relakkes) 9fce822df3 feat: add secure ngrok remote access with device pairing 2026-09-13 23:28:30 +08:00
程序员阿江(Relakkes) 57feabde05 fix(desktop): align terminal themes and streamline workspace controls 2026-09-13 22:10:30 +08:00
程序员阿江(Relakkes) 69e8c014af fix(desktop): keep workspace browser visible beneath native menus
Use a native Electron menu with validated host actions and lifecycle cancellation. Preserve browser state, persistent annotations, and disabled page actions on blank tabs.
2026-09-13 22:01:39 +08:00
程序员阿江(Relakkes) b39945fcda fix(desktop): rename provider settings to model management across locales 2026-09-13 21:33:04 +08:00
程序员阿江(Relakkes) 6f0650c5cb fix(workspace): align resource panels and complete backend state flows
Unify workspace controls and resource tabs, refine browser, file and diff
interactions, and remove superseded panels. Preserve resource lifecycles,
refresh Git comparisons after external changes, and correlate terminal
startup events across IPC.
2026-09-13 16:02:58 +08:00
程序员阿江(Relakkes) dbc1e483ca feat(desktop): rebuild the workspace as a unified tab controller
Replaces the two-mode right-side panel (files ↔ browser) with one controller
that owns layout, navigation and resource lifetime for four content kinds:
files, browser pages, Git review and terminals. Follows the Codex desktop
reference captured in the workspace-refactor plan.

The old panel conflated three things that have different lifetimes: a UI tab,
the content it shows, and the process behind it. That is why switching modes
destroyed a live page, why a second link overwrote the first, and why the
toolbar button reported "show workspace" while the workspace was already open.
Splitting them is the whole change:

- workspaceStore    layout, docks, tab order, preview/pinned, focus
- workspaceContentStore / workspaceReviewStore   file and diff data
- host services     webContents and PTYs, keyed by resource id, never by tab id

Consequences that fall out of the split:

- Hiding the panel, switching tabs and switching tasks keep every page and PTY
  alive; only closing a tab releases them.
- A terminal moves between the side and bottom docks without restarting.
- Pages live in a shared persistent partition with native navigation history;
  popups become sibling tabs in the task that opened them.
- Review names both sides of every comparison and performs real index and
  working-tree writes, guarded by a snapshot token and a backup-first revert.

Also adds versioned workspace persistence with a forward migration: terminals
come back stopped and restartable, pages reload lazily, and nothing holding
content, cookies or handles is written to storage.

The previous implementation is no longer reachable but is left in place: the new
file tab does not yet reproduce workspace search, quick-open or the changed-file
view, so removing it now would lose those. Real three-platform Electron
acceptance (plan item A10) has not been performed; all evidence here is
deterministic tests, type checks and a packaging smoke.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 17:17:39 +08:00
程序员阿江(Relakkes) 85e7f3a20e release: v0.6.2 2026-09-11 18:49:24 +08:00
程序员阿江(Relakkes) 920a4a90c9 fix(plan-mode): approve plans with an explicit permission mode (#1254)
Approving a plan only sent the plan's addRules, so the CLI restored the mode
from prePlanMode and fell back to default when a session had nothing to
restore (it was launched in plan mode) — implementation then asked for
permission on every tool call. The desktop plan dialog now offers "Approve &
auto-accept edits" and "Approve & bypass permissions", mirroring the official
terminal dialog, and pins the chosen mode through a setMode permission update.

A mode delivered through a permission update also skipped the plan exit
transition: ExitPlanMode's cleanup only ran while the mode was still plan, so
the exit flags, the exit/re-entry reminders, the auto-mode teardown and the
rules stripped on entry were left half-applied. That path now reuses the same
transition the CLI runs for its own switches, and refuses bypassPermissions
when the session cannot use it.

Fixes #1254

Validation: new PermissionUpdate regressions plus the plan dialog tests;
src/utils/permissions + src/services/tools + src/cli 103 passed; check:policy
330 passed; desktop suite 330 files passed; compiled claude-sidecar smoke
passed; real-CLI A/B keeps the exit state identical with and without the pinned
mode.
2026-09-11 18:10:31 +08:00
程序员阿江(Relakkes) 446cc47846 fix(streaming): scale the overall stream cap with the user request timeout
The desktop injects CLAUDE_STREAM_MAX_DURATION_MS=600000, a wall-clock cap
that no incoming chunk resets. Slow local models (LM Studio / qwen3) and very
large generations legitimately keep streaming thinking_delta past ten minutes,
so the stream was killed mid-response even though it was alive and producing
content (#1307, #1275). Raising "请求超时" could not help: the cap was
hardcoded, and the first-token budget it was meant to raise was silently
bounded by that same 600s.

Derive the cap from the user's request timeout, never below the existing 600s
default so the #766 trickle protection is not tightened when a short first-byte
budget is configured. Push the derived value through the per-turn env hot
update as well — otherwise a session that is already running keeps its spawn
time 600s no matter what the user configures, which is exactly the retry path
the reports describe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-11 16:41:01 +08:00
程序员阿江(Relakkes) d5c02b43d9 fix(chat): scope file changes to their turns and collapse summaries (#1305)
Prevent carried-forward checkpoints from attributing old edits to later
chat-only turns while retaining legacy snapshot evidence. Default file
summaries to collapsed and preserve disclosure state and return focus.

Fixes #1305

(cherry picked from commit d03898d5203fa73d3c6c9ac66ca8c1c23ee36a59)
2026-09-11 16:16:42 +08:00
程序员阿江(Relakkes) a0a8fd4b45 fix(computer-use): load cursor resources from packaged apps
Resolve optional click animation assets from the running helper bundle.
Avoid SwiftPM Bundle.module's fatal fallback to a build-machine path so
visible clicks can return successfully and keep the daemon alive.

Keep procedural feedback when optional frames are absent or unreadable.
Add an input-free resource probe and run it against relocated release and
staged helper apps. Cover standard Resources copying and reject probes
that load from an external build directory.

Validation: 524 XCTest and 15 Swift Testing cases passed, including eight
new resource regressions. Build fixtures, compiled sidecar smoke, Electron
checks, local packaging and final relocated-package probes passed.
2026-09-11 15:09:50 +08:00
程序员阿江(Relakkes) 17f194df40 fix(proxy): preserve protocol completion and output budget contracts 2026-09-11 14:22:50 +08:00
程序员阿江(Relakkes) ecbd6b0e7f release: v0.6.1 2026-09-10 21:01:19 +08:00
程序员阿江(Relakkes) dcf20fe7dd merge: integrate QA-005 MCP disable fix 2026-09-10 18:10:55 +08:00
程序员阿江(Relakkes) a24931002d fix(mcp): enforce disabled state across active sessions 2026-09-10 18:09:43 +08:00
程序员阿江(Relakkes) e2478bb083 fix(desktop): preserve provider 1m flags in runtime selection 2026-09-10 17:56:12 +08:00
程序员阿江(Relakkes) 74f87a27e4 revert(session): remove protocol-based session restrictions
Revert efe5cae19 so existing sessions remain usable and models can be
switched without protocol admission checks. Retain the additive index
schema for already-upgraded caches and rebuild their summaries.
2026-09-10 14:00:50 +08:00
程序员阿江(Relakkes) 56c6a9aa8e feat(computer-use): align native app automation with Codex
Add a persistent isolated JavaScript worker for native app actions and batch
known operations without a model round trip between each input. Preserve
per-cell context, native errors, screenshot coordinates, and image types.

Align macOS gesture, key, inventory, capture, scroll, and clipboard behavior;
include a signed native receiver fixture and compiled sidecar regression tests.
Keep the Windows pixel route and fix cancellation with session-owned mouse
cleanup, lock revalidation, and portable signing-fixture tests.
2026-09-10 03:34:39 +08:00
程序员阿江(Relakkes) a2b3f59aaf feat(session): keep transcripts for 365 days by default
Transcripts now default to a 365-day retention and can be changed from
Settings > General, where a change previews how many files the new window
removes and requires confirmation before anything is deleted. Plans, file
history, debug logs, pastes and agent worktrees keep the previous 30-day
window instead of inheriting the transcript setting.

Also cleans subagent transcripts together with their parent session, and
restricts the bulk cleanup endpoint to the desktop process token so an H5
token cannot wipe every transcript in one request.
2026-09-09 23:24:38 +08:00
程序员阿江(Relakkes) efe5cae19e fix(session): lock model switching to the session API protocol 2026-09-09 23:05:33 +08:00
程序员阿江(Relakkes) 9c731d8ea5 feat(provider): add ApiSmart sponsorship and image generation support 2026-09-09 21:32:04 +08:00
程序员阿江(Relakkes) 8546f43ec3 fix(desktop): load older sessions while scrolling within projects 2026-09-08 16:05:48 +08:00
程序员阿江(Relakkes) 0d8a1a11fa fix(desktop): add bounded full session history browsing
Keep historical conversations reachable beyond the recent sidebar limit, hydrate only opened sessions, and restore old tabs through metadata-only lookups. Preserve newer metadata across history and restore races. Refs #1287.
2026-09-08 15:32:36 +08:00
程序员阿江(Relakkes) 405b3d6276 fix(chat): preserve hydrated history during stream retry cleanup 2026-09-08 13:32:43 +08:00
程序员阿江(Relakkes) 1aa4d3ef9c merge: integrate main into history race fix 2026-09-08 13:28:19 +08:00