✨ style(device): run remote CC on a configured device (cwd + context + online guard)#15343
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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>
There was a problem hiding this comment.
💡 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".
| const deviceCwd = | ||
| appContext?.initialTopicMetadata?.workingDirectory || | ||
| boundDevice?.defaultCwd || |
There was a problem hiding this comment.
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 Report❌ Patch coverage is 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
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
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>
- "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>
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>
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>
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>
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>
# 🚀 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
Summary
Makes a heterogeneous agent (claude-code / codex) actually usable when dispatched to an
lh connectdevice (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
useDevicebranch: topic-levelworkingDirectoryoverride > the bound device'sdefaultCwd(read from DB viaDeviceModel; the gateway only knows live connections), and pass it todispatchAgentRun.3b — device-specific systemContext, end to end
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.systemContextthrough the contract:AgentRunRequestMessage→GatewayHttpClient.dispatchAgentRun→deviceProxy.dispatchAgentRun.spawnLhHeteroExecinjectssystemContextas the first block of a content-block array on stdin (mirrorsspawnHeteroSandbox); it previously wrote only the bare prompt, so any context was silently dropped....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 storesrecentCwdsverbatim).useUpdateDeviceCwd: optimisticdevice.updateDevice— patches thelistDevicescache inonMutate, invalidatesonSettledto re-sync truth.Device online guard
executionTarget='device'): offline / unbound → send disabled + alert, instead of failing at dispatch.Tests
buildRemoteDeviceHeteroContext(6) ·nextRecentCwds(5) ·GatewayConnectionCtrforwards cwd/systemContext (1). Existing device/desktop/pkg suites green.type-checkclean.Related
cli > desktop: lobehub-biz/device-gateway#11 (independent, not a hard dep).🤖 Generated with Claude Code