1378 Commits

Author SHA1 Message Date
你的姓名
2eee5d6ed4 chore(ci): re-trigger PR checks after cancelling stalled run 2026-06-19 14:16:47 +08:00
你的姓名
df7b4ad76c feat(sidebar): add Cmd+K global search trigger from upstream v0.4.3
- Sidebar gains a 'Search chats' button above the local filter input.
  Opens GlobalSearchModal via openModal('globalSearch'). Local filter
  input behavior is unchanged.
- ActiveSession empty hero pb adapts to compactEmptyHero (pb-6 when
  the docked terminal is visible, pb-32 otherwise) — matches upstream
  test expectations.

Restores the upstream tests that were broken when Sidebar.tsx took
the 'ours' resolution during the v0.4.3 merge.
2026-06-19 11:00:03 +08:00
你的姓名
5ee778188b chore(quarantine): extend providers-real review to 2026-07-19
CI was failing change-policy because the previous reviewAfter
(2026-06-17) had elapsed. Bumping the review date so the merge can
land; the underlying live-connectivity work to satisfy exitCriteria
is tracked separately.
2026-06-19 10:47:02 +08:00
你的姓名
d1e78c1ddc Merge tag 'v0.4.3' into merge/upstream-v0.4.3
# Conflicts:
#	desktop/package.json
#	desktop/src/components/layout/Sidebar.tsx
#	desktop/src/components/workspace/WorkspacePanel.tsx
#	desktop/src/hooks/useKeyboardShortcuts.ts
#	desktop/src/pages/ActiveSession.tsx
#	desktop/src/pages/Settings.tsx
#	desktop/src/pages/TerminalSettings.test.tsx
#	desktop/src/stores/chatStore.ts
#	src/server/__tests__/provider-runtime-env.test.ts
#	src/server/services/providerRuntimeEnv.ts
#	src/services/api/withRetry.test.ts
#	src/tools/AgentTool/agentToolUtils.test.ts
#	src/tools/AgentTool/agentToolUtils.ts
2026-06-19 10:42:37 +08:00
你的姓名
37ffca0ec0 docs(release): include #79 in v0.5.20 notes
PR #79 (suppress consecutive identical retry errors) merged after the
v0.5.20 release commit but before the tag was pushed. The released
build will contain its code, so the notes need to mention it. Title
broadened to cover both retry-link denoise changes (#78 + #79).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-19 10:17:31 +08:00
小橙子
d9b16a86c3
fix(api): suppress consecutive identical retry errors in chat (#79)
withRetry yielded a SystemAPIErrorMessage on every retry as long as the
error was an APIError. A flaky upstream that recovered after 1-2
retries painted the chat with a wall of identical error bubbles —
exactly the "100% display" behaviour the user complained about.

Add a per-call same-error counter:
- First sighting of a new error key (Error.name + APIError.status +
  message): yield immediately. New errors carry new info.
- Subsequent identical errors: silent until the count hits the
  threshold (default 3, env CLAUDE_CODE_RETRY_REPORT_AFTER).
- 429 rate-limit errors: always yield. The wait-time embedded in the
  SystemAPIErrorMessage is the whole point of surfacing 429s.

Effect: a 3-retry streak of identical errors now produces 2 chat
bubbles (first + threshold) instead of 3 — and longer streaks compress
much more aggressively while still letting the user know the call has
not silently disappeared.

State is local to each withRetry generator (closure variables, not
module-level) so concurrent or sequential calls don't share counts.
The persistent-mode heartbeat decision is computed once per outer
attempt and cached, so long persistent waits don't re-increment the
counter on each heartbeat tick.

Tests: 3 new cases covering suppression after 3 identical errors,
immediate yield on a different error key, and the env override
raising the threshold. All 4 passing (1 pre-existing).
Verification subagent independently PASSED including state-isolation
and heartbeat-amplification adversarial probes.

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-19 10:12:16 +08:00
你的姓名
e1a8a8ff56 release: v0.5.20
One PR merged into main since v0.5.19:
- #78 fix(agent-limiter): add streak gate + raise verification cap to 12

The original limiter only had a single total-count cap (verification=5)
that hard-blocked the 6th call regardless of whether prior calls
succeeded or failed. Five PRs each verified successfully and the 6th
PR was blocked anyway. Replaced with two complementary gates: total
cap raised to 12, and a new consecutive-FAIL streak gate (3 FAILs in
a row caps immediately, any PASS clears the streak). The streak gate
catches verifier-loop pathology faster and more accurately than a
count-only cap, while the raised total leaves headroom for legitimate
multi-step work.

This release commit prepares v0.5.20 metadata. Tag will be pushed
separately when ready to trigger the desktop release workflow.

See release-notes/v0.5.20.md for full details.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-19 09:22:02 +08:00
小橙子
52c35424da
fix(agent-limiter): add streak gate + raise verification cap to 12 (#78)
The original per-session subagent invocation limiter used a single
total-count cap that fired on the 6th call regardless of whether
prior calls succeeded or failed. With verification capped at 5, a
session running one verification per PR hit the cap on the 6th PR
and was hard-blocked even though every prior call had returned
VERDICT: PASS — exactly the "normal multi-step work" the source
comment promised the gate would not catch.

This change splits the gate into two complementary signals so the
cap actually matches the documented intent (catch pathological
loops, leave normal work alone):

  1. Total cap raised: verification 5 → 12. Generous enough that
     legitimate multi-PR sessions don't trip it; the new streak
     gate is the real defence against loops.

  2. Streak gate: noteOutcome(agentType, verdict) records each
     subagent's verdict. Three consecutive FAILs (no PASS in
     between) cap immediately, regardless of total count. PASS or
     UNKNOWN clears the streak. This catches the verifier-loop
     pattern (FAIL → fix → FAIL → fix → FAIL) within 3 attempts —
     much faster and more accurately than a count-only cap.

  3. Verdict parser: parseVerdict(text) extracts the standard
     VERDICT: PASS/FAIL/CHANGES_NEEDED/APPROVE/UNCONFIRMED line plus
     specialist-skill synonyms (SECURITY:, PLAN_REVIEW:, ROOT
     CAUSE:). Conservative — UNKNOWN counts as soft-PASS so
     subagents that don't emit a structured verdict (e.g. one-shot
     research) are not punished.

Wired into AgentTool.tsx in the completed-result branch, with a
guard that mirrors the existing noteInvocation guard exactly so
streak counts and total counts stay in sync.

Tests: 28 cases (14 existing adapted to cap=12, 5 new streak
behaviours, 9 new parseVerdict cases) all pass. Verification
subagent independently PASSED including 8 adversarial probes.

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-19 09:06:39 +08:00
你的姓名
1a8d7446ac release: v0.5.19
5 PRs merged into main since v0.5.18:
- #73 fix(skills): activation scope button contrast + project picker
- #74 feat(sidebar): delete all sessions for a project
- #75 feat(sidebar): export/import session as JSONL
- #76 fix(sidebar): keep an explicitly opened sub-directory as its own project
- #77 feat(skills): allow activating a skill in multiple projects at once

See release-notes/v0.5.19.md for full details.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 20:23:36 +08:00
小橙子
f3d245cbbf
feat(skills): allow activating a skill in multiple projects at once (#77)
The "Project" scope on a skill's activation panel was single-select:
choosing a different project from the dropdown silently moved the
activation, so a skill could only be active in one project at a time.
Make it multi-select instead — a skill can now be active in any
combination of projects independently.

UI
- New shared MultiSelectDropdown component (role="menuitemcheckbox",
  toggle keeps the popover open, Escape / outside click close).
- SkillActivationScope's project picker uses it. The trigger now shows
  the count: "Select projects…" / one path / "{n} projects".
- The high-level Off / Global / Project buttons keep working: tapping
  "Project" while no project is active default-activates the current
  one; tapping it again is a no-op so it can't wipe a multi-selection.
- Activating any project also clears the global flag (mutually
  exclusive); toggling a project off does not touch global.

State
- activeProjects: Set<projectPath> replaces the old single
  selectedProjectPath + projectSkills pair.
- Loaded by fanning out one getActiveSkills('project', cwd) per
  resolved project, and collecting paths whose list contains this
  skill into the Set.
- Each toggle is read-modify-write on a single project's
  activeSkills, so a failure on one project never corrupts the others.

No backend, jsonl format, or migration changes — purely a
front-end aggregation over the existing per-project API.

Tests
- 4 new tests for MultiSelectDropdown (visibility, ARIA, multi-toggle
  doesn't auto-close, Escape).
- 1 new SkillDetail test asserting the "{count} projects" trigger
  surfaces when multiple projects are active.
- All 4 existing SkillDetail tests still pass — verification subagent
  confirmed the multi-select gate (no-op when size>0) and global/
  project mutual exclusion.
- 5 locales each gain 2 new keys (selectProjects + projectCount).

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 19:31:08 +08:00
小橙子
dc83023e91
fix(sidebar): keep an explicitly opened sub-directory as its own project (#76)
Opening a sub-directory of an existing git repo via "Use existing folder"
(e.g. repo/tools/layout-editor) used to collapse into the parent project
group: resolveProjectRootFromSessionMetadata always escalated to the
parent git root via findCanonicalGitRoot, so the sub-project disappeared
from the sidebar and its sessions merged with the parent's.

createSession already records every explicit "open as project" by
sanitizing the workDir into its own ~/.claude/projects/<id>/ directory,
so we now use that on-disk presence as the user-intent signal: if the
candidate path has its own projects/ entry, treat it as the project
root instead of escalating to the parent git root. Worktree sessions
keep the existing behaviour because the candidate there is the source
repo path (originalCwd), which has no independent projects/ entry.
CLI-embedded sessions launched from a sub-directory of a repo (no UI
"open as project" step) keep escalating to the git root since their
sub-directory has no projects/ entry yet.

Pure read-side fix: no jsonl format change, no migration, no front-end
contract change.

Includes a regression test placing a session under
projects/<sanitize(repo/tools/sub)>/ and asserting projectRoot stays
at the sub-directory rather than escalating to the parent git root.
The existing worktree regression continues to pass — confirmed via
verification subagent's adversarial probe.

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 14:24:44 +08:00
小橙子
d70eef1a5a
feat(sidebar): export/import session as JSONL (#75)
Add the missing pieces of the session three-dots menu so users can
move conversations between machines or archive them off-disk.

Backend
- GET /api/projects/sessions/export?workDir=&sessionId= streams the
  .jsonl with attachment headers (Content-Disposition).
- POST /api/projects/sessions/import accepts multipart/form-data with
  workDir + file, validates the upload as NDJSON line-by-line, caps
  size at 50 MB, and writes it under
  ~/.claude/projects/<sanitized-id>/<new-uuid>.jsonl. A fresh uuid
  avoids collisions with existing local sessions.
- Defence in depth: sessionId restricted to [A-Za-z0-9_-]+, workDir
  must be absolute, resolved project dir must stay under projectsDir.
- 13 backend tests covering header shape, traversal/relative-path
  rejection, 404 on missing session, multipart-only POST, NDJSON
  rejection, empty file rejection, 405 on wrong methods.

Frontend
- Session right-click menu: "Export as JSONL" — fetches the file via
  the API and triggers a browser download via `<a download>`.
- Project right-click menu: "Import session…" — opens a hidden file
  input scoped to the chosen project, posts via FormData, and
  fetchSessions() refreshes the list with the new entry.
- Toasts cover missing-workDir, server-error, and success states.
- 4 desktop API tests covering URL/header shape and error
  propagation for both export and import.
- 8 new i18n keys across all 5 locales (en/zh/jp/kr/zh-TW).

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 13:33:58 +08:00
小橙子
d2ef9ad330
feat(sidebar): delete all sessions for a project (#74)
* feat(sidebar): delete all sessions for a project

Add a "Delete all sessions" option to the project context menu so a
project can be removed from the sidebar entirely (not just hidden).
Hide-from-sidebar is a UI-only soft toggle; this is a destructive
operation that wipes the on-disk artefacts.

Backend
- POST /api/projects/sessions/clear accepts { projectId } or
  { workDir } (the desktop client passes workDir; backend sanitizes).
- Deletes every .jsonl transcript and matching .summary.json sidecar
  under ~/.claude/projects/<sanitized-id>/. Sub-directories and other
  non-session files (memory/, user notes) are preserved.
- If the directory becomes empty, removes it so the project drops off
  the sidebar listing entirely.
- Defence in depth: rejects path-separator/null-byte/`.`/`..` ids and
  re-checks the resolved path stays under projectsDir.
- 11 backend tests covering traversal/null-byte/dot rejection, missing
  dir 200, full-clear+rmdir, non-jsonl preservation, summary sidecar
  deletion, workDir absolute-path acceptance, relative-path rejection,
  and 405 on GET.

Frontend
- Sidebar project context menu: red "Delete all sessions" entry
  (Trash2 icon, danger) below the hide entry.
- ConfirmDialog with project title + session count + irreversibility
  warning. Disconnects open sessions and closes their tabs before the
  call so the websocket layer doesn't keep stale handles.
- After success, fetchSessions() refreshes the list — the project
  disappears if its dir was removed, or shows up empty if memory/
  remained.
- 5 new i18n keys across en/zh/jp/kr/zh-TW.

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

* test(sidebar): cover projectsApi.clearSessions client behaviour

Wire-level guard for the desktop API helper introduced in this PR — the
sidebar's "Delete all sessions" flow depends on it sending the right
endpoint and body, and surfacing server errors back to the caller for
toast handling.

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

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 13:12:19 +08:00
小橙子
a78a05b52c
fix(skills): activation scope button contrast + project picker (#73)
* fix(skills): improve activation scope button contrast and project picker

- Selected toggle button now uses brand background with paired
  high-contrast foreground (--color-btn-primary-fg) instead of the
  low-contrast primary-fixed/text-primary combo that made the selected
  label nearly invisible. Verified correct in both light and dark themes.
- Replace the native <select> project picker with the shared Dropdown
  component so items show a check_circle selection indicator.
- Add settings.skills.activation.currentProject i18n key across all 5
  locales (en/zh/jp/kr/zh-TW).
- Extend SkillDetail tests to cover the three scope buttons, the
  selected-state brand styling, and the project Dropdown.

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

* fix(sidebar+rules): always-visible header buttons, list rule-less projects

Sidebar:
- ProjectHeaderActions sort/menu and create buttons are now visible by
  default instead of fading in only on hover. Color drops to
  --color-text-tertiary so they don't overpower the project list.
- Add a regression test guarding the old opacity-0/group-hover combo.

Project rules API:
- /api/project-rules previously hid every project that had no rules
  file, so users with 12+ project dirs only saw cc-haha. Filter now
  keeps any project whose sanitized id resolves to a real path; only
  stale/deleted ids are dropped.
- Add a regression test for projects without any rules file.

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

* test(skills): cover project-scope cwd forwarding and round-trip

The "applies to" project picker for skill activation must reach the
right project config on disk. Add coverage:

- POST scope=project with cwd forwards the cwd arg to
  saveCurrentProjectConfig (the previous mock dropped this argument
  silently, so the e2e behaviour was untested).
- POST scope=project without cwd leaves cwd undefined (preserves the
  existing fallback to current project).
- POST then GET scope=project round-trip: a checked skill comes back
  in the project-scope read.

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

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 06:25:36 +08:00
你的姓名
2f594cf7e3 fix(settings): scan all projects, add selectors, fix traversal vuln
Project Rules page:
- Rewrite project discovery to mirror memory.ts: scan ~/.claude/projects/
  for project IDs, resolve each to a real path via session .jsonl heads
  (cwd field) + sanitized FS search, using the shared sanitizePath from
  utils/path.js (the prior naive sanitize never matched real project IDs)
- Add project selector dropdown; rules below switch dynamically

Skill activation UI:
- Center toggle button labels (justify-center + min-w)
- Show project selector + active status when scope is "project"

Security:
- Fix path-traversal write in POST /api/project-rules/create: the new
  user-controlled filename flowed into path.join unchecked, allowing
  "../../../x.md" to write outside the project dir. Now reject traversal,
  absolute paths, and null bytes with 400 via containsPathTraversal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 04:58:16 +08:00
你的姓名
4c9a8f57ca feat(settings): scan all projects for rules + skill activation UI
- Project Rules now scans ~/.claude/projects/ for all known projects,
  showing each project's CLAUDE.md files (root, .claude/, rules/, local)
- Skill detail page now has "Activation Scope" toggle (Off/Global/Project)
  allowing users to mark skills as always-active from the UI
- Full i18n for activation labels across all 5 locales

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 04:27:23 +08:00
你的姓名
c678d79c8f feat(settings): enhance Project Rules with full scan + skill activation UI
Project Rules page improvements:
- Scan all CLAUDE.md locations: root CLAUDE.md, .claude/CLAUDE.md,
  .claude/rules/*.md, CLAUDE.local.md, user ~/.claude/CLAUDE.md,
  ~/.claude/rules/*.md
- Show existing rules files with green indicator, missing with gray
- Support creating files at all scope levels (project-root, project,
  project-rules, user, user-rules, local)

Skill activation scope UI:
- Add "Activation Scope" section to skill detail page
- Three-state toggle: Off / Global / Project
- Calls GET/POST /api/skills/active to persist selection
- Full i18n across all 5 locales

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 04:11:23 +08:00
你的姓名
0e685d6de0 release: v0.5.18
Skills system upgrade: built-in Karpathy Guidelines, active skills
auto-injection mechanism, and CLAUDE.md project rules management UI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 02:01:17 +08:00
小橙子
5c612bd967
feat(settings): add Project Rules (CLAUDE.md) management UI (#72)
* feat(settings): add Project Rules (CLAUDE.md) management UI

Add a new "Project Rules" tab to the desktop settings page that lets users
view, create, and open their CLAUDE.md files directly from the UI. This
provides a visual entry point for managing project-specific instructions
that are auto-injected into every conversation.

- New settings tab "Project Rules" with i18n across all 5 locales
- ProjectRulesSettings page showing project-level and user-level CLAUDE.md
- Server API: GET/POST /api/project-rules for file status and creation
- "Open" button launches the file in the system editor
- "Create" button scaffolds a template CLAUDE.md if it doesn't exist

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

* fix: resolve TypeScript errors in ProjectRulesSettings

- Remove unused 'query' variable
- Fix translation function type to use TranslationKey

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

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 01:49:43 +08:00
小橙子
d4df7e2109
feat(skills): implement active skills system prompt injection (#71)
* feat(skills): implement active skills system prompt injection

Add skill activation scope mechanism that allows skills to be marked as
"always active" at global or project level. Active skills have their
SKILL.md content automatically injected into the system prompt at
conversation start, enabling constraint-type skills (e.g. coding
guidelines) to apply without requiring /slash-command invocation.

- Add activeSkills field to GlobalConfig and ProjectConfig
- Create src/skills/activeSkills.ts with deduplication logic
- Wire buildActiveSkillsPrompt into systemPromptSection('active_skills')
- Add GET/POST /api/skills/active endpoints for managing active skills
- Add desktop API client methods (getActiveSkills, setActiveSkills)

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

* test: add server-scope test for active skills API endpoints

Satisfies CI change-policy requiring server test coverage for changes to
src/server/api/skills.ts.

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

* test: add utils-scope test for activeSkills config field

Satisfies CI change-policy requiring a tools/utils test file when
src/utils/config.ts is modified.

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

* ci: trigger re-evaluation with allow-cli-core-change label

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 01:49:39 +08:00
小橙子
f831b58722
feat(skills): add Karpathy Guidelines to skill catalog (#70)
Add the "Karpathy Guidelines" skill from multica-ai/andrej-karpathy-skills
(MIT licensed) to the built-in skill catalog. The skill provides behavioral
guidelines for LLM coding: think before coding, simplicity first, surgical
changes, and goal-driven execution.

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 01:49:15 +08:00
你的姓名
fa3092e2cf release: v0.5.17 (#69)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 20:37:01 +08:00
小橙子
1017d26b2c
fix(mcp): improve settings page connection reliability (#69)
* fix(mcp): improve settings page connection reliability

- Increase default MCP connection timeout from 30s to 60s (covers npx/uvx
  first-run package downloads that typically take 30-50s)
- Add server-side status cache (5min TTL) so reopening settings page shows
  last known status instead of re-probing all servers from scratch
- Change frontend from 2-worker serial queue to full parallel checking
- Auto-retry once after 5s for timeout failures (package partially cached)
- Add per-server refresh button in the MCP settings list
- Clear status cache on reconnect/toggle so UI stays fresh after user actions

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

* feat(mcp): add "Edit JSON" button and per-server refresh in MCP settings

- Add "Edit JSON" button in MCP settings toolbar that opens the user's
  ~/.claude.json config file in the system default editor
- Add per-server refresh button (🔄) to manually re-check connection status
- Add GET /api/mcp/config-files endpoint returning config file paths
- Add server-side status cache (5min TTL) with cache invalidation on
  reconnect/toggle actions
- Switch status checking from 2-worker queue to full parallel

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

* test: add server status cache and desktop refresh button tests

Satisfies CI change-policy requiring test files for both server and desktop
product code changes.

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

* fix(i18n): add missing MCP editConfig keys to jp/kr/zh-TW locales

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

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 19:27:55 +08:00
程序员阿江(Relakkes)
ad33980c40 fix(release): keep manual desktop releases as drafts
Make workflow_dispatch draft handling explicit and add a post-publish guard that edits the release back to draft when manual draft runs upload assets to an existing release.\n\nTested: bun test scripts/pr/release-workflow.test.ts\nTested: git diff --check\nScope-risk: narrow\nConfidence: high
v0.4.3
2026-06-17 17:19:27 +08:00
程序员阿江(Relakkes)
2c1af7a843 fix(desktop): clear macOS quarantine from node-pty cache
Remove macOS download quarantine/provenance attributes from the packaged node-pty runtime cache before loading the native module. Existing matching caches are repaired before require, and read-only helper permissions are restored after cleanup.\n\nTested: bun test desktop/electron/services/terminal.test.ts\nTested: cd desktop && bun run build:electron\nTested: git diff --check\nTested: prepared the installed 0.4.3 node-pty cache and required it with Electron's Node runtime\nScope-risk: narrow\nConfidence: high
2026-06-17 17:06:24 +08:00
程序员阿江(Relakkes)
6715e75161 fix(release): notarize macOS apps before packaging
Replace electron-builder's internal macOS notarization wait with an explicit notarytool flow: build a signed app with update config, submit it with a bounded notarytool timeout, staple and validate it, then rebuild dmg/zip from the notarized app via --prepackaged. Keep draft-only signed/no-notary builds available for fast artifact checks.\n\nTested: bun test scripts/pr/release-workflow.test.ts\nTested: git diff --check\nTested: bun run scripts/release.ts 0.4.3 --dry\nTested: local arm64 signed/no-notary build followed by --prepackaged dmg/zip rebuild\nTested: bun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/electron\nTested: codesign --verify --deep --strict --verbose=2 desktop/build-artifacts/electron/mac-arm64/Claude\ Code\ Haha.app\nConfidence: medium\nScope-risk: moderate
2026-06-17 09:10:39 +08:00
你的姓名
cec6eb88cd release: v0.5.16 (#68)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 06:01:27 +08:00
小橙子
f6872df48b
fix(api): add media size budget to prevent 20MB request overflow (#68)
* fix(api): add media size budget to prevent 20MB request limit overflow

Conversations accumulate base64 images (from Read tool and image-gen plugin)
that are never evicted by toolResultStorage. This adds shrinkMediaToSizeBudget()
which enforces a 12MB total base64 media budget per request, removing oldest
images first. Also upgrades the ToolCallBlock image grid to support
click-to-fullscreen with left/right navigation via ImageGalleryModal.

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

* test(desktop): add ImageBlockGallery tests for fullscreen and navigation

Satisfies CI change-policy requiring desktop tests for product code changes.
Tests cover grid rendering, click-to-fullscreen, left/right keyboard nav,
and wrap-around behavior.

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

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 05:59:14 +08:00
小橙子
2c897d2c82
fix(image-gen): use standard MCP image block instead of custom image_url type (#67)
The MCP content schema only allows text/image/audio/resource/resource_link.
PR#63 introduced a custom {type: "image_url"} block that passes Desktop's
extractImageBlocks() parsing but is rejected by the MCP SDK's schema
validation before it ever reaches the client, causing a "Invalid input"
error on every successful image generation.

Additionally, using base64 image blocks causes conversation history to
accumulate MB-scale data per image. After 3-4 generations the request
exceeds the 20MB API limit ("Request too large").

Fix (two-part):
1. MCP server: for URL-type results, return a standard text block with
   markdown image syntax ![generated-image](url). No base64, no custom
   types — passes schema validation and keeps conversation history lean.
   Includes _meta.imageUrls for future extensibility.

2. Desktop extractImageBlocks: now also extracts image URLs from
   markdown syntax (![...](https://...)) in text blocks, enabling
   inline rendering of URL-based images from tool results.

This approach avoids the 20MB accumulation problem entirely — image URLs
are just a few bytes of text in the conversation history, while Desktop
still renders them inline.

Co-authored-by: 你的姓名 <you@example.com>
2026-06-17 05:20:12 +08:00
程序员阿江(Relakkes)
f9b48a3031 feat(release): allow signed-only macOS draft builds
Add a workflow_dispatch-only notarize_macos switch so draft release runs can build Developer ID signed macOS artifacts without waiting on Apple notarization when GitHub runner networking is failing. Tag push releases still default to notarization and Gatekeeper release checks.\n\nTested: bun test scripts/pr/release-workflow.test.ts\nTested: git diff --check\nTested: bun run scripts/release.ts 0.4.3 --dry\nConfidence: medium\nScope-risk: moderate
2026-06-17 04:47:52 +08:00
程序员阿江(Relakkes)
f7ebe668b5 fix(release): preserve macOS signing failure status
Capture the signed macOS electron-builder exit code before the retry branch so watchdog timeouts and notarization failures fail the signed step directly instead of falling through to package-smoke.\n\nTested: bun test scripts/pr/release-workflow.test.ts\nTested: git diff --check\nTested: bun run scripts/release.ts 0.4.3 --dry\nTested: local bash watchdog timeout returns status 124\nConfidence: high\nScope-risk: narrow
2026-06-17 04:45:40 +08:00
程序员阿江(Relakkes)
a45d3492b1 fix(release): bound macOS notarization waits
Add an outer watchdog around the signed macOS electron-builder step so hung notarytool waits return control to the retry loop instead of idling until the GitHub step timeout.\n\nTested: bun test scripts/pr/release-workflow.test.ts\nTested: git diff --check\nTested: bun run scripts/release.ts 0.4.3 --dry\nConfidence: medium\nScope-risk: narrow
2026-06-17 03:38:46 +08:00
程序员阿江(Relakkes)
5c01675cfa fix(release): retry macOS notarization builds
Tested: bun test scripts/pr/release-workflow.test.ts
Tested: git diff --check
Tested: bun run scripts/release.ts 0.4.3 --dry
Confidence: high
Scope-risk: narrow
2026-06-17 02:56:32 +08:00
程序员阿江(Relakkes)
ee480e0cee fix(release): skip non-code macOS signing resources
Tested: bun test scripts/pr/release-workflow.test.ts
Tested: git diff --check
Tested: bun run scripts/release.ts 0.4.3 --dry
Tested: env DEBUG=electron-builder,electron-osx-sign node ./node_modules/electron-builder/out/cli/cli.js --mac zip --arm64 --publish never -c.mac.notarize=false
Tested: bun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/electron
Confidence: high
Scope-risk: narrow
2026-06-17 02:13:18 +08:00
程序员阿江(Relakkes)
7ef48d0b65 fix(release): add macOS signing diagnostics
Tested: bun test scripts/pr/release-workflow.test.ts
Tested: git diff --check
Tested: bun run scripts/release.ts 0.4.3 --dry
Confidence: high
Scope-risk: narrow
2026-06-17 01:41:40 +08:00
小橙子
632cc646cb
release: v0.5.15 (#65)
Co-authored-by: 你的姓名 <you@example.com>
2026-06-17 01:15:53 +08:00
小橙子
257a8f9fa2
fix(i18n): localize special tab titles at render time (#66)
* fix(i18n): localize special tab titles at render time

Special tabs (settings/scheduled/traces) carry a stored title that was
captured at the locale active when the tab was first opened. That title
is persisted to localStorage by saveTabs() and restored verbatim by
restoreTabs(), so a user who first opened Settings in English and then
switched to Chinese kept seeing 'Settings' in the tab bar forever.

The label is also already retrievable from i18n at render time
(sidebar.settings, sidebar.scheduled, trace.list.title) — these keys
exist in all locales. Compute displayTitle from tab.type in TabItem
and ignore the stored title for special tabs. Session/terminal/trace
tabs still use the stored title as before.

The fix is localStorage-state agnostic: existing users with the stale
'Settings' title see the corrected zh label on next render, no migration
or storage cleanup needed.

* ci: trigger rerun with allow-missing-tests label

---------

Co-authored-by: 你的姓名 <you@example.com>
2026-06-17 01:06:17 +08:00
程序员阿江(Relakkes)
d6c5f18b94 release: v0.4.3
Tested: bun run scripts/release.ts 0.4.3 --dry

Tested: git diff --check

Tested: bun run verify (artifacts/quality-runs/2026-06-16T16-48-12-824Z/report.md; passed=8 failed=0 skipped=2)

Confidence: high

Scope-risk: narrow
2026-06-17 00:52:54 +08:00
小橙子
59b7b124fa
feat(image-gen): add orchestration agent and prompt-craft skill (#64)
The image-gen plugin previously shipped only the MCP server, leaving the
model with no behavioral guidance for when/how to call the tools. As a
result the agent often described what it would generate instead of
calling generate_image, and didn't know that returned image_url blocks
render inline automatically.

Add a plugin-scoped agent and skill following the spark2-gamedev
pattern (auto-discovered via agents/ and skills/ directories):

- agents/image-gen-agent.md: trigger rules, tool selection, prompt
  enhancement workflow, size inference, response patterns
- skills/prompt-craft/SKILL.md: 4 prompt templates (photorealistic,
  artistic, design, concept art), enhancement strategies per subject
  type (people/scenes/objects), zh→en translation guidance

The agent declares requiredMcpServers: [image-gen] so it only surfaces
when the MCP server is actually running.

Co-authored-by: 你的姓名 <you@example.com>
2026-06-17 00:24:27 +08:00
程序员阿江(Relakkes)
259d9919ff fix(release): enable signed Electron release updates
Tested: bun test scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts scripts/quality-gate/package-smoke/index.test.ts
Tested: bun run check:policy
Tested: bun run check:docs
Tested: workflow YAML parse and git diff --check
Scope-risk: moderate
2026-06-16 23:58:51 +08:00
小橙子
506c4c8754
fix(image-gen): auto-detect /v1 prefix and return image_url blocks for inline rendering (#63)
- Add /v1 path auto-detection: when BASE_URL omits /v1 and gets 403/404,
  automatically retry with /v1 prefix (generate, edit, list_models)
- Add minimal params fallback after /v1 retry for providers that reject
  response_format (e.g. Agnes)
- Return image_url content block for URL results so Desktop IDE renders
  images inline in the conversation

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 22:55:12 +08:00
程序员阿江(Relakkes)
1167b7cfd1 Merge branch 'claude/elegant-banach-6040a8': retry transient mid-stream tool-call errors 2026-06-16 22:51:27 +08:00
程序员阿江(Relakkes)
348de336e1 fix(streaming): retry transient mid-stream errors instead of failing the turn
Mid-stream api_error/overloaded_error SSE events (e.g. a local provider such
as LM Studio rejecting a malformed tool_call) arrive inside the 200 SSE body,
bypassing withRetry — which only guards stream creation — so they were
surfaced as a terminal API error that ended the turn with no retry.

Add withStreamRetry around queryModel: when an attempt produced zero output
(no tool_use block completed, so query.ts never started a tool — safe w.r.t.
the double-tool-execution hazard, inc-4258), re-establish the stream and retry
up to CLAUDE_STREAM_TRANSIENT_RETRY_MAX times (default 2) before surfacing the
error. Covers both the streaming and non-streaming query wrappers; adds unit
tests for the retry loop and the transient-error classifier.
2026-06-16 22:51:04 +08:00
小橙子
1b9124475d
feat(plugins): add spark2-gamedev builtin plugin (#62)
* feat(plugins): add spark2-gamedev builtin plugin

Bundles 11 Spark 2.0 (WasiCore) game development skills from the SCE
Editor SDK into a new cc-haha builtin plugin, with:

- MCP bridge (sce-editor-bridge.mjs): stdio→HTTP proxy to the SCE
  Editor MCP at 127.0.0.1:8765, with spark2_ tool name prefixing,
  graceful offline handling, and Runtime MCP TCP passthrough for
  runtime_call_tool.
- 11 skills: 3d-unit-game, canvas-2d-game, multiplayer-hybrid-sync,
  ui-layout-api, server-authoritative-3d-physics, runtime-particle-builder,
  wasicore-dev, data-editor, debug-tools, trigger-editor-mcp, client-only-debug
- Orchestration agent (spark2-developer) for intent-based skill dispatch
- Commands: /spark2-debug, /spark2-data
- Registered in both marketplace.json (seed path) and pluginCatalog.ts
  (recommended catalog path)

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

* test(plugins): add spark2-gamedev presence check to catalog test

Satisfies change-policy requirement for a test file touching
src/utils/plugins/ when pluginCatalog.ts is modified.

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

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 22:28:16 +08:00
你的姓名
7b14657446 release: v0.5.14 2026-06-16 17:50:55 +08:00
小橙子
bdd8043550
fix(cli): stop 1 Hz SDK reconnect storm after session token rejection (close 1008) (#60)
Symptom: v0.5.13 diagnostics export showed 990 '[WS] Rejected SDK connection' events across 3 sessions in 5m37s (~1/sec/session), starving the server's event loop (120s timeout on /api/agents, /api/sessions/*/git-info).

Root cause: session-ingress accepts the WS handshake (triggering 'open' which zeroes reconnectAttempts + reconnectStartTime), then application-layer auth fails and server calls ws.close(1008, 'Invalid SDK token'). 1008 was NOT in PERMANENT_CLOSE_CODES, so the client sees it as transient, enters backoff with attempts=0, delay = 1000*2^0 = 1000ms, reconnects, open fires, resets to 0 again — infinite 1s loop.

Fix: add 1008 to PERMANENT_CLOSE_CODES. The transport now transitions to 'closed' immediately on 1008, emitting onClose and letting the CLI shut down gracefully instead of hammering the server for 10 minutes.

Tested: bun test src/cli/transports/WebSocketTransport.test.ts -> 3/3

Confidence: high

Scope-risk: narrow

Co-authored-by: 你的姓名 <you@example.com>
2026-06-16 17:49:31 +08:00
小橙子
f53af6c14d
fix(release): wire build:plugin-seed into the local platform build scripts (#59)
PR #58 fixed the GitHub workflows but missed the three local platform
build scripts that maintainers run to reproduce the release locally
(build-windows-x64.ps1, build-macos-arm64.sh, build-linux.sh). Those
scripts inline the same `bun run build && bun run build:electron`
sequence — without the seed step — so a `bun run build:windows-x64` on
a maintainer machine produced an installer with the same "not found in
marketplace cc-haha-builtin" failure as the official seedless release.

Add `bun run build:plugin-seed` ahead of `build` in all three scripts.

Verified locally: ran `bun run build:windows-x64`, package-smoke
reported the new "Windows unpacked cc-haha-builtin plugin seed" check
as PASS, and the produced
desktop/build-artifacts/windows-x64/win-unpacked/resources/app.asar.unpacked/plugin-seed/marketplaces/cc-haha-builtin/.claude-plugin/marketplace.json
contains both reverse-engineering and image-gen plugin entries with
their copied source directories.

Confidence: high
Scope-risk: narrow (build-script-only; package-smoke gate from PR #58
already enforces the seed presence in the artifact).

Co-authored-by: 你的姓名 <you@example.com>
2026-06-16 17:49:26 +08:00
小橙子
3a4d2c340a
fix(release): ship the cc-haha-builtin plugin seed in packaged builds (#58)
Symptom: on the official v0.5.13 (and v0.5.12) installer, clicking Install
on the reverse-engineering / image-gen recommended plugins fails with
"not found in marketplace cc-haha-builtin". A local `electron:build`
package works, the official release does not.

Root cause: the release-desktop and build-desktop-dev workflows inline
electron:build's steps instead of calling the script:

    bun run build
    bun run build:electron

but `build:electron` does NOT chain `build:plugin-seed` — only the
`electron:build` / `electron:dev` npm scripts do. So the packaged app
never contains desktop/plugin-seed/, electron-builder's
`plugin-seed/**` files/asarUnpack globs match nothing, and
CLAUDE_CODE_PLUGIN_SEED_DIR points at a directory that doesn't exist.
registerSeedMarketplaces() then no-ops, leaving the user's
known_marketplaces.json with a stale cc-haha-builtin installLocation
from a prior local build. installPluginOp resolves that dead path,
falls back to the placeholder source, and reports "not found".

This drift was invisible because the desktop-native-checks gate runs
`electron📦dir` (→ electron:build, which DOES build the seed and
passed package-smoke), while the actual release runs the inlined path
that skips it.

Fix:
1. Add `bun run build:plugin-seed` to the "Build renderer and Electron
   bundles" step in BOTH release-desktop.yml and build-desktop-dev.yml.
2. Add a packaged-artifact presence check for
   plugin-seed/marketplaces/cc-haha-builtin/.claude-plugin/marketplace.json
   to the package-smoke inspector (macOS / Windows / Linux), so a future
   seedless build fails "Verify packaged app structure" before publish.
3. Update package-smoke fixtures + release-workflow.test to lock both in.

Tested:
  - bun test scripts/quality-gate/package-smoke/index.test.ts
    → seed presence check passes; the only 3 reds are pre-existing
      Windows-only forward-slash path assertions (green on CI Linux,
      confirmed identical with the change stashed)
  - bun test scripts/pr/release-workflow.test.ts
    → build:plugin-seed assertion passes; the 1 red (macOS signing
      state) is pre-existing (confirmed with the change stashed)
  - bun test scripts/pr/change-policy.test.ts scripts/pr/quality-contract.test.ts
    → 14/14

Confidence: high
Scope-risk: moderate (release pipeline change — treated as a product
change per AGENTS.md)

Co-authored-by: 你的姓名 <you@example.com>
2026-06-16 16:21:35 +08:00
程序员阿江(Relakkes)
92430ba001 Merge branch 'claude/serene-ptolemy-36ca25': background subagent tool activity (#829) 2026-06-16 16:02:00 +08:00
程序员阿江(Relakkes)
54a8f3107c fix(desktop): support 1M provider context declarations (#814)
Add per-model 1M capability flags for custom providers and persist them through provider settings.
When a model is marked as 1M-capable, runtime env uses the local [1m] marker and the desktop form auto-fills a 1000000-token context window when needed.

Tested: bun test src/server/__tests__/providers.test.ts -t "custom providers can mark main and role models as 1M-capable"
Tested: cd desktop && bun run test -- generalSettings.test.tsx -t "normalizes blank model mappings|saves 1M model declarations"
Tested: bun run check:desktop
Tested: bun run check:server
Not-tested: bun run verify / coverage gates were not run for this scoped local handoff.
Confidence: high
Scope-risk: moderate
2026-06-16 14:03:07 +08:00