Skip to content

✨ style(device): run remote CC on a configured device (cwd + context + online guard)#15343

Merged
arvinxx merged 39 commits into
canaryfrom
feat/remote-cc-device-binding
May 31, 2026
Merged

✨ style(device): run remote CC on a configured device (cwd + context + online guard)#15343
arvinxx merged 39 commits into
canaryfrom
feat/remote-cc-device-binding

Conversation

@arvinxx

@arvinxx arvinxx commented May 30, 2026

Copy link
Copy Markdown
Member

Summary

Makes a heterogeneous agent (claude-code / codex) actually usable when dispatched to an lh connect device (executionTarget='device') — it now runs in the user's configured directory, with a device-appropriate system context, and the send button is gated on the bound device being online. Part of LOBE-9579 (Step 3).

Changes

3a — server cwd passthrough

  • Resolve the run cwd in the hetero useDevice branch: topic-level workingDirectory override > the bound device's defaultCwd (read from DB via DeviceModel; the gateway only knows live connections), and pass it to dispatchAgentRun.

3b — device-specific systemContext, end to end

  • New buildRemoteDeviceHeteroContext — strips the cloud-sandbox boilerplate (ephemeral /workspace, pre-cloned repos, commit-or-lose warnings) that would mislead an agent on the user's own persistent machine; keeps agent static context + resumed conversation history + a minimal cwd note.
  • Thread systemContext through the contract: AgentRunRequestMessageGatewayHttpClient.dispatchAgentRundeviceProxy.dispatchAgentRun.
  • Desktop spawnLhHeteroExec injects systemContext as the first block of a content-block array on stdin (mirrors spawnHeteroSandbox); it previously wrote only the bare prompt, so any context was silently dropped.
  • The gateway relays unknown fields transparently (...runParams), so no gateway change is needed for this.

3c — optimistic device cwd persistence (data layer)

  • nextRecentCwds: pure most-recent-first / dedupe / cap-20 list builder (server stores recentCwds verbatim).
  • useUpdateDeviceCwd: optimistic device.updateDevice — patches the listDevices cache in onMutate, invalidates onSettled to re-sync truth.
  • (The device-targeted cwd picker that consumes this is a follow-up in this same branch.)

Device online guard

  • Extend the pre-send guard from remote-only agents (openclaw / hermes) to any hetero run targeting a device (claude-code / codex + executionTarget='device'): offline / unbound → send disabled + alert, instead of failing at dispatch.
  • Device execution no longer requires cloud credentials (it doesn't use the cloud sandbox).

Tests

  • buildRemoteDeviceHeteroContext (6) · nextRecentCwds (5) · GatewayConnectionCtr forwards cwd/systemContext (1). Existing device/desktop/pkg suites green. type-check clean.

Related

  • Gateway dispatch priority cli > desktop: lobehub-biz/device-gateway#11 (independent, not a hard dep).

🤖 Generated with Claude Code

arvinxx and others added 5 commits May 30, 2026 21:01
…e context

Make `claude-code`/`codex` dispatched to an `lh connect` device (executionTarget
='device') run in the user's configured directory with a device-appropriate
system context, instead of inheriting the cloud-sandbox setup.

3a — server cwd passthrough:
- resolve the run cwd in the useDevice branch: topic-level workingDirectory
  override > the bound device's `defaultCwd` (read from DB via DeviceModel; the
  gateway only knows live connections, not the user-owned cwd), and pass it to
  dispatchAgentRun.

3b — device-specific systemContext, end to end:
- new `buildRemoteDeviceHeteroContext` — strips the cloud-sandbox boilerplate
  (ephemeral /workspace, pre-cloned repos, commit-or-lose warnings) that would
  mislead an agent on the user's own persistent machine; keeps agent static
  context + resumed conversation history + a minimal cwd note.
- thread `systemContext` through the contract: AgentRunRequestMessage,
  GatewayHttpClient.dispatchAgentRun, deviceProxy.dispatchAgentRun.
- desktop: spawnLhHeteroExec now injects systemContext as the first text block
  of a content-block array on stdin (mirrors spawnHeteroSandbox); previously it
  wrote only the bare prompt, so any context was silently dropped.

The gateway relays unknown fields transparently (`...runParams`), so no gateway
change is needed.

Tests: buildRemoteDeviceHeteroContext unit (6) + GatewayConnectionCtr forwards
cwd/systemContext. type-check clean; existing device/desktop/pkg suites green.

Part of LOBE-9579 (Step 3a/3b). Old ephemeral boundDeviceId migration (3d) and
the web cwd picker (3c) are out of scope here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tCwds)

Foundation for the device-scoped cwd picker (executionTarget=device): persist a
working-directory pick to the bound device's registry record so the server's
hetero dispatch (which reads device.defaultCwd) stays in sync and the picker can
offer recent dirs.

- nextRecentCwds: pure most-recent-first / dedupe / cap-20 list builder (the
  server stores recentCwds verbatim, so the client owns this) — unit tested.
- useUpdateDeviceCwd: optimistic `device.updateDevice` — patches the listDevices
  cache in onMutate for instant UI, invalidates onSettled to re-sync truth (self-
  corrects a failed write without manual rollback).

Not yet wired into a picker — the target=device recentCwds-list + manual-input
picker mode that consumes this is the next step.

Part of LOBE-9579 (Step 3c, data layer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hetero

Extend the pre-send device guard from remote-only agents (openclaw / hermes) to
any hetero agent whose run dispatches to a device — i.e. claude-code / codex with
executionTarget='device'. If the bound device is offline (or none is bound), the
send button is disabled and a guard alert is shown, instead of letting the run
fail at dispatch time.

- new selector currentAgentExecutionTarget
- isDeviceExecution = remote-typed OR executionTarget==='device'; drives the
  guard's enabled flag, the blocked state, and the alert.
- device execution no longer requires cloud credentials (it doesn't use the
  cloud sandbox), so the cloud-not-configured gate now exempts it.

The guard hook already handled non-remote types (online check only, no platform
capability probe), so no hook change is needed.

Part of LOBE-9579 (Step 3, device online guard).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gle-layer surfaces

Remove the card-in-card look across builtin tool renders by dropping the outer
colorFillQuaternary container fill (the framework tool card already provides the
surface) and keeping at most one delineated inner box.

- claude-code AskUserQuestion: rebuilt as a flat Question / divider / Selected
  layout; add i18n keys (question/selected/reply/noAnswer)
- claude-code Skill, local-system WriteFile: flat container + single previewBox
- agent-management CreateAgent/GetAgentDetail: flat container, keep outlined
  systemRole block
- web-onboarding SaveUserQuestion: drop the redundant inner value box

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ders

Add §0.8 "stay single-layer — don't nest filled cards": the framework tool
card is already the surface, so the Render's outer wrapper carries no fill and
at most one filled box delineates real content. Cross-link from §2 Render rules
and the diagnostic table, and note the deliberate outlined-panel exception
(TodoWrite / Task).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label May 30, 2026
@vercel

vercel Bot commented May 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lobehub Ready Ready Preview, Comment May 31, 2026 7:07am

Request Review

@dosubot dosubot Bot added feature:agent Assistant/Agent configuration and behavior feature:tool Tool calling and function execution platform:desktop Desktop client labels May 30, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've reviewed this pull request using the Sourcery rules engine

The §0 shared rules had drifted into 8 one-line subsections (0.1–0.8). Fold the
five mechanical "every file looks like this" rules ('use client', memo +
displayName, BuiltinXProps generics, t('plugin'), store reads) into a single
annotated component skeleton (0.1), merge the two styling rules into 0.2, and
keep the single-layer surface rule as 0.3. Update the §0.8 cross-references in
§2 and the diagnostic table to §0.3.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a50d99c063

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +957 to +959
const deviceCwd =
appContext?.initialTopicMetadata?.workingDirectory ||
boundDevice?.defaultCwd ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve topic cwd when dispatching device runs

When continuing an existing topic that already has metadata.workingDirectory, this only checks appContext.initialTopicMetadata, which is only populated for newly-created topics (checked src/store/chat/slices/aiChat/actions/gateway.ts). As a result, device-targeted Claude/Codex turns in an existing project topic ignore the topic-level cwd and fall back to the device default, so the remote CLI can run in the wrong directory and lose resume/session continuity. Use the fetched topic?.metadata?.workingDirectory before falling back to boundDevice.defaultCwd.

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented May 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 28.41530% with 262 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.11%. Comparing base (3caa3ef) to head (a7eef36).

Additional details and impacted files
@@            Coverage Diff             @@
##           canary   #15343      +/-   ##
==========================================
- Coverage   71.14%   71.11%   -0.03%     
==========================================
  Files        3183     3187       +4     
  Lines      318341   318588     +247     
  Branches    34754    34767      +13     
==========================================
+ Hits       226479   226574      +95     
- Misses      91690    91842     +152     
  Partials      172      172              
Flag Coverage Δ
app 61.95% <28.41%> (-0.03%) ⬇️
database 92.39% <ø> (ø)
packages/agent-runtime 80.48% <ø> (ø)
packages/builtin-tool-lobe-agent 18.52% <ø> (ø)
packages/context-engine 84.17% <ø> (ø)
packages/conversation-flow 91.29% <ø> (ø)
packages/file-loaders 87.89% <ø> (ø)
packages/memory-user-memory 74.99% <ø> (ø)
packages/model-bank 99.99% <ø> (ø)
packages/model-runtime 84.70% <ø> (ø)
packages/prompts 72.68% <ø> (ø)
packages/python-interpreter 92.90% <ø> (ø)
packages/ssrf-safe-fetch 0.00% <ø> (ø)
packages/types 35.89% <ø> (ø)
packages/utils 88.47% <ø> (ø)
packages/web-crawler 88.08% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Store 68.44% <66.66%> (-0.01%) ⬇️
Services 54.64% <ø> (ø)
Server 72.35% <66.00%> (-0.01%) ⬇️
Libs 57.01% <ø> (ø)
Utils 81.44% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

arvinxx and others added 4 commits May 30, 2026 23:37
The single 770-line ui.md had grown unwieldy. Break it into references/ui/
with a README index and one file per topic: principles, shared-rules, the six
surfaces (inspector/render/placeholder/streaming/intervention/portal),
composition, and diagnostics. Convert in-doc §-number cross-refs to cross-file
links and repoint SKILL.md + tool-design.md at the new folder.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a hetero run is bound to a remote device, the device's filesystem isn't
browsable from here, so the local folder picker doesn't apply. Add
DeviceWorkingDirectory — a self-contained bar item (chip + popover) sourced from
the bound device's recentCwds plus a manual path input.

- Picking/typing a cwd pins it to the active topic (override) and persists it to
  the device via useUpdateDeviceCwd (optimistic defaultCwd + recentCwds), which
  is exactly what the server's device-dispatch branch reads back.
- Same per-cwd CC-session-reset confirm as the local picker.
- WorkingDirectoryBar routes to it when executionTarget==='device' (both web —
  replacing CloudRepoSwitcher — and desktop, replacing the local picker +
  GitStatus); local/sandbox paths are unchanged.
- Reuses existing i18n keys (recent / noRecent / placeholder).

Completes LOBE-9579 Step 3c. type-check clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ToolResultCard was the card-in-card shared component (colorFillQuaternary
wrapper around a colorBgContainer box) behind CC Read/Grep/Glob/Write/WebSearch/
WebFetch. Flatten it to single-layer (flat wrapper, one colorFillTertiary
content box) so all consumers stop stacking fills inside the framework tool card.

CC Read header showed the filename strong-label and then dumped the full
absolute path whose tail repeated the same basename, end-truncated so the
meaningful suffix was hidden. Show the directory only (filename stays the
strong label), and drop the conflicting word-break so the dir ellipsizes on one
line.

Note ToolResultCard in the skill as the canonical single-layer header+content
card to reuse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…button

Settings → Devices page polish:
- Badge the row for the machine you're on ("This device"), resolved from the
  desktop gateway's own deviceId (web has no current device → no badge).
- For the current device, the edit modal's Default working directory gains a
  native folder picker (electronSystemService.selectFolder) next to the manual
  input — you can't browse a remote device's filesystem, only your own.
- Edit modal footer now uses real Button components (Cancel + primary Save)
  instead of the base-ui Modal's default okText, which rendered with the wrong
  (non-primary) color.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels May 30, 2026
- "This device" badge uses the default neutral tag instead of success green.
- Show each live connection's channel as a small tag (desktop / cli) so a
  multi-channel device's connections are individually legible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
arvinxx and others added 6 commits May 31, 2026 00:11
The render gallery stacked all of a toolset's API previews in one scroll column
(67 for Claude Code), making any specific render slow to find. Add a middle
column listing the toolset's apiNames: clicking scrolls the matching preview
card into view (landing below the sticky lifecycle bar via scroll-margin), and
an rAF-throttled scrollspy highlights the API the reader is on and keeps that
item visible in the list. A leading dot marks APIs that ship a Render. The
content area now owns its own scroll so the list stays pinned.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The jump-list was a wall of identical `mcp__claude_ai_Linear__…` truncations and
the active item barely differed from hover. Show just the trailing action for
mcp__ tools (full id in a title tooltip + the preview card header), render names
in monospace, and give the active item a primary left-accent so it reads as
selected. Clicking now pins a `#api-<name>` hash (deep-linkable / shareable) and
loading a hashed URL jumps straight to that card below the sticky bar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The gallery only previewed each API in isolation. Add a View tab (By API /
Aggregate): Aggregate stitches every render-bearing API into one compact
content + tool message flow, so renders can be judged in conversational context
across any lifecycle mode. Inspector-only MCP tools are dropped to keep the
thread about the renders, and the API jump-list column hides in this view.

Extract the Inspector/Body surface rendering out of ToolPreview into shared
ToolInspectorSlot / ToolBodySlot (toolSurfaces.tsx) so both tabs derive props
identically and never drift. View choice persists to localStorage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The earlier "shorten mcp names" change solved the wrong problem and hid the
`mcp__` prefix, so MCP tools no longer read as MCP. The actual complaint was row
height. Restore the full identifier and instead middle-elide it
(`mcp__claude_ai_Li…get_diff`) so both the muted `mcp` namespace and the
distinguishing trailing action stay visible; full id remains in the title
tooltip. Drop row height to a fixed dense 22px (flex-shrink:0 so it scrolls
instead of squishing) to fit far more APIs per screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion renderer

The hand-rolled MessageList only approximated the chat. Replace it with the
actual shipping renderer: seed a `ConversationProvider` (skipFetch) with fixture
`assistantGroup` messages and map each render-bearing API to a real tool
payload, then render the real `MessageItem` for each. Tool state is driven
purely by the message shape — `result` → success, `result.error` → error,
`intervention.pending` → intervention, unterminated `arguments` JSON →
streaming — so the preview is byte-for-byte what users see in chat. Skips the
virtualized `ChatList` (and its data fetches) by mapping `MessageItem` directly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clicking a device row now opens a right-side detail drawer instead of a small
edit modal:
- Connections: render every live connection from the `channels` array, each
  with its channel tag (desktop / cli) + connected-since.
- Name + default working directory (native folder browse on the current
  device); saving a default cwd also seeds the recent list.
- Recent directories: list `recentCwds`, click to reuse, × to remove — this is
  where you can see and manage the recent list (previously not surfaced).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
arvinxx and others added 7 commits May 31, 2026 01:26
Per feedback — mirror the memory-preferences master-detail pattern:
- No device is selected by default; the right detail panel only renders once a
  row is clicked (clicking the selected row again closes it). Panel has its own
  close (×).
- List flexes to fill when nothing is selected; the detail appears as a right
  column on selection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…roll

The page root used height:100%, which only resolves when an ancestor route
provides a bounded height — under mounts that don't, the whole page grew to
content height and the API list never scrolled internally. Bind the root to
100dvh directly and add min-height:0 to the flex chain (main + the API list)
so the scroll container engages regardless of how the route is mounted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both APIs had no fixture, so the gallery fell back to schema-sampled args with no
content and the renders drew empty (just the icon). Add fixtures with realistic
args + content: WebFetch (url + prompt + markdown answer), WebSearch (query +
allowed_domains + results), plus their apiList descriptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the device.online-based synthetic single-channel fallback — the connection
rows now come purely from the device.channels[] array (one row per live
connection), with offline = empty array.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 429 "Server is temporarily limiting requests (not your usage limit)"
was classified as a user rate_limit, rendering the misleading "Claude
Code usage limit reached" reset-time guide. Key the rate_limit vs
overloaded decision on the structured rate_limit_event reset window
(resetsAt / rateLimitType) instead of the HTTP status, so 429/529 with
no quota signal fall through to the overloaded (retry) UX.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
22px rows at 12px overcorrected into a cramped sidebar. Relax to 30px rows,
13px label, a small inter-row gap, and a touch more vertical padding so the
jump-list reads comfortably.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The connection rows had a 30px inline-start padding that pushed them right of
the cwd line; align them with the rest of the device info.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@arvinxx arvinxx changed the title ✨ feat(device): run remote CC on a configured device (cwd + context + online guard) ✨ style(device): run remote CC on a configured device (cwd + context + online guard) May 30, 2026
arvinxx and others added 2 commits May 31, 2026 01:58
The online/offline status now sits as a dot next to the device name + badges
(with the connected / last-active time as a tooltip), instead of a separate
third line. Per-channel connection detail still lives in the detail panel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The seeded preview conversation resolved its avatar/name through an agentId that
wasn't in the agent store, so every turn fell back to the unresolved-agent
"Unnamed Assistant" / UN avatar. Seed agentMap with a Lobe AI meta
(DEFAULT_INBOX_AVATAR + title) for the devtools agentId, shared via
DEVTOOLS_AGENT_ID / DEVTOOLS_AGENT_META so MessageList's context and the store
seed stay in sync. Restored on unmount.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
arvinxx and others added 6 commits May 31, 2026 12:02
The Aggregate preview passes `result.state` to inspectors, matching the
real runtime, but the canonical `result` type omitted `state` — failing
type-check. Add `state?: any` so devtools and runtime agree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Prefer the topic's own `metadata.workingDirectory` over the device
  default when dispatching, so an existing topic keeps its pinned cwd
- Add `heteroTracingEnabled` store flag to trace CLI raw streams in
  packaged builds (Help menu checkbox)
- Reorder the connection status dot ahead of badges in DeviceItem

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s in production

Packaged builds previously never wrote hetero-agent (CC / Codex) CLI traces,
so production issues couldn't be captured. Add a persisted `heteroTracingEnabled`
toggle in the Help menu (all 3 platforms) plus an "Open HeteroAgent Directory"
entry. Dev still always traces to `cwd/.heerogeneous-tracing`; packaged builds,
when enabled, centralize traces under `<appStoragePath>/heteroAgent/tracing`
(sibling to the existing files cache) via shared dir constants.

Closes LOBE-9828

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge the standalone `stacked-prs` skill into `pr` as a supplementary section
(ordering rule, file placement, git split recipe, dependency verification,
Linear bookkeeping, gotchas) and absorb its triggers into the pr description,
rather than keeping a separate skill.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unfinished tool states (streaming / loading) now emit a paired tool result
message with `LOADING_FLAT` content instead of none, and every assistant turn
chains onto the previous message's id. The tool_use → tool_result link is what
lets conversation-flow merge the turns into one assistantGroup; without it the
unfinished modes rendered as one orphaned group per tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…Packaged

`resolveTraceRootDir` now centralizes traces under
`<appStoragePath>/heteroAgent/tracing` whenever `heteroTracingEnabled` is on,
instead of gating on `isPackaged`. Packaged behavior is unchanged (it only
traces when the toggle is on), and a dev who opts in now also gets the
centralized dir reachable from the Help-menu entry. Plain dev runs keep
writing to `cwd/.heerogeneous-tracing`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Importing the new `HETERO_AGENT_*` constants from `@/const/dir` dragged that
module's load-time `app.getPath()` / `app.getAppPath()` calls into the menu and
controller import graphs, breaking menu/controller suites whose electron mocks
or partial `@/const/dir` mocks didn't anticipate it. Relocate the pure path
segments to `@/const/heteroAgent` (no electron import) and point the controller
+ all three menu impls there. Also add the now-required `storeManager.get/set`
to the menu test app mocks (the Help-menu tracing checkbox reads it at build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… fixtures

- flatten the active ApiList item (drop accent bar) and the ToolPreview card shadow
- give the Aggregate thread a white container surface
- hide deprecated lobe-notebook toolset and legacy *Local* aliases from the gallery
- re-key local-system fixtures to current API names + add missing call args
- backfill agent-management call args so inspectors render their argument rows

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ss is safe

`@/const/dir` reads `app.getAppPath()` / `app.getPath()` at module load — fine
in production (app is ready), but it forced every test that transitively imports
it to stub those basics, which is the real root of the recent breakages.

Register a default `electron` mock in the global vitest setup, giving every
suite a ready `app` (paths + readiness) plus light stubs for the common
namespaces. Suites that need specific behavior still declare their own
`vi.mock('electron', …)`, which overrides this per-file. This keeps production
free to use plain value-style path constants instead of lazy getter functions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@arvinxx arvinxx merged commit 373b5e9 into canary May 31, 2026
38 of 45 checks passed
@arvinxx arvinxx deleted the feat/remote-cc-device-binding branch May 31, 2026 07:11
@arvinxx arvinxx mentioned this pull request Jun 3, 2026
arvinxx added a commit that referenced this pull request Jun 4, 2026
# 🚀 LobeHub Release (20260604)

**Release Date:** June 4, 2026  
**Since v2.2.1:** 88 merged PRs · 11 contributors

> This week brings Execution Devices out of the lab — run agents and
Claude Code on any configured local or remote machine — alongside Claude
Opus 4.8, token-usage analytics, and Page sharing.

---

## ✨ Highlights

- **Execution Devices** — Pick where an agent runs. Desktop and CLI
devices auto-register with a stable machine ID, route through the
gateway by channel, and surface a device switcher in the chat input. Run
remote Claude Code on a configured device, with a recent-directory
picker you can drag to reorder. (#15300, #15315, #15322, #15343, #15351,
#15371)
- **Claude Opus 4.8** — Day-one support for Anthropic's latest model.
(#15314)
- **Token-usage analytics** — A new token-usage mode on the activity
heatmap, backed by a denormalized topic usage/cost rollup so totals stay
accurate without recomputing from messages. (#15365, #15417, #15425)
- **Page sharing** — Share a Page through a dedicated document share
flow, plus new Workspace and Agent share tables. (#15309, #15439)
- **Self-iteration agents** — Agent Signal's execAgent migration lands a
server-runtime bridge, async memory writer, and a registered
self-iteration tool package, with a CLI trigger command for testing.
(#15360, #15364, #15392)
- **Knowledge search** — BM25 search now extends to file-backed
documents, and the portal ships an editable CodeMirror viewer for local
files with document highlighting. (#15247, #15298)

---

## 🏗️ Core Agent & Architecture

### Agent Signal & Runtime

- **execAgent migration** — Server-runtime bridge, completion
projection, async memory writer, and removal of the legacy
`executeSelfIteration` path. (#15392)
- Registered the self-iteration builtin tool package and restored the
three mode-specific self-iteration agent slugs. (#15202, #15364)
- Added a CLI trigger command with a golden-snapshot fixture for Agent
Signal. (#15360)
- **Skill priority** — Agent Builder now emits a skill-priority
instruction with matching server runtime. (#15409)
- Retry empty LLM completions instead of silently finishing the turn.
(#15355)
- Classify topic/agent/session foreign-key violations as
`ConversationParentMissing` for clearer recovery. (#15408)
- Persist canonical nested usage/performance on assistant messages, and
re-link orphan tool messages at the raw bucket write boundary. (#15359,
#15438)
- Guard `createAgent` against LLM double-encoded array fields. (#15381)

---

## 🖥️ Execution Devices & Gateway

- Auto-register desktop and CLI devices with a stable machine ID, and
add the `@lobechat/device-identity` package. (#15300, #15321)
- New Devices settings page behind the Execution Device Switcher lab,
with a device switcher shown for all agents in the chat input. (#15315,
#15371)
- `connectionId` + channel routing across the gateway client and device
list; preset the local device on the first LLM request for the 本机
target. (#15322, #15435)
- Run remote Claude Code on a configured device, with drag-to-reorder
recent-directory management and client renders for device tool results.
(#15343, #15351, #15437)
- Preserve content and state across gateway tool calls, and prevent
duplicate streaming from stale reconnects. (#15114, #15354)

---

## 🖥️ CLI & Desktop

- Preserve content/state for connect local file and shell tools; render
the `runCommand` tool result card. (#15441, #15442)
- New `lh topic view` command; CLI now auto-registers its device on
login, matching desktop. (#15340, #15377)
- Resolve CLI tools from the shell `PATH`, and clarify local command
session handling. (#15368, #15389)
- Relocate visual-ref helpers to `@lobechat/const` to fix a renderer
crash; upload `.blockmap` files to S3 for differential updates. (#15326,
#15369)
- Fix a market OAuth expiry that triggered the wrong re-login modal, and
kill dev child processes on parent shutdown. (#15246, #15290)

---

## 🗂️ Pages, Library & Knowledge

- Document share flow with business slot stubs, plus Workspace and Agent
share tables. (#15309, #15439)
- Export Agent profiles as Markdown, preserving an empty agent prompt on
export. (#15312, #15316)
- Editable CodeMirror viewer for local files with document highlighting;
BM25 search extended to file-backed documents. (#15247, #15298)
- Default new Agent-doc files to `.md` and preserve IME composition;
refresh folder data on slug switch and dedupe breadcrumb fetches.
(#15335, #15427)

---

## 💬 Chat & User Experience

- Group-by-status mode for the Topic sidebar; dropped the legacy
session→agentId compatibility path from Topic queries. (#15366, #15378)
- Restore editor focus after the file picker closes, and close the skill
dropdown before navigating to settings. (#15391, #15394)
- Strip markdown tokens from fallback Topic titles; keep an open
ActionBar popup when hovering another message. (#15303, #15372)
- Stabilize home starter loading and stop transliterating model names in
the home starter; show artifact source while streaming. (#15310, #15324,
#15386)
- Group the sidebar spacer with recents and agents. (#15373)

---

## 📊 Analytics, Tasks & Notifications

- Token-usage mode on the activity heatmap, backed by a denormalized
topic usage/cost rollup. (#15365, #15417, #15425)
- Push: new `PushChannel`, receipt cron, and `pushToken` tRPC API.
(#15233)
- Tasks now support file and image attachments. (#15141)

---

## 🧩 Models & Providers

- Support Claude Opus 4.8 and configurable model routing with starters.
(#15314, #15384)
- MiniMax M3: new model entry and an Anthropic video runtime. (#15380,
#15403)
- Add `intern-s2-preview` with `thinking_mode`, and `step-3.7-flash`
support. (#15308, #15317)
- Block disabling the official provider; fix default provider setup in
business mode. (#15379, #15382)

---

## 🎨 UI & Modals

- Migrate modals to `@lobehub/ui/base-ui` (LOBE-9711 + eval batch),
including the create-custom-model and feedback/changelog modals.
(#15401, #15416)
- Restructure confirmModal title and content across deletion flows;
polish the service-model form and migrate its Switch to base-ui.
(#15426, #15440)
- Wrap the BlueBubbles bridge config into a connection card; update
`@lobehub/ui` to v5.15.5. (#15325, #15342)

---

## 🔒 Reliability

- Replace hardcoded `session_context` values with template variables in
credentials. (#15352)
- Point `CHANGELOG_URL` to `/changelog`. (#15428)

---

## 👥 Contributors

Huge thanks to **11 contributors** who shipped **88 merged PRs** this
cycle.

@hezhijie0327 · @qybaihe · @sxjeru · @arvinxx · @Innei · @tjx666 ·
@lijian · @sudongyuer · @cy948 · @rivertwilight · @AmAzing129

Plus @lobehubbot and renovate[bot] for maintenance.

---

**Full Changelog**: v2.2.1...release/weekly-20260604
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature:agent Assistant/Agent configuration and behavior feature:tool Tool calling and function execution platform:desktop Desktop client size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant